5

我使用 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() 函数?

谢谢!

4

4 回答 4

14

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

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

// global!

Engine * myEngine;
int theCallback(lua_State * L)
{
  return myEngine->pcall_log(L);
}

Engine::Run()
{
  /* ... */
  myEngine = this;
  luabind::set_pcall_callback(&theCallback);
  /* ... */
}

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

于 2011-08-02T07:33:16.050 回答
3

不适合您的 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);

但请注意。您必须注意该对象的生命周期。调用约定也必须匹配。

于 2018-09-20T14:08:32.070 回答
0

作为回调,通常使用static functions:

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

这将解决您的问题。

于 2011-08-02T07:34:14.813 回答
0

从方法指针到函数指针的显式转换在 C++ 中是非法的 - 期间。

但是有一个黑客。我们必须首先将 (const) 方法指针转换为 (const) void*,然后再转换为 (const) 函数指针。它有效。为什么不呢?一切,我的意思是一切都可以由 a 指向,void*因为一切都有一个地址。


警告:以下是 DAAEINGEROUS 黑客领域。如果你正在为战斗机或其他什么东西开发软件,你应该知道最好不要使用它。我不负责!我只是出于教育目的在这里提供这个。


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

这样我们就可以通过函数指针调用成员函数(指针),第一个参数是Type*指向目标类对象的指针,相当于成员函数调用的this*.

给定一个: MethodPointerType f;

然后

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

或使其更明确地依次使用以下两个,用于非常量和 const 成员函数:

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 );
}

使用 C++17 中的完整可编译示例在此处使用它:https ://onlinegdb.com/HybR8crqw

它也适用于 Visual Studio。

于 2020-11-20T19:26:16.943 回答