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
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.
As a callback, it is usual to use static function
s:
class Engine //Whole class not shown for brevity
{
....
static int pcall_log(lua_State*);
...
}
This would solve your issue.
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.
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).