如何在 C++ 中制作唯一的 lambda 实例?或者,如何使用 libstrophe 在 xmpp 中设置 id 消息处理程序?
How to make unique lambda instances in C++? Or, how to set up id message handlers in xmpp with libstrophe?
我正在使用 C libstrophe library to make an xmpp application in C++11. I'm trying to register message handlers for specific IDs, so I can recognize a specific return message, using 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 对此的实现。
Strophe 将只接受形式为
的函数指针
typedef int (*xmpp_handler)(xmpp_conn_t * const conn,
xmpp_stanza_t * const stanza,
void * const userdata);
使用静态函数很容易做到这一点,但是查看源代码我发现 this
/* 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 两次,但使用不同的 ID 和用户数据,它将拒绝第二次调用。
所以我想也许每次我想添加一个新的 ID 处理程序时我都可以创建一个 lambda,就像这样
auto msgHandler = [](xmpp_conn_t* const pConn,
xmpp_stanza_t* const pStanza,
void* const pUserdata) -> int
但是当我查看 lambda 的指针值时
printf("%p\n", (void*)((xmpp_handler)msgHandler));
和运行两次,我两次都得到相同的值。在这种情况下,lambda 似乎就像一个静态函数。
那么,每次我想监听一个新的 ID 时,我怎样才能创建一个新的、唯一的函数指针呢?或者,我是否误解了 libstrophe 应该如何使用?您是否应该为每个要侦听的新 ID 设置一个新的静态函数?
如果您使用的是 c++14,您可以创建一个通用的 lambda,每次调用它时返回唯一的 lambda(或者更确切地说,lambda 转换为静态函数):
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 的情况下,允许将 lambda 声明为内联并不那么容易,但让我们试一试。我们可以创建一些辅助结构来包装我们的 lambda 但由于一元 +
运算符不是 constexpr(lambda 没有链接)我们需要做一些变通方法...:
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
x 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
调用提供唯一的 id 作为在编译时已知的值,我们就可以享受传递的 lambda 的唯一函数指针:
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
libstrophe 问题跟踪器中已经提到了同样的问题:https://github.com/strophe/libstrophe/issues/97。分离分支中有一个补丁将被合并到主分支。因此,次要版本 0.9.2
将包含它。该补丁允许添加独特的 handler
和 userdata
对,而不是仅添加 handler
.
我正在使用 C libstrophe library to make an xmpp application in C++11. I'm trying to register message handlers for specific IDs, so I can recognize a specific return message, using 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 对此的实现。
Strophe 将只接受形式为
的函数指针typedef int (*xmpp_handler)(xmpp_conn_t * const conn,
xmpp_stanza_t * const stanza,
void * const userdata);
使用静态函数很容易做到这一点,但是查看源代码我发现 this
/* 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 两次,但使用不同的 ID 和用户数据,它将拒绝第二次调用。
所以我想也许每次我想添加一个新的 ID 处理程序时我都可以创建一个 lambda,就像这样
auto msgHandler = [](xmpp_conn_t* const pConn,
xmpp_stanza_t* const pStanza,
void* const pUserdata) -> int
但是当我查看 lambda 的指针值时
printf("%p\n", (void*)((xmpp_handler)msgHandler));
和运行两次,我两次都得到相同的值。在这种情况下,lambda 似乎就像一个静态函数。
那么,每次我想监听一个新的 ID 时,我怎样才能创建一个新的、唯一的函数指针呢?或者,我是否误解了 libstrophe 应该如何使用?您是否应该为每个要侦听的新 ID 设置一个新的静态函数?
如果您使用的是 c++14,您可以创建一个通用的 lambda,每次调用它时返回唯一的 lambda(或者更确切地说,lambda 转换为静态函数):
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 的情况下,允许将 lambda 声明为内联并不那么容易,但让我们试一试。我们可以创建一些辅助结构来包装我们的 lambda 但由于一元 +
运算符不是 constexpr(lambda 没有链接)我们需要做一些变通方法...:
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
x 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
调用提供唯一的 id 作为在编译时已知的值,我们就可以享受传递的 lambda 的唯一函数指针:
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
libstrophe 问题跟踪器中已经提到了同样的问题:https://github.com/strophe/libstrophe/issues/97。分离分支中有一个补丁将被合并到主分支。因此,次要版本 0.9.2
将包含它。该补丁允许添加独特的 handler
和 userdata
对,而不是仅添加 handler
.