【问题标题】:C++ Alternative to void* in one particular caseC++ 在一种特定情况下替代 void*
【发布时间】:2017-06-09 21:32:53
【问题描述】:

我正在考虑 C 中 void* 的一个用例,我试图以最惯用的方式将该代码翻译成 C++,而不会降低效率。这是问题所在,以下是解决方法

void callback(void* args) {
    Something* something = static_cast<Something*>(args);
    // use something
}

Something something;
add_callback(callback, &something);

这里,只要参数 something 发生某些事件,就会调用回调。在 C++ 中进行此操作的惯用方式(如果您认为不正确,请帮助我找到更好的解决方案)类似于 关注

Something something;
add_callback([=something]() { 
    // do something
});

然后add_callback() 将在内部擦除 lambda 的类型并将其存储在 std::function&lt;&gt; 中,以便在时机成熟时回调。与旧版本相比,这样做的好处是回调更好地封装了它的状态。

但是我看到的问题是,每次调用回调时,这都会导致虚函数调度的不必要开销。 (假设即使没有可证明的开销,只是因为我很好奇它是开销),我能想到的唯一解决方案是在这里使用std::any(但这需要对象是可复制构造的)并且要么使使用std::any 作为成员变量的回调统一签名,或者只是将std::any 传递给函数(存储为函数指针)并放弃对回调状态的封装。但有件事告诉我有更好的解决方案......

【问题讨论】:

  • 我不明白,如果callback 指向的东西总是Something,为什么还要使用void*?为什么不直接使用Something*
  • @Brian 提供add_callback() 的库不知道,例如libevent 中的event_new()
  • 或标准库中的示例,qsortbsearch
  • TBH,在几乎任何涉及异步回调的情况下,虚拟调度的成本与其他一切的成本相比都可以忽略不计。
  • @Curious:这是一个从将自己定义为一个角落开始的问题。没有值类型,因为这需要复制。没有间接调度,因为那“需要”虚函数。等等。也不清楚您想要修复 C 方法的哪些缺陷:无法提供任意可调用对象(函子等),检查 void* 参数是否实际上是它被强制转换为的类型,或别的什么。

标签: c++ c++11 callback c++17 type-erasure


【解决方案1】:

您可以使用函数模板来简化 C++ 中完整类型的使用,但无法避免 static_cast

例子:

struct CallbackHandler
{
   void doSomething() {}
}

template <typename T>
void cpp_callback(T* args)
{
   // Use args with full access to T.
   args->doSomething();
}

template <typename T>
void callback(void* args)
{
   cpp_callback(static_cast<T*>(args));
}

int main()
{
   add_callback(callback<CallbackHandler>, new CallbackHandler);
}

【讨论】:

    【解决方案2】:

    我认为你不能同时获得类型擦除、类型安全和零开销。

    我想出的最好的是这样的:

    typedef void (*func_t)(void *p);
    
    template<typename T, void (T::*F)()>
    struct CB_helper
    {
        static void func(void *p)
        {
            T *t = static_cast<T*>(p);
            (t->*F)();
        }
    };
    
    template<typename T, void (T::*F)()>
    func_t CB()
    {
        return CB_helper<T, F>::func;
    }
    

    然后你可以使用它:

    struct Something
    {
        void do();
    };
    Something some;
    add_callback(CB<Something, &Something::do>(), &some);
    

    缺点是您需要为您要使用的每种回调类型创建一个新的CB。您可以进行一些智能元编程,使每个参数数量只有一个版本。

    【讨论】:

      猜你喜欢
      • 2023-03-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-08-09
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多