是否可以将()* this绑定到类成员函数以对C API进行回调

时间:2013-09-17 11:44:23

标签: c++ boost std

有没有办法使用boost或std bind()所以我可以在C API中使用结果作为回调? 这是我使用的示例代码:

#include <boost/function.hpp>
#include <boost/bind/bind.hpp>

typedef void (*CallbackType)();

void CStyleFunction(CallbackType functionPointer)
{
    functionPointer();
}

class Class_w_callback
{
public:
    Class_w_callback()
    {
        //This would not work
    CStyleFunction(boost::bind(&Class_w_callback::Callback, this));
    }
    void Callback(){std::cout<<"I got here!\n";};
};

谢谢!

4 个答案:

答案 0 :(得分:3)

不,没有办法做到这一点。问题是C函数指针基本上只是一个指令地址:“转到这个地址,并执行你找到的指令”。 您想要加入函数的任何状态必须是全局的,或者作为参数传递。

这就是为什么大多数C回调API都有一个“context”参数,通常是一个void指针,你可以传入,只是为了让你传入你需要的数据。

答案 1 :(得分:3)

你不能在便携式C ++中这样做。但是,有些库可以创建类似闭包的C函数。这些库在其实现中包含汇编代码,需要手动移植到新平台,但如果它们支持关心的架构,则它们可以正常工作。

例如,使用Bruno Haible的trampoline库,您可以编写如下代码:

#include <trampoline.h>

class Class_w_callback
{
    static Class_w_callback* saved_this;
public:
    Class_w_callback()
    {
        CallbackType cb = (CallbackType) alloc_trampoline((tramp_cb) invoke, &saved_this, this);
        CStyleFunction(cb);
        free_trampoline(cb);
    }
    void Callback(){std::cout<<"I got here!\n";};
    typedef int (*tramp_cb)(...);
    static int invoke() {
        Class_w_callback& me = *saved_this;
        me.Callback();
        return 0;
    }
};

请注意,尽管使用了静态成员,但alloc_trampoline创建的trampolines是可重入的:当调用返回的回调时,它首先将指针复制到指定的地址,然后调用原始函数原始论点。如果代码也必须是线程安全的,那么saved_this应该是线程本地的。

答案 2 :(得分:1)

这不起作用。

问题是bind返回一个仿函数,即一个带有operator()成员函数的C ++类。这不会绑定到C函数指针。您需要的是静态或非成员函数,它将this指针存储在全局变量或静态变量中。当然,为当前回调找到正确的this指针可能是一项非常重要的任务。

答案 3 :(得分:0)

全局变量

正如其他人所提到的,您需要一个全局变量(一个静态成员是一个隐藏为变量成员的全局变量),当然,如果您需要多个对象以在上述回调中使用不同的参数, ,它将无法正常工作。

回调中的上下文参数

C库可以提供void *或类似的上下文。在这种情况下,请使用该功能。

回溯中的库上下文参数

C库可以使用其对象(struct指针)来调用您的回调。假设您有一个名为example的库,该库提供了一个名为example_t的类型,并定义了这样的回调:

callback(example_t *e, int param);

然后,您可以将上下文(也称为this指针)放在该example_t结构中,并在回调中将其取回。

连续通话

假设您只有一个线程使用该特定的C库,并且仅当您在库中调用一个函数时才可以触发回调(即,您不会在某个随机时间点触发事件),您仍然可以使用全局变量。您要做的是在每次调用之前将当前对象保存在全局对象中。像这样:

object_i_am_working_with = this;
make_a_call_to_that_library();

这样,在回调内部,您始终可以访问object_i_am_working_with指针。在多线程应用程序中或库在后台自动生成事件(例如,按键,来自网络的数据包,计时器等)时,此功能不起作用

每个对象一个线程(自C ++ 11起)

在多线程环境中,这是一个有趣的解决方案。如果您没有以前的解决方案,则可以使用线程解决问题。

在C ++ 11中,有一个名为thread_local的新特殊说明符。在过去,您必须手动处理特定于每个线程实现的问题……现在您可以执行以下操作:

thread_local Class_w_callback * callback_context = nullptr;

然后,在回调中,您可以使用callback_context作为指向Class_w_callback类的指针。

当然,这意味着您需要为创建的每个对象创建一个线程。在您的环境中,这可能不可行。就我而言,我的组件都在各自的循环中运行,因此每个组件都有各自的thread_local环境。

请注意,如果库自动生成事件,则您可能也不会这样做。

使用线程的旧方法(和C解决方案)

如上所述,在过去,您必须自己管理本地线程环境。使用pthread(基于Linux),您可以通过pthread_getspecific()访问特定于线程的数据:

void *pthread_getspecific(pthread_key_t key);
int pthread_setspecific(pthread_key_t key, const void *value);

这利用了动态分配的内存。可能是在Linux下的g ++中实现thread_local的方式。

在MS-Windows下,您可能会使用TlsAlloc函数。