C++ class member function pointer to function pointer

后端 未结 4 1736
情话喂你
情话喂你 2020-12-06 19:32

I am using luabind as my lua to C++ wrapper. Luabind offers a method to use my own callback function to handle exceptions thrown by lua, set_pcall_callback(). So I paraphras

相关标签:
4条回答
  • 2020-12-06 19:58

    Explicit conversion from method pointer to function pointer is illegal in C++ - period.

    But there is a hack. We have to first convert the (const) method pointer to (const) void* and then to (const) function pointer. And it works. And why wouldn't it? Everything and I mean everything can be pointed to by a void* because everything has an address.


    WARNING: The below is DAAEINGEROUS hack territory. If you're developing software for a fighter jet or whatnot, you should know better than to use this. I'm not responsible! I'm just providing this here for educational purposes.


    The trick is that you can't call a member function pointer through a void* object in memory. The pointer must be converted to a pointer of the target object's class type. However you can call the same member function through a function pointer to void* to this* object.

    Given a: MethodPointerType f;

    then

    FunctionPointerType m_pFn = reinterpret_cast<FunctionPointerType>( reinterpret_cast<void*&>( f ) );
    

    or to make it more explicit use two of the following in sequence, for non-const and const member functions:

    template<typename MP>
    void* getMethodVoidPointer( MP ptr )
    {
        return *reinterpret_cast<void**>( &ptr );
    }
    template<typename FP>
    FP getFunctionPointer( void* p )
    {
        return reinterpret_cast<FP>( p );
    }
    template<typename MP>
    const void* getMethodConstVoidPointer( MP ptr )
    {
        return *reinterpret_cast<const void**>( &ptr );
    }
    template<typename FP>
    FP getConstFunctionPointer( const void* p )
    {
        return reinterpret_cast<FP>( p );
    }
    

    Play with it live here with a complete compilable sample in C++17: https://onlinegdb.com/HybR8crqw

    It works in Visual Studio too.

    0 讨论(0)
  • 2020-12-06 20:07

    As a callback, it is usual to use static functions:

    class Engine //Whole class not shown for brevity
    {
        ....
        static int pcall_log(lua_State*);
        ...
    }
    

    This would solve your issue.

    0 讨论(0)
  • 2020-12-06 20:13

    Not suitable for your LUA problem, but maybe on other libraries: If a function requests a a function pointer like func(void* param, ...) and you can ensure that the lifetime of your object is greater than the stored function pointer, then you could technically also use a method pointer (looks the same on the stack), but C++ prevents direct casting of method pointers to function pointers.

    But with a little trick, you can also cast method pointers to function pointers:

    template<typename M> inline void* GetMethodPointer(M ptr)
    {
        return *reinterpret_cast<void**>(&ptr);
    }
    

    Using that, you can use method pointers for example with libmicrohttpd:

    this->m_pDaemon = MHD_start_daemon(MHD_USE_THREAD_PER_CONNECTION, this->m_wPort, NULL, NULL, reinterpret_cast<MHD_AccessHandlerCallback>(GetMethodPointer(&CMyWebServer::AccessHandlerCallback)), this, MHD_OPTION_END);
    

    But be aware of it. You must take care of the lifetime of that object. Also the calling conventions must match.

    0 讨论(0)
  • 2020-12-06 20:23

    No. A member function is not a free function. The type is entirely different, and a pointer to a member function (PTMF) is a completely different, incompatible object from a function pointer. (A PTMF is usually much bigger, for example.) Most importantly a pointer-to-member must always be used together with an instance pointer to the object whose member you want to call, so you cannot even use a PTMF the same way you use a function pointer.

    The easiest solution for interacting with C code is to write a global wrapper function that dispatches your call, or to make your member function static (in which case it becomes essentially a free function):

    // global!
    
    Engine * myEngine;
    int theCallback(lua_State * L)
    {
      return myEngine->pcall_log(L);
    }
    
    Engine::Run()
    {
      /* ... */
      myEngine = this;
      luabind::set_pcall_callback(&theCallback);
      /* ... */
    }
    

    The conceptual problem here is that you have an engine class, although you will practically only have one single instance of it. For a genuine class with many objects, a PTMF wouldn't make sense because you'd have to specify which object to use for the call, whereas your engine class perhaps is essentially a singleton class which could be entirely static (i.e. a glorified namespace).

    0 讨论(0)
提交回复
热议问题