C++:路由到成员函数的静态函数包装器?

Fra*_*ank 3 c++ design-patterns callback

我尝试过各种各样的设计方法来解决这个问题,但我似乎无法做到这一点.

我需要公开一些静态函数来用作C lib的回调函数.但是,我希望实际的实现是非静态的,所以我可以使用虚函数并在基类中重用代码.如:

class Callbacks {
  static void MyCallBack() { impl->MyCallBackImpl(); }
  ...

class CallbackImplBase {
   virtual void MyCallBackImpl() = 0;
Run Code Online (Sandbox Code Playgroud)

但是我尝试解决这个问题(Singleton,让回调包含在实现者类中,等等).我最终陷入了死胡同(impl通常最终指向基类,而不是派生类).

我想知道它是否完全可能,或者我是否坚持创建某种辅助函数而不是使用继承?

Mar*_*ork 8

问题1:

虽然它可能看起来似乎适用于您的设置,但由于未定义C++ ABI,因此无法保证其正常工作.因此从技术上讲,您不能将C++静态成员函数用作C代码使用的函数指针.

问题2:

所有C callacks(我知道)允许您将用户数据作为void*传回.您可以将其用作指向具有虚方法的对象的指针.但是,在将其转换为void*之前,必须确保将dynamic_cast <>()用于基类(具有回调中使用的虚方法的基类),否则可能无法正确解释另一端的指针(尤其是如果涉及多个继承).

问题3:

例外:C不适用于异常(特别是带有回调的旧C库).所以不要指望逃避回调的异常为调用者提供任何有意义的东西(它们更可能导致应用程序终止).

解:

你需要做的是使用extern"C"函数作为回调,它在知道类型的对象上调用虚方法并抛弃所有异常.

C pthread例程的示例

#include <iostream>

extern "C" void* start_thread(void* data);

class Work
{
    public:
    virtual ~Work() {}
    virtual void doWork() = 0;
};

/*
 * To be used as a callback for C code this MUST be declared as
 * with extern "C" linkage to make sure the calling code can
 * correctly call it
 */
void* start_thread(void* data)
{
    /*
     * Use reinterpret_cast<>() because the only thing you know
     * that you can do is cast back to a Work* pointer.
     *
     */
    Work*  work = reinterpret_cast<Work*>(data);
    try
    {
        work->doWork();
    }
    catch(...)
    {
        // Never let an exception escape a callback.
        // As you are being called back from C code this would probably result
        // in program termination as the C ABI does not know how to cope with
        // exceptions and thus would not be able to unwind the call stack.
        //
        // An exception is if the C code had been built with a C++ compiler
        // But if like pthread this is an existing C lib you are unlikely to get
        // the results you expect.
    }
    return NULL;
}

class PrintWork: public Work
{
    public:
    virtual void doWork()
    {
        std::cout << "Hi \n";
    }
};

int main()
{
    pthread_t   thread;
    PrintWork   printer;
    /*
     * Use dynamic_cast<>() here because you must make sure that
     * the underlying routine receives a Work* pointer
     * 
     * As it is working with a void* there is no way for the compiler
     * to do this intrinsically so you must do it manually at this end
     */
    int check = pthread_create(&thread,NULL,start_thread,dynamic_cast<Work*>(&printer));
    if (check == 0)
    {
        void*   result;
        pthread_join(thread,&result);
    }
}
Run Code Online (Sandbox Code Playgroud)