C++;类成员函数指针到函数指针 我把LuabFin作为我的LUA给C++包装器。Luabind提供了一种方法,可以使用我自己的回调函数来处理lua抛出的异常,set_pcall_callback()。因此,我解释了文档中的一个示例,更改为logger->log()函数,并将该函数放入一个名为“Engine”的类中,因此它不再是一个常规的全局函数,而是一个成员函数,这就是我的问题所在

C++;类成员函数指针到函数指针 我把LuabFin作为我的LUA给C++包装器。Luabind提供了一种方法,可以使用我自己的回调函数来处理lua抛出的异常,set_pcall_callback()。因此,我解释了文档中的一个示例,更改为logger->log()函数,并将该函数放入一个名为“Engine”的类中,因此它不再是一个常规的全局函数,而是一个成员函数,这就是我的问题所在,c++,casting,function-pointers,luabind,C++,Casting,Function Pointers,Luabind,以下是相关的代码片段: class Engine //Whole class not shown for brevity { public: Engine(); ~Engine(); void Run(); int pcall_log(lua_State*); private: ILogger *logger; }; Engine::Run() { lua_State* L = lua_open(); luaL_openlibs(L); open(L);

以下是相关的代码片段:

class Engine //Whole class not shown for brevity
{
public:
    Engine();
    ~Engine();
    void Run();
    int pcall_log(lua_State*);
private:
    ILogger *logger;
};

Engine::Run()
{
lua_State* L = lua_open();
luaL_openlibs(L);
open(L);
luabind::set_pcall_callback(&Engine::pcall_log); //<--- Problem line
//etc...rest of the code not shown for brevity
}

int Engine::pcall_log(lua_State *L)
{
lua_Debug d;
lua_getstack( L,1,&d);
lua_getinfo( L, "Sln", &d);
lua_pop(L, 1);
stringstream ss;
ss.clear();
ss.str("");
ss << d.short_src;
ss << ": ";
ss << d.currentline;
ss << ": ";
if ( d.name != 0)
{
    ss << d.namewhat;
    ss << " ";
    ss << d.name;
    ss << ") ";
}
ss << lua_tostring(L, -1);
logger->log(ss.str().c_str(),ELL_ERROR);
return 1;
}
因此,似乎错误在于函数需要一个常规函数指针,而不是类成员函数指针。是否有方法强制转换或使用中间函数指针传递给set_pcall_callback()函数

谢谢大家!

否。成员函数不是自由函数。类型完全不同,指向成员函数的指针(PTMF)与函数指针是完全不同的、不兼容的对象。(例如,PTMF通常要大得多。)最重要的是,指向成员的指针必须始终与指向要调用其成员的对象的实例指针一起使用,因此您甚至不能像使用函数指针那样使用PTMF

与C代码交互的最简单解决方案是编写一个全局包装函数来分派您的调用,或者使您的成员函数成为静态的(在这种情况下,它本质上成为一个自由函数):


这里的概念问题是您有一个engine类,尽管实际上只有一个实例。对于一个包含许多对象的真正类,PTMF没有意义,因为您必须指定用于调用的对象,而您的引擎类可能本质上是一个完全静态的单例类(即美化的命名空间)。

作为回调,通常使用
静态函数
s:

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

这将解决您的问题。

不适合您的LUA问题,但可能适用于其他库: 如果函数请求一个函数指针,如FUNC(Value*PARAM,…),并且可以确保对象的寿命大于存储的函数指针,那么您也可以在技术上使用方法指针(在堆栈上看起来相同),但是C++防止方法指针直接指向函数指针。 但通过一个小技巧,您还可以将方法指针强制转换为函数指针:

template<typename M> inline void* GetMethodPointer(M ptr)
{
    return *reinterpret_cast<void**>(&ptr);
}
模板内联void*GetMethodPointer(M ptr)
{
返回*重新解释铸件(&ptr);
}
使用它,您可以使用方法指针,例如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);
this->m_pDaemon=MHD_start_守护进程(MHD_使用每个连接的线程,this->m_wPort,NULL,NULL,reinterpret_cast(GetMethodPointer(&CMyWebServer::AccessHandlerCallback)),this,MHD_选项\u END);

但是要意识到这一点。您必须注意该对象的生命周期。调用约定也必须匹配。

在C++时期,从方法指针到函数指针的显式转换是非法的。 但是有一个黑客。我们必须首先将(const)方法指针转换为(const)void*,然后再转换为(const)函数指针。它是有效的。为什么不呢?所有东西,我的意思是所有东西都可以用一个
void*
来指向,因为所有东西都有一个地址


警告:以下是危险的黑客领地。如果你正在为战斗机或诸如此类的东西开发软件,你应该知道如何更好地使用它。我不负责任!我在这里提供这些只是为了教育目的


诀窍是我们必须通过中间转换在方法指针和函数指针之间进行转换,可能是cv(const volatile)限定的,
void*

通过这种方式,我们可以通过函数指针调用成员函数(指针),第一个参数是指向目标类对象的
类型*
指针,这相当于成员函数调用的
This*

给定一个:
methodpointer类型f

然后

FunctionPointerType m_pFn=reinterpret_cast(reinterpret_cast(f));
或者,为了使其更明确,对于非常量和常量成员函数,请依次使用以下两种函数:

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 );
}
模板
void*getMethodVoidPointer(MP ptr)
{
返回*重新解释铸件(&ptr);
}
模板
FP getFunctionPointer(void*p)
{
返回重新解释(p);
}
模板
常量无效*getMethodConstVoidPointer(MP ptr)
{
返回*重新解释铸件(&ptr);
}
模板
FP getConstFunctionPointer(常量无效*p)
{
返回重新解释(p);
}
使用C++17中的完整可编译示例在此处现场使用它:


它也可以在Visual Studio中使用。

如果您可以将
pcall\u log
设置为静态,那么这可能是最简单的修复方法。这肯定不是最简单的解决方案。最简单的方法是使方法静态化。这是一个漂亮而完整的答案+1.@Let_Me_Be:这取决于您的类,不是吗?如果您的类实际上只是应该是全局函数的包装器,那么
静态
就是一种方法。但这可能并不总是一个选项。包装器解决方案解决了这个问题。engine类包装了图形和事件指针,因此我不必到处传递全局上下文结构。如果
lua_State
允许您存储和检索一些
用户数据
,你可以把
这个
放在那里。我的回答中有一个重要的提示:用户定义的参数(上面调用的param)必须是回调函数的第一个参数,才能使它工作。在成员函数上,此指针始终是堆栈上的第一个参数。因此,如果我们将成员函数强制转换为非成员函数,那么必须确保将this指针作为第一个参数传递。
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);
FunctionPointerType m_pFn = reinterpret_cast<FunctionPointerType>( reinterpret_cast<void*&>( f ) );
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 );
}