【问题标题】:How to make unique lambda instances in C++? Or, how to set up id message handlers in xmpp with libstrophe?如何在 C++ 中创建唯一的 lambda 实例?或者,如何使用 libstrophe 在 xmpp 中设置 id 消息处理程序?
【发布时间】:2016-12-14 15:19:16
【问题描述】:

我正在使用C libstrophe library 在 C++11 中制作 xmpp 应用程序。我正在尝试为特定 ID 注册消息处理程序,因此我可以使用 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 和 userdata 不同,它将拒绝第二次调用。

所以我想也许每次我想添加一个新的 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 设置一个新的静态函数?

【问题讨论】:

  • @Olaf 我很抱歉,我认为它们是相关的,因为 libstrophe 是用 C 编写的,问题的答案可能是我误解了如何使用 xmpp
  • 据我了解,您询问有关使用 C++ 库的问题。它看起来不像是关于 C 语言本身的问题(分别是该语言的问题)。 xmpp 类似。 libaray-tag 在这里应该足够了。如果答案证明我错了,请随意添加标签(毕竟我并不完美,可能会像其他人一样犯错),但请不要提前添加。
  • @RichardHodges 我有很多 ID,这不是问题。问题是 strophe 不允许你使用相同的处理函数两次,即使你使用不同的 ID 和用户数据
  • @Sossisos hash_get(conn->id_handlers, id) 返回特定 id 的处理程序列表,因此它不应阻止您对不同的 id 使用相同的函数。
  • @AlexanderAnikin 具体的阻塞代码行是“if (item->handler == (void *)handler)”

标签: c++ lambda libstrophe


【解决方案1】:

libstrophe 问题跟踪器中已经提到了同样的问题:https://github.com/strophe/libstrophe/issues/97。分离分支中有一个补丁,它将被合并到主分支。因此,次要版本0.9.2 将包含它。该补丁允许添加唯一的handler 加上userdata 而不仅仅是handler

【讨论】:

    【解决方案2】:

    如果您使用的是 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:/我会更新问题
    【解决方案3】:

    在 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

    [live demo]

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-07-17
      • 1970-01-01
      • 1970-01-01
      • 2010-11-07
      • 2015-08-16
      • 1970-01-01
      • 2021-12-05
      • 1970-01-01
      相关资源
      最近更新 更多