我使用luabind作为我的lua到C ++包装器。 Luabind提供了一种方法来使用我自己的回调函数来处理lua,set_pcall_callback()抛出的异常。所以我从文档中解释了一个例子,更改是logger-> log()函数,并将函数放在一个名为'Engine'的类中,因此它不是一个常规的全局函数,而是现在的成员函数,是我的问题似乎。
以下是相关的代码剪辑:
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;
}
以下是编译器在编译期间所说的内容:
C:\pb\engine.cpp|31|error: cannot convert 'int (Engine::*)(lua_State*)' to 'int (*)(lua_State*)' for argument '1' to 'void luabind::set_pcall_callback(int (*)(lua_State*))'|
所以似乎错误是函数需要常规函数指针,而不是类成员函数指针。有没有办法转换或使用中间函数指针传递给set_pcall_callback()函数?
谢谢!
答案 0 :(得分:12)
没有。 成员函数不是自由函数。类型完全不同,指向成员函数(PTMF)的指针是与函数指针完全不同的不兼容对象。 (例如,PTMF通常要大得多。)最重要的是,指向成员的指针必须始终与指向要调用其成员的对象的实例指针一起使用,因此您甚至无法使用 PTMF与使用函数指针的方式相同。
与C代码交互的最简单的解决方案是编写一个全局包装函数来调度您的调用,或者使您的成员函数 static (在这种情况下它实际上变成了一个自由函数):< / p>
// global!
Engine * myEngine;
int theCallback(lua_State * L)
{
return myEngine->pcall_log(L);
}
Engine::Run()
{
/* ... */
myEngine = this;
luabind::set_pcall_callback(&theCallback);
/* ... */
}
这里的概念问题是你有一个引擎类,尽管你实际上只有一个实例。对于具有许多对象的真正类,PTMF没有意义,因为您必须指定要用于调用的对象,而您的引擎类可能本质上是一个单独的类,它可以是完全静态的(即一个美化的命名空间) )。
答案 1 :(得分:0)
作为回调,通常使用static function
s:
class Engine //Whole class not shown for brevity
{
....
static int pcall_log(lua_State*);
...
}
这可以解决您的问题。
答案 2 :(得分:0)
不适合您的LUA问题,但可能适用于其他库: 如果一个函数要求一个像func(void * param,...)这样的函数指针,并且您可以确保对象的生存期大于存储的函数指针,那么从技术上讲,您也可以使用方法指针(在堆栈),但C ++阻止将方法指针直接转换为函数指针。
但有一点技巧,您也可以将方法指针转换为函数指针:
template<typename M> inline void* GetMethodPointer(M ptr)
{
return *reinterpret_cast<void**>(&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);
但是要注意。您必须照顾该对象的生命周期。另外,调用约定必须匹配。
答案 3 :(得分:0)
在C ++中,从方法指针到函数指针的明确转换是非法的。
但是有一个hack。我们必须首先将(const)方法指针转换为(const)void *,然后再转换为(const)函数指针。而且有效。为什么不呢?一切,我的意思是一切都可以用void*
指向,因为一切都有地址。
警告:以下是DAAEINGEROUS hack领域。如果您正在为战斗机或其他方面开发软件,则应该比使用它更了解。我不负责!我只是出于教育目的在这里提供此信息。
诀窍在于,我们必须通过中间转换(可能转换为cv(常量易失性)合格的void*
)在方法指针和函数指针之间进行转换。
这样,我们可以通过函数指针来调用成员函数(指针),第一个参数是指向目标类对象的Type*
指针,这等效于成员函数调用的{{1 }}。
给出一个:
this*
然后
MethodPointerType f;
或更明确地使用以下两个顺序,用于非const和const成员函数:
FunctionPointerType m_pFn = reinterpret_cast<FunctionPointerType>( reinterpret_cast<void*&>( f ) );
在此处实时使用C ++ 17中完整的可编译示例进行实验:https://onlinegdb.com/HybR8crqw
它也可以在Visual Studio中工作。