如何将指向非静态成员方法的指针传递给 extern "C" 函数?

How do I hand over a pointer to a non-static member method to an extern "C" function?

我有一个名为 tmc 的 class,它现在包含(除其他外,此处不相关)一个私有构造函数,一个静态工厂方法,以及一个名为 ReadLoop (void*):

的实例方法
extern "C" {
#include <pigpiod_if2.h>
}

class tmc {
public:
  static tmc* Initialize ();
  static int  main ();

private:
  void *ReadLoop (void *params);
  tmc ();
  pthread_t *tmc_receiver_reader;
};

tmc::tmc ()
: tmc_receiver_reader (start_thread (tmc::ReadLoop, NULL))
{
}

void* tmc::ReadLoop (void *params)
{
  return params;
}

tmc* tmc::Initialize ()
{
  tmc* instance = new tmc ();
  return instance;
}

int tmc::main ()
{
  return (tmc::Initialize ()) == NULL ? 0 : 1;
}

现在的问题是:如何使用ReadLoop作为函数指针与pigpiod_if2中包含的start_thread ()函数一起使用?由于以下错误,此处显示的代码无法编译:

error: invalid use of non-static member function ‘void* tmc::ReadLoop(void*)’
   tmc_receiver_reader  (start_thread (tmc::ReadLoop, NULL))

我在 SO 上看到了几个问题,它们的错误消息相同,但其中 none 是关于指向 C 函数的非静态成员方法的指针。请注意,即使这里创建的对象是单例,我也无法将 ReadLoop () 设为静态。我在 Raspbian Buster 上使用 g++ 6.5.0。谢谢。

幸运的是,C 接口提供了一个 void* 回传来帮助解决这个问题。您可以使用它来指向正确的 tmc 对象:

extern "C" void* tmc_pigpio_ReadLoop_callback(void* userdata);
class tmc {
    // ...
private:
    void* ReadLoop();
    friend void* tmc_pigpio_ReadLoop_callback(void*);
    // ...
};

void* tmc_pigpio_ReadLoop_callback(void* userdata)
{
    auto* tmcp = static_cast<tmc*>(userdata);
    return tmcp->ReadLoop();
}

// Note "this" passed to start_thread.
tmc::tmc ()
: tmc_receiver_reader (start_thread (tmc_pigpio_ReadLoop_callback, this))
{
}

或者,如果您已经将 void* userdata 用于不同的用途,请将 tmc* 指针添加到它指向的结构,或者创建一个包含 tmc* 指针的新结构和要传递的其他数据。

根本无法在需要独立函数的地方使用 非静态 class 方法。它们不兼容。

您将不得不使用 staticnon-member 代理函数。幸运的是,start_thread() 允许您将用户定义的参数传递给函数,因此您可以使用它来传递 tmc 实例的 this 指针,例如:

class tmc {
  ...
private:
  static void* ReadLoopProxy(void *params);
  void* ReadLoop();
  tmc ();
  pthread_t *tmc_receiver_reader;
};

tmc::tmc ()
  : tmc_receiver_reader (start_thread (tmc::ReadLoopProxy, this))
{
}

void* tmc::ReadLoopProxy(void *params)
{
    return static_cast<tmc*>(params)->ReadLoop();
}

void* tmc::ReadLoop()
{
  return NULL;
}

...