Я использую C библиотека libstrophe сделать приложение xmpp на C ++ 11. Я пытаюсь зарегистрировать обработчики сообщений для определенных идентификаторов, чтобы я мог распознать конкретное возвращаемое сообщение, используя xmpp_id_handler_add.
void xmpp_id_handler_add(xmpp_conn_t * const conn,
xmpp_handler handler,
const char * const id,
void * const userdata)
Но есть что-то в реализации этого, что я не понимаю.
Strophe будет принимать только указатель на функцию вида
typedef int (*xmpp_handler)(xmpp_conn_t * const conn,
xmpp_stanza_t * const stanza,
void * const userdata);
Это достаточно просто сделать со статической функцией, но, глядя на исходный код, я нахожу этот
/* check if handler is already in the list */
item = (xmpp_handlist_t *)hash_get(conn->id_handlers, id);
while (item) {
if (item->handler == (void *)handler)
break;
item = item->next;
}
if (item) return;
Это означает, что если я попытаюсь дважды вызвать xmpp_id_handler_add с одной и той же статической функцией, но с другим идентификатором и данными пользователя, он отклонит второй вызов.
Поэтому я подумал, что, возможно, я смогу сделать лямбду каждый раз, когда захочу добавить новый обработчик идентификатора, например
auto msgHandler = [](xmpp_conn_t* const pConn,
xmpp_stanza_t* const pStanza,
void* const pUserdata) -> int
Но когда я посмотрел на значение указателя лямбда
printf("%p\n", (void*)((xmpp_handler)msgHandler));
И запустил его дважды, я получил одно и то же значение оба раза. Кажется, что лямбда в этом случае похожа на статическую функцию.
Итак, как я могу создать новый уникальный указатель на функцию каждый раз, когда я хочу прослушать новый идентификатор? С другой стороны, я неправильно понимаю, как предполагается использовать libstrophe? У вас должна быть новая статическая функция для каждого нового идентификатора, который вы хотите прослушать?
Та же проблема уже упоминалась в трекере ошибок libstrophe: https://github.com/strophe/libstrophe/issues/97. В отдельной ветке есть патч, который собирается объединить с основной веткой. Поэтому минорный выпуск 0.9.2
будет содержать это. Патч позволяет добавлять уникальные пары handler
плюс userdata
вместо только handler
,
Если вы используете c ++ 14, вы можете создать общую лямбду, возвращающую уникальную лямбду (или, скорее, лямбду, преобразованную в статическую функцию) каждый раз, когда вы вызываете ее:
auto msgHandlerCreator = [](auto X){ return +[](xmpp_conn_t* const pConn,
xmpp_stanza_t* const pStanza,
void* const pUserdata) -> int {/*...*/}; };
и звоните каждый раз с другим std::integral_constant
например.:
msgHandlerCreator(std::integral_constant<std::size_t, 0>{})
msgHandlerCreator(std::integral_constant<std::size_t, 1>{})
В случае c ++ 11 будет нелегко разрешить лямбду объявлять встроенным, но давайте попробуем. Мы могли бы создать некоторую вспомогательную структуру, чтобы обернуть нашу лямбду, но как унарный +
оператор не является constexpr (лямбда не имеет связи) нам нужно сделать несколько обходных путей …:
template <class... Args>
struct voider {
using type = void;
};
template <std::size_t, class, class = void>
struct FunctionWrapper;
template <std::size_t N, class L>
struct FunctionWrapper<N, L, typename voider<decltype(&L::operator())>::type>: FunctionWrapper<N, decltype(&L::operator())> {};
template <std::size_t N, class Res, class L, class... Args>
struct FunctionWrapper<N, Res (L::*)(Args...) const, void> {
static Res (*l)(Args...);
static Res foo(Args... args) {
return l(args...);
}
};
template <std::size_t N, class Res, class L, class... Args>
Res (* FunctionWrapper<N, Res (L::*)(Args...) const, void>::l)(Args...);
FunctionWrapper
теперь должен предоставить уникальную статическую функцию для каждой пары std::size_t
Икс Lambda
, Теперь функция, которая будет выполнять фактическую упаковку:
template <std::size_t N, class L>
decltype(&FunctionWrapper<N, L>::foo) wrap(L &l) {
FunctionWrapper<N, L>::l = +l;
return &FunctionWrapper<N, L>::foo;
}
И мы можем наслаждаться уникальным указателем функции для переданной лямбды, если мы предоставляем уникальный идентификатор как значение, известное во время компиляции для каждого wrap
вызов:
auto lambda = [](int x){std::cout << x << std::endl;};
std::cout << (void *)wrap<0>(lambda) << std::endl;
std::cout << (void *)wrap<1>(lambda) << std::endl;
wrap<0>(lambda)(1);
wrap<1>(lambda)(1);
Примерный вывод:
0x400f28
0x400f76
1
1