const char* в качестве ключа для std::map
От: niXman Ниоткуда https://github.com/niXman
Дата: 04.08.17 10:13
Оценка:
привет!

допустим, есть такой класс:
struct invoker {
   void invoke() {}
};

struct type {
    void register(const char *api) {
        shared_ptr<invoker> p = std::make_shared<invoker>();
        map.emplace(api, std::move(p));
    }

    void invoke(const char *api) {
        auto it = map.find(api);
        if ( it == map.end() ) return;

        it->second->invoke();
    }

private:
   std::map<const char*, shared_ptr<invoker>> map;
};

тут type::register() зовется единожды при запуске программы, но а type::invoke() множество раз.
сейчас, похожий код хранит в мапе std::string, но для вызова invoke() мне доступен только const char*(приходит из сети), и из него приходится постоянно создавать временный std::string, только для вызова. вот от этого и хочу избавиться.
ключи для мапа могут быть только си-строками, ибо должны быть человекочитаемыми.

погугли про использование указателя для мапы, говорят, что достаточно указать свой компаратор мапе. есть ли еще какие-то "тонкости"?
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Re: const char* в качестве ключа для std::map
От: niXman Ниоткуда https://github.com/niXman
Дата: 04.08.17 10:16
Оценка:
в принципе, не так страшно хранить с мапе std::string, т.е. можно оставить как есть. но хочется избавиться от временной std::string, т.е. чтоб была возможность сказать мапе, чтоб сравнивала std::string и const char*.
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Re[2]: const char* в качестве ключа для std::map
От: watchmaker  
Дата: 04.08.17 10:42
Оценка: 5 (1) +1
Здравствуйте, niXman, Вы писали:


X>погугли про использование указателя для мапы, говорят, что достаточно указать свой компаратор мапе. есть ли еще какие-то "тонкости"?

Только то, что ты сам должен гарантировать, что ключи будут жить достаточно долго. То есть, что указатели из map должны ссылаться на валидные строки всё время жизни map.

X>в принципе, не так страшно хранить с мапе std::string, т.е. можно оставить как есть. но хочется избавиться от временной std::string, т.е. чтоб была возможность сказать мапе, чтоб сравнивала std::string и const char*.

Так в C++ есть же прозрачные компараторы:
 std::map<std::string, shared_ptr<invoker>, std::less<>> map;

Теперь временные строки при поиске const char* создаваться не будут.
Re: const char* в качестве ключа для std::map
От: oziro Нигерия  
Дата: 04.08.17 11:32
Оценка: +1
Можно рассмотреть вариант хранения вместо стринга std::array<char, MAX123>, если под задачу подходит. Если MAX123 не слишком велик, и хочется избавиться от сложности внутреннего представления std::string.
Re[3]: const char* в качестве ключа для std::map
От: Stanislav V. Zudin Россия  
Дата: 04.08.17 11:33
Оценка: -1
Здравствуйте, watchmaker, Вы писали:

W>
 std::map<std::string, shared_ptr<invoker>, std::less<>> map;

W>Теперь временные строки при поиске const char* создаваться не будут.

Наверное, только с С++14 (правда зачем тут менять компаратор)?
Сигнатура у старого find принимает ключ, т.е. временная строка создаваться таки будет.
_____________________
С уважением,
Stanislav V. Zudin
Re: const char* в качестве ключа для std::map
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 04.08.17 11:39
Оценка:
Здравствуйте, niXman, Вы писали:

X>сейчас, похожий код хранит в мапе std::string, но для вызова invoke() мне доступен только const char*(приходит из сети), и из него приходится постоянно создавать временный std::string, только для вызова. вот от этого и хочу избавиться.

X>ключи для мапа могут быть только си-строками, ибо должны быть человекочитаемыми.

Ключ должен быть скопирован в мапу для хранения. Ты гарантируешь, что строка по твоему указателю const char* будут сохраняться в памяти всё время жизни соответствующей пары значений?

А во временном std::string для вызова нет особо плохого, по крайней мере в реализациях, где там отдельный буфер со счётчиком использования — как минимум так в GCC STL.
The God is real, unless declared integer.
Re[2]: const char* в качестве ключа для std::map
От: night beast СССР  
Дата: 04.08.17 11:51
Оценка: +1
Здравствуйте, netch80, Вы писали:

N>Ключ должен быть скопирован в мапу для хранения. Ты гарантируешь, что строка по твоему указателю const char* будут сохраняться в памяти всё время жизни соответствующей пары значений?


N>А во временном std::string для вызова нет особо плохого, по крайней мере в реализациях, где там отдельный буфер со счётчиком использования — как минимум так в GCC STL.


строки со счетчиком уже не стандартны.
гсс их вроде выпиливали.
Re[2]: const char* в качестве ключа для std::map
От: so5team https://stiffstream.com
Дата: 04.08.17 11:54
Оценка: +1
Здравствуйте, netch80, Вы писали:

N>А во временном std::string для вызова нет особо плохого, по крайней мере в реализациях, где там отдельный буфер со счётчиком использования — как минимум так в GCC STL.


Начиная с gcc 5.1 в тамошнем STL std::basic_string уже не использует COW, поскольку COW не вписывается требования стандарта C++11 для std::basic_string.
Re[4]: const char* в качестве ключа для std::map
От: watchmaker  
Дата: 04.08.17 11:54
Оценка:
Здравствуйте, Stanislav V. Zudin, Вы писали:

SVZ>Здравствуйте, watchmaker, Вы писали:


W>>
 std::map<std::string, shared_ptr<invoker>, std::less<>> map;

W>>Теперь временные строки при поиске const char* создаваться не будут.

SVZ>(правда зачем тут менять компаратор)?

Так std::less<> и std::less<std::string> — разные вещи. Если не указывать, то по умолчанию будет использоваться второй, который будет создавать экземпляры std::string даже при использовании шаблонной версии метода find.
Re[3]: const char* в качестве ключа для std::map
От: Masterspline  
Дата: 04.08.17 11:56
Оценка: -1
W>Так в C++ есть же прозрачные компараторы:
W>
 std::map<std::string, shared_ptr<invoker>, std::less<>> map;

W>Теперь временные строки при поиске const char* создаваться не будут.

std::map::find() в качестве аргумента принимает Key& (а это std::string) и не факт, что оптимизатор сможет удалить создание временной строки.
Re[4]: const char* в качестве ключа для std::map
От: watchmaker  
Дата: 04.08.17 12:01
Оценка: +4
Здравствуйте, Masterspline, Вы писали:

M>std::map::find() в качестве аргумента принимает Key& (а это std::string) и не факт, что оптимизатор сможет удалить создание временной строки.

В std::map есть несколько методов find. Как указанный вами с аргументом const Key&, так и шаблонный
template< class K > iterator find( const K& x );

который будет выбираться для const char*.

И при прозрачном компараторе он внутри себя не будет конструировать std::string, а напрямую будет сравнивать std::string с const char*
Отредактировано 04.08.2017 12:04 watchmaker . Предыдущая версия .
Re: const char* в качестве ключа для std::map
От: Masterspline  
Дата: 04.08.17 12:03
Оценка: +1
X>допустим, есть такой класс:
X>
struct invoker {
   void invoke() {}
};

struct type {
    void register(const char *api) {
        shared_ptr<invoker> p = std::make_shared<invoker>();
        map.emplace(api, std::move(p));
    }

    void invoke(const char *api) {
        auto it = map.find(api);
        if ( it == map.end() ) return;

        it->>second->invoke();
    }

private:
   std::map<const char*, shared_ptr<invoker>> map;
};

X>тут type::register() зовется единожды при запуске программы, но а type::invoke() множество раз.
X>сейчас, похожий код хранит в мапе std::string, но для вызова invoke() мне доступен только const char*(приходит из сети), и из него приходится постоянно создавать временный std::string, только для вызова. вот от этого и хочу избавиться.

Если есть гарантия, что аргумент register() живет дольше, чем существует map, то достаточно задать компаратор для map. Если же такой гарантии нет, то я бы сделал кроме
std::map<const char*, shared_ptr<invoker>,Comparator> map;
еще и временное хранилище ключей в контейнере с std::string и в map сохранял адрес строки в этом хранилище.
Re[5]: const char* в качестве ключа для std::map
От: Masterspline  
Дата: 04.08.17 12:06
Оценка: +1
W>В std::map есть несколько методов find. Как указанный вами с аргументом const Key&, так и шаблонный
W>template< class K > iterator find( const K& x );

W>который будет выбираться для const char*.

W>И при прозрачном компараторе он внутри себя не будет конструировать std::string, а напрямую будет сравнивать std::string с const char*


Да, действительно есть. Похоже начиная с c++14 достаточно в map задать компаратор, причем подойдет стандартный std::less<> (без типа).
Re: const char* в качестве ключа для std::map
От: Alexander G Украина  
Дата: 04.08.17 16:42
Оценка:
Здравствуйте, niXman, Вы писали:

X>тут type::register() зовется единожды при запуске программы, но а type::invoke() множество раз.


Используй std::vector<std::string>, отсортируй после всех register, но перед первым invoke, ищи через свободную функцию std::lower_bound, передавая свой компазатор:
struct Comparator 
{
    bool operator()(cosnt std::string& s1, const char* s2) const { return std::strcmp(s1.c_str(), s2) < 0; }
    bool operator()(const char* s1, const std::string& s2) const { return std::strcmp(s1, s2.c_str()) < 0; }
};
Русский военный корабль идёт ко дну!
Отредактировано 04.08.2017 16:43 Alexander G . Предыдущая версия .
Re[3]: const char* в качестве ключа для std::map
От: ArtDenis Россия  
Дата: 04.08.17 17:37
Оценка: +2
Здравствуйте, watchmaker, Вы писали:
W>Так в C++ есть же прозрачные компараторы:
W>
 std::map<std::string, shared_ptr<invoker>, std::less<>> map;

W>Теперь временные строки при поиске const char* создаваться не будут.

На всякий случай почему нужен std::less<>:

https://en.wikipedia.org/wiki/C%2B%2B14#Heterogeneous_lookup_in_associative_containers
[ 🎯 Дартс-лига Уфы | 🌙 Программа для сложения астрофото ]
Re: const char* в качестве ключа для std::map
От: savitar  
Дата: 04.08.17 20:50
Оценка: +2
Здравствуйте, niXman, Вы писали:

X>...


string_view вместо const char*. string_view создается без аллокаций, передавать можно и char* и string.
Re[3]: const char* в качестве ключа для std::map
От: niXman Ниоткуда https://github.com/niXman
Дата: 06.08.17 09:52
Оценка:
Здравствуйте, watchmaker, Вы писали:

W>Только то, что ты сам должен гарантировать, что ключи будут жить достаточно долго. То есть, что указатели из map должны ссылаться на валидные строки всё время жизни map.

сейчас эти строки генерятся макросом, который используется для описания интерфейсов, и который генерит дополнительную метаинфу.
но, т.к. это библиотечный код который используется несколькими коллегами — лучше таки на это не надеяться и копировать строку в мапу.

W>Так в C++ есть же прозрачные компараторы:

что-то я такое слышал, но не использовал... и, с++14 у нас пока не используется...
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Re[2]: const char* в качестве ключа для std::map
От: niXman Ниоткуда https://github.com/niXman
Дата: 06.08.17 09:58
Оценка:
Здравствуйте, Masterspline, Вы писали:

M> .... я бы сделал кроме
std::map<const char*, shared_ptr<invoker>,Comparator> map;
еще и временное хранилище ключей в контейнере с std::string и в map сохранял адрес строки в этом хранилище.


кстати, идея для следующей оптимизации! =)
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.