用指向实例中方法的指针替换参数中的静态函数指针

Posted

技术标签:

【中文标题】用指向实例中方法的指针替换参数中的静态函数指针【英文标题】:Replacing static function pointer in argument by a pointer to a method in instance 【发布时间】:2015-11-25 13:24:05 【问题描述】:

我使用第三方库,它需要将指向静态函数的指针作为回调参数传递。现在我必须做这样的事情:

static int MyCallback( ...)

    // Callback code here...


int main( int argc, char* argv[])

    ThirdPartyFunction( &MyCallback, ... );

我想做的是将我必须提供的静态回调函数替换为 C++ 类实例的成员方法。像这样的:

class MyClass

public:
    int MyCallbackMethod( ... );
;

int main(int argc, char* argv[])

    MyClass instanceOfMyClass;

    ThirdPartyFunction( &(????), ... );

我的问题是当我将&instanceOfMyClass::MyCallbackMethod 传递给ThirdPartyFunction() 时它无法编译。当启动一个线程(例如,一个 std::thread)时,一个通过 (&MyClass::MyCallbackMethod, this) 但在这种情况下,ThirdPartyFunction(似乎是用 C 而不是 C++ 编写的?)没有不允许我传递函数指针和拥有该方法的对象。

另一方面,如果我在MyClass 中声明static int MyCallbackMethod,它当然可以工作,但我没有实例,这不是我想要的。

我的问题很简单:是否可以传递 (&MyClass::MyCallbackMethod, this)实例方法)之类的东西来代替 &MyClass::MyCallbackMethod静态方法)?我正在使用 C++11

提前感谢您的帮助!

编辑 1

我使用 libmicrohttpd(见 microhttpd.h)

函数“ThirdPartyFunction”:

_MHD_EXTERN struct MHD_Daemon *
MHD_start_daemon_va (unsigned int flags,
         uint16_t port,
         MHD_AcceptPolicyCallback apc, void *apc_cls,
         MHD_AccessHandlerCallback dh, void *dh_cls,
         va_list ap);

MHD_AcessHandlerCallback,例如,被声明为:

typedef int
(*MHD_AccessHandlerCallback) (void *cls,
                          struct MHD_Connection *connection,
                          const char *url,
                          const char *method,
                          const char *version,
                          const char *upload_data,
                          size_t *upload_data_size,
                          void **con_cls);

所以我必须使用具有此签名的函数。我想在实例方法而不是静态函数上使用相同的签名。

【问题讨论】:

IMO 你不能。第三方库想要一个指向具有特定签名的函数的函数指针,您必须遵守该签名。为什么需要实例方法?第三方库对您的课程一无所知。 能否请您显示回调函数和用于注册该回调的函数的正确签名? ThirdPartyFunction 是否允许您传递任何用户数据?就像例如指针?那就是传递给回调函数?然后,您可以创建一个 static 函数包装器,将其传递给 ThirdPartyFunction,并将指向对象的指针作为用户数据传递,并在包装​​器中使用用户数据指针并调用非静态成员函数. @Michael Walz 可能,但是很难“接受”。在 C++11 中没有 adapter 或类似的东西可以将成员方法的签名更改为静态方法/函数??????? apc_cls 似乎是您在MHD_AccessHandlerCallback 的第一个参数中检索到的用户数据。 【参考方案1】:

如果ThirdPartyFunction 是在 C 中定义的,那么就不可能以可以向其传递非静态成员函数的方式声明它。

您只能将常规函数绑定到常规函数指针。因此,编写一个不是调用成员函数的非静态成员的包装函数。但是如何获取调用非静态成员的实例呢?

好吧,C Api 可能允许您传递一个void* 指向将传递给回调的任意数据。如果是这样,那么您可以将对象实例作为任意数据传递。

如果没有,那么您可以使用静态实例或指向实例的静态指针,但这可能会造成很大的问题,因为这会使回调在静态初始化期间无法使用并使其不可重入,这也使得它在多线程上下文中不可用。


根据您的编辑更新:

您的 API 确实支持将 void* 数据传递给回调(两个回调),所以我建议您使用它。

* @param apc callback ...
* @param apc_cls extra argument to apc

【讨论】:

感谢您的回答。 关于 apc/apc_cls 你是对的。我没有意识到我可以这样使用它们。【参考方案2】:

您不能将指向成员函数的指针传递给指向非成员或静态成员函数的指针,因为成员函数需要调用它们的对象。指向该对象的指针成为您的成员函数的隐式 this 参数。

如果您需要分派对成员函数的调用,则需要有一种方法来查找调用该函数的对象。这里有两种通用方法:

将指向对象的对象指针放在程序已知的某个位置。传递一个静态函数来查找对象目标,并在其上调用回调 依靠 API 功能,您可以将“用户数据”传递给回调,并在其中放置一个对象指针。

在这两种情况下,您都将静态或非成员函数注册为第三方 API 的回调函数。

这是第一种方法的示例:

struct MyClass 
    int MyCallbackMethod( ... );
;

static MyClass *instancePointer;

static int MyStaticCallback( ...) 
    instancePointer->MyCallbackMethod(...);


int main(int argc, char* argv[]) 
    MyClass instanceOfMyClass;
    // Point instancePointer to instanceOfMyClass for the static callbacl
    instancePointer = &instanceOfMyClass;
    // Register static callback
    ThirdPartyFunction( &MyStaticCallback, ... );

第二种方法取决于 API。假设您的回调接收到可以传递给ThirdPartyFunctionvoid*,实现如下:

struct MyClass 
    int MyCallbackMethod( ... );
;

static int MyStaticCallback(void* userData) 
    MyClass *instancePointer = (MyClass*)userData;
    instancePointer->MyCallbackMethod();


int main(int argc, char* argv[]) 
    MyClass instanceOfMyClass;
    ThirdPartyFunction( &MyStaticCallback, (void*)(&instanceOfMyClass));

在您的情况下,cls 指针似乎是传递回您的回调的内容,而 apc_cls / dh_cls 传递给第三方函数以进行注册的内容。

【讨论】:

谢谢,你的第一个解决方案对我来说似乎很有趣,我会尽快测试它。 @dom_beau 请记住,第一个解决方案更难维护,除非您发送调用的对象是真正的单例。在有多个收件人的情况下,最好使用带有apc_clsdh_cls void 指针的第二种方法。 考虑在内。谢谢

以上是关于用指向实例中方法的指针替换参数中的静态函数指针的主要内容,如果未能解决你的问题,请参考以下文章

返回指向函数静态数据的指针是不是合适?

为啥可以从指向实例化基类对象的强制转换指针调用非静态派生类方法?

在cpp的静态函数中访问这个指针

从 C++ 中的共享库调用指向列表的静态指针

重温委托(delegate)和事件(event)

C言语指针变量作为函数参数