Как сделать уникальные лямбда-экземпляры в C ++? Или как настроить обработчики сообщений id в xmpp с помощью libstrophe?

Я использую 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? У вас должна быть новая статическая функция для каждого нового идентификатора, который вы хотите прослушать?

0

Решение

Та же проблема уже упоминалась в трекере ошибок libstrophe: https://github.com/strophe/libstrophe/issues/97. В отдельной ветке есть патч, который собирается объединить с основной веткой. Поэтому минорный выпуск 0.9.2 будет содержать это. Патч позволяет добавлять уникальные пары handler плюс userdata вместо только handler,

1

Другие решения

Если вы используете 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>{})
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

[живое демо]

1
По вопросам рекламы [email protected]