C ++在一个特定情况下替代void *

时间:2017-06-09 21:32:54

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

我在思考C语言中void*的一个用例,我试图以最惯用的方式将该代码转换为C ++而不会降低效率。这是问题,以下是解决方案

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

Something something;
add_callback(callback, &something);

只要某个事件发生了某个事件,就会调用回调。在C ++中执行此操作的惯用方法(帮助我找到一个更好的解决方案,如果你认为这是不对的)就像是 以下

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

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

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

2 个答案:

答案 0 :(得分:0)

您可以使用函数模板简化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);
}

答案 1 :(得分:0)

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

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

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。你可以做一些智能元编程,每个参数只有一个版本。