简体   繁体   English

使用C ++类成员函数(不能是静态的)作为C回调函数

[英]Using a C++ class member function (cannot be static) as a C callback function

I have a C library function that expects a function pointer for callback, and I want to pass in a C++ member function. 我有一个C库函数,需要一个函数指针用于回调,我想传入一个C ++成员函数。 The C++ function modifies a member variable, so I can't use a static free function (as suggested in several similar posts). C ++函数修改了一个成员变量,所以我不能使用静态自由函数(如几个类似的帖子所示)。 My attempt (shown below) fails with a compiler error. 我的尝试(如下所示)因编译器错误而失败。

This post comes closest to what I need: 这篇文章最接近我的要求:

Using a C++ class member function as a C callback function 使用C ++类成员函数作为C回调函数

How can I do this without static functions? 如果没有静态函数,我怎么能这样做? Thanks! 谢谢!


test.h test.h

#ifndef TEST_H_
#define TEST_H_

#ifdef __cplusplus
extern "C" {
#endif

typedef void (*handler_t)(int foo, void *bar);

void set_handler(handler_t h);

#ifdef __cplusplus
}
#endif

#endif

test.c test.c的

#include "test.h"
#include <stdlib.h>

static handler_t handler_ = NULL;
void set_handler(handler_t h) {
        handler_ = h;
}

void handle_event(int foo, void *bar) {
        if (handler_ != NULL) handler_(foo, bar);
}

test.cpp TEST.CPP

#include "test.h"
#include <iostream>
using namespace std;

class Foo {
public:
        Foo() : ctr_(0) {};

        // handler needs to access non-static variable, so it can't be static
        void handler(int foo, void *bar) { ++ctr_;  }

private:
        int ctr_;
};

int main(int argc, char **argv) {
        // error: can't convert to "void (*)(int, void*)"
        set_handler(&Foo::handler);

        cout << "done" << endl;
        return 0;
}

GCC barf GCC barf

$ gcc test.cpp test.c 
test.cpp: In function ‘int main(int, char**)’: 
test.cpp:18: error: cannot convert ‘void (Foo::*)(int, void*)’ to ‘void (*)(int, void*)’ for argument ‘1’ to ‘void set_handler(void (*)(int, void*))’

It is not possible, at least with that handler_t signature. 至少使用handler_t签名是不可能的。

While you can create a free function on your .cpp to wrap the member call, you need a pointer to the Foo instance: 虽然您可以在.cpp上创建一个自由函数来包装成员调用,但您需要一个指向Foo实例的指针:

void my_wrap(int foo, void* bar) {
    Foo* some_foo_instance = ...;
    some_foo_instance->handler(foo, bar);
}

int main(int argc, char **argv) {
    set_handler(&my_wrap);
}

You need some void* to pass the Foo instance as a handler attribute: 您需要一些void *来将Foo实例作为处理程序属性传递:

// Header
typedef void (*handler_t)(int foo, void *bar, void* arg1);
void set_handler(handler_t h, void* arg1);

// Impl.
void set_handler(handler_t h, void* arg1) {
        handler_ = h;
        handler_arg1_ = arg1;
}

// cpp
void my_wrap(int foo, void* bar, void* arg1) {
    Foo* some_foo_instance = static_cast<Foo*>(arg1);
    some_foo_instance->handler(foo, bar);
}

// main
int main(int argc, char **argv) {
    Foo some_concrete_instance;
    set_handler(&my_wrap, static_cast<void*>(&some_concrete_instance));
}

The big question is how many times you need to call set_handler multiple times to call methods on different objects. 最大的问题是需要多次调用set_handler来调用不同对象上的方法。 If this answer is one, you can do something like this: 如果这个答案是一个,你可以这样做:

#include <boost/function.hpp>

class HandlerContext
{
    static boost::function<void (int, void*)> s_func

    static void forward(int foo, void* bar)
    {
         s_func(foo, bar);
    }

public:
    static void set(boost::function<int, void*> const& f)
    {
        s_func = f;
        set_handler(&HandlerContext::forward);
    }
};

If the answer is "more than once", you can have multiple forwarding functions that get their function objects out of an array. 如果答案是“不止一次”,您可以使用多个转发函数将其函数对象从数组中取出。 You will need to preassign slots in this case, because the function in use will indicate which callback to make. 在这种情况下,您需要预先分配插槽,因为正在使用的功能将指示要进行的回调。

Suppose you create a mapping function: 假设您创建了一个映射函数:

Foo *inst = // some instance of Foo you're keeping around...

void wrapper(int foo, void *bar){
    inst->handler(foo, bar);
}

Then use wrapper as the callback. 然后使用wrapper作为回调。 Instance semantics in a callback are kind of strange, so I'm not sure how you're going to be sure you bind to the correct instance -- if this is a singleton maybe that doesn't matter. 回调中的实例语义有点奇怪,所以我不确定你将如何确定你绑定到正确的实例 - 如果这是一个单例可能无关紧要。

This sentence: 这个句子:

I have a C library function 我有一个C库函数

This means you can NOT pass it any C++ object. 这意味着你不能传递任何C ++对象。
If the library you are using is a C library it does not know about C++ so it can not using anything that is C++ it can only use C stuff. 如果你使用的库是一个C库,它不知道C ++,所以它不能使用任何C ++它只能使用C的东西。

You MUST make it call a free function in you code. 必须让它在你的代码中调用一个自由函数。
Now your free function can then call a method on an object (that is why C callbacks have a void* parameter (so you can pass context to the callback)). 现在你的自由函数可以调用一个对象的方法(这就是为什么C回调有一个void *参数(所以你可以将上下文传递给回调))。

Here is an ugly hack I invented awhile ago to solve this problem: 这是我前一段时间发明的一个丑陋的黑客来解决这个问题:

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

using ::boost::function;
using ::boost::bind;

typedef int (*callback_t)(const char *, int);

typedef function<int(const char *, int)> MyFTWFunction;

template <MyFTWFunction *callback>
class callback_binder {
 public:
   static int callbackThunk(const char *s, int i) {
      return (*callback)(s, i);
   }
};

extern void register_callback(callback_t f);

int random_func(const char *s, int i)
{
   if (s && *s) {
      return i;
   } else {
      return -1;
   }
}

MyFTWFunction myfunc;

class FooClass {
 public:
   virtual int callme(const char *s, int x) { return 0; };
};

int main(int argc, const char *argv[])
{
   FooClass foo;
   myfunc = bind(&FooClass::callme, &foo, _1, _2);
   register_callback(&callback_binder<&myfunc>::callbackThunk);
   return 0;
}

This could probably be fixed to use stuff from TR1 and remove the dependency on Boost. 这可能是固定使用来自TR1的东西并删除对Boost的依赖。

And also, of course, myfunc is a global variable. 当然, myfunc也是一个全局变量。 It has to be a global variable. 它必须是一个全局变量。 You must have one global variable per different possible object you'd want to call back into. 每个不同的可能对象必须有一个全局变量,您想要回调。 OTOH, you can have as many of these globals as you want. OTOH,你可以拥有任意数量的全局变量。

The main issue here is that it is absolutely impossible to do what you want within the given constraints. 这里的主要问题是在给定的约束条件下完全不可能做到你想要的。 The pointer to the object you want to call back into has to come from somewhere. 指向要回调的对象的指针必须来自某个地方。 In some languages (like Python for example) you can create a function on-the-fly that has it's own copy of the object pointer. 在某些语言中(例如Python),您可以在运行中创建一个具有自己的对象指针副本的函数。 This cannot be done in C++. 这不能用C ++完成。 All functions must exist completely at compile time. 所有函数必须在编译时完全存在。 You cannot create new function instances at run time. 您无法在运行时创建新的函数实例。

With C++0x, you can sort of create functions at runtime with lambda functions. 使用C ++ 0x,您可以使用lambda函数在运行时创建函数。 But these functions have an unspecified type and there is absolutely no way you could ever then pass them to a C function and have it work. 但是这些函数有一个未指定的类型,你绝对没有办法将它们传递给C函数并让它工作。 Lambda expressions are meant to be supplied as template parameters and it's pretty hard to use them for anything else because their address can't be taken, and even if it could you ccouldn't actually know what type the pointer is pointing to. Lambda表达式意味着作为模板参数提供,并且很难将它们用于其他任何事情,因为它们的地址不能被采用,即使它实际上你也不知道指针指向的是什么类型。

I highly recommend not using it. 我强烈建议不要使用它。 The little void * most callback interfaces allow you to specify that gets handed back to you along with the data is meant to hold an object pointer of some kind. little void *大多数回调接口允许您指定与数据一起交还给您的数据意味着保存某种对象指针。 If possible, you should be doing that instead. 如果可能的话,你应该这样做。

If you have control over how handler is defined, I recommend using Boost function objects instead of function pointers. 如果您可以控制如何定义处理程序,我建议使用Boost函数对象而不是函数指针。

If you HAVE to use function pointers, define handler_t with an extra void* whose value is passed along with the handler, watch out for the gotchas Martin York linked in a comment . 如果您必须使用函数指针,请使用额外的void *定义handler_t,其值与处理程序一起传递,请注意Martin York在注释中链接的陷阱 Then you have something like this: 然后你有这样的事情:

typedef void (*handler_t)(int foo, void *bar, void *data);

static handler_t handler_ = NULL;
static void* handler_data_ = NULL;
void set_handler(handler_t h, void *d = NULL) {
    handler_ = h;
    handler_data = d;
}

void handle_event(int foo, void *bar) {
    if (handler_ != NULL) handler_(foo, bar, handler_data_);
}

void foo_handler(int foo, void *bar, void *data) {
    Foo *fooObj = static_cast<Foo*>(data);
    fooObj->handler(foo, bar);
}

// in main
    set_handler(foo_handler, &some_foo_object);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM