C++: static function wrapper that routes to member function?

前端 未结 4 334
北荒
北荒 2021-01-14 05:45

I\'ve tried all sorts of design approaches to solve this problem, but I just can\'t seem to get it right.

I need to expose some static functions to use as callback f

4条回答
  •  别那么骄傲
    2021-01-14 06:30

    Problem 1:

    Though it may look and seem to work on your setup this is not guaranteed to work as the C++ ABI is not defined. So technically you can not use C++ static member functions as functions pointers to be used by C code.

    Problem 2:

    All C callacks (that I know of) allow you to pass user data back as a void*. You can use this as the pointer to your object that has the virtual method. BUT You must make sure you use dynamic_cast<>() to the base class (the one with the virtual method used in the callback) before it is converted into the void* otherwise the pointer at the other end may not be interpreted correctly (especially if there is multiple inheritance involved).

    Problem 3:

    Exceptions: C is not designed to work with exceptions (especially old C libraries with callbacks). So don't expect exceptions that escape your callback to provide anything meaningful to the caller (they are more likely to result in application termination).

    Solution:

    What you need to do is use extern "C" function as the callback that calls the virtual method on an object of know type and throws away all exceptions.

    An example for the C pthread routines

    #include 
    
    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(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(&printer));
        if (check == 0)
        {
            void*   result;
            pthread_join(thread,&result);
        }
    }
    
    
        
    

提交回复
热议问题