如何将C++成员函数作为参数传递给C API

11

我的 C++ 程序需要调用这个 C API:

GConn* gnet_conn_new (const gchar *hostname,
                      gint port,
                      GConnFunc func);

GConnFunc的定义如下:

void (*GConnFunc) (GConn *conn);

我的问题是,如果我有一个 C++ 类,并且有一个类成员函数像这样:

Class A {
 public:
   A();
   void my_func (GConn* conn);
}

在我的A::A()构造函数中,我如何将this->myfunc作为GConnFunc参数传递给gnet_conn_new

谢谢。


提议的重复问题(https://dev59.com/n3VC5IYBdhLWcg3w-WSs)似乎*不是*重复。它讨论了在全C++上下文中传递指向成员的指针。 - dmckee --- ex-moderator kitten
4个回答

14

大多数API都提供了一个指针大小的"用户数据"参数,它可能看起来像这样:

GConn* gnet_conn_new (const gchar *hostname, 
                  gint port, 
                  GConnFunc func,
                  void* user_data); 
这将使您能够通过user_data传递一个实例,并让您的C函数像这样转发到成员函数:
void my_func(GConn *conn, void* user_data)
{
    ((MyClass*)user_data)->MyMemberFunc(conn);
}

也许你的API有一个类似的“用户数据”参数替代方案。否则,你不能没有全局变量或静态变量实现它。


3

你可以使用一个包装函数来处理这个问题,或者将my_func设为静态的(假设它不需要访问任何实例变量)。


3
简短的回答是,你不能。
长话短说:
如果API允许足够的灵活性,你可以编写一个函数并将其注册为中介来调用。通常,回调函数允许您包含额外的数据,该数据与生成回调的对象一起传递到该函数中。它通常被称为“client_data”,类型为void *或long。在这种情况下,您可以使用此数据来包含足够的信息以调用所需的函数,并指定要调用的实例,例如添加boost :: function <>。
请注意,代表成员函数的中介函数可以是静态成员或自由函数。
不幸的是,在此处您没有这个选项,因此您的工作将变得更加困难。您确实需要某种方法来确保“正确”的实例获得消息,并且没有办法从回调数据中识别该实例。有几个可能适合您的选项:
1. 确保您永远不会将多个实例用作回调目标。使用单例或某种全局跟踪一次引用事件的实例的东西来注册此实例。
2. 注册尽可能多的实例,只需确保它们都收到消息,然后处理或不处理。您可以在此处使用一种“责任链”的形式,其中目标有机会处理事件,然后返回某些内容,使循环知道是否应继续或停止。
总之,没有直接的方法可以实现您想要的内容,但是如果您考虑一些选项,则可能会获得所需的答案。提供给C API函数的任何用作回调的函数必须可引用为函数指针,而不是任何类型的成员指针...不幸的是,也不能是functor。C不知道如何处理这些内容,因此您的回调必须是自由函数或静态成员。

2

在C++类中,非静态成员函数有一个隐藏的"this"参数。如果你可以假设将其转换为C语言,该函数将被翻译为:

void A_my_func (A * const this, GConn* conn);

在您的代码中,实际上没有自动执行此转换的方法,但是您可以使用上面的原型编写包装函数,并将指针传递给C函数。

通常的处理方式是传递一个自由(非成员)函数或类静态函数。这两种类型都没有隐藏参数。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接