如何在c ++中将成员函数注册到没有lua bind的lua

Hel*_*rld 6 c++ lua

我在我的c ++游戏项目中使用lua 5.1,但是当我尝试注册c ++成员函数时,我很难使用lua.我想在lua中使用我的c ++类成员函数,但是lua_register()函数的第3个参数只能接受c类型的普通函数指针或静态成员函数的指针.

我听说lua绑定库可以解决这个问题,但我不想使用lua bind.这很好,但对我的项目来说太重了.是否有任何方法可以在没有任何库的情况下注册c ++成员函数?我该怎么办呢?

Chr*_*eck 9

我自己也经历过同样的经历.

我知道基本上有两个很好的解决方案.一个是好的,如果成员函数是针对每个lua状态只有一个的类.另一个更灵活,但更复杂,更慢.(我很想学习这些方法的其他方法/改进!)

我认为lua_bind使用一些与方法1非常相似的模板,但是使用技巧使实现像方法2一样灵活.我认为其中任何一个都比lua_bind更透明.

方法1

(1)对于my_class你要传递给lua的每个成员函数,它应该采取lua_State * L并返回int.

(2)在该LUA被初始化的时间,存储的指针相关联my_classlua_extraspace.

*static_cast<my_class**>(lua_getextraspace(L_)) = &instance;
Run Code Online (Sandbox Code Playgroud)

(3)如果要将成员函数传递给lua,请使用如下模板:

typedef int (my_class::*mem_func)(lua_State * L);

// This template wraps a member function into a C-style "free" function compatible with lua.
template <mem_func func>
int dispatch(lua_State * L) {
    my_class * ptr = *static_cast<my_class**>(lua_getextraspace(L));
    return ((*ptr).*func)(L);
}
Run Code Online (Sandbox Code Playgroud)

然后你注册这样的事情:

const luaL_Reg regs[] = {
    { "callback_1", &dispatch<&my_class::callback_1> },
    { "callback_2", &dispatch<&my_class::callback_2> },
    { "callback_3", &dispatch<&my_class::callback_3> },
    { NULL, NULL }
};
luaL_register(L, regs); 
Run Code Online (Sandbox Code Playgroud)

方法1的好处是非常简单且非常快,我认为它会比lua bind快.因为,get_extraspace除了一点指针算术之外什么都不做.最有可能的是,一个好的编译器可以优化dispatch模板,以便它所做的功能内联,并且没有开销.

您可能希望更改dispatch模板,以便在额外空间中检查空指针,这取决于您的lua状态和您的生命周期的my_class管理方式.

潜在地,您还可以在额外空间中存储更复杂的内容,例如指向几个不同对象的指针,甚至像向量或其他内容(您可以阅读有关如何在其文档中配置lua额外空间的内容).或者您可以将内容存储在lua注册表中,并且调度函数可以从那里检索它们,但是额外空间更快 - 这取决于您.

方法2

在方法2中,您基本上使用通常的lua技术将C++对象推送到lua所拥有的lua,但是在对象是C++的情况下执行它std::function并且重载_call元函数以便它调用函数.(如果您不在C++ 11中,则可以使用boost::function.)

然后,当你想将一个c ++成员函数推送到lua时,你std::bind可以使它成为一个函数对象.

这种方法的缺点是在lua中,"函数"的类型实际上是userdata,但是因为你可以调用它并且将它作为函数使用它并不重要.如果这对你不好,你可以做的一件事是使用相同的技巧,但之后,创建一个具有函数对象userdata作为upvalue的闭包,并且当调用闭包时,它只是将参数转发给函数对象并返回结果.然后闭包的类型将function在lua中,但它基本上会做同样的事情.

typedef std::function<int(lua_State *)> lua_function;
char const * cpp_function = "CPP_Function";

static int intf_dispatcher ( lua_State* L )
{
    //make a temporary copy, in case lua_remove(L,1) might cause lua to garbage collect and destroy it
    lua_function f = * static_cast<lua_function *> (luaL_checkudata(L, 1, cpp_function));
    // remove from the stack before executing, so that like all other callbacks, f finds only its intended arguments on the stack.
    lua_remove(L,1);
    int result = (f)(L);
    return result;
}

static int intf_cleanup ( lua_State* L )
{
    lua_function * d = static_cast< lua_function *> (luaL_testudata(L, 1, cpp_function));
    if (d == NULL) {
        std::cerr << "ERROR: intf_cleanup called on data of type: " << lua_typename( L, lua_type( L, 1 ) ) << std::endl;
        lua_pushstring(L, "C++ function object garbage collection failure");
        lua_error(L);
    } else {
        d->~lua_function();
    }
    return 0;
}

static int intf_tostring( lua_State* L )
{
    lua_function * d = static_cast< lua_function *> (luaL_checkudata(L, 1, cpp_function));
    // d is not null, if it was null then checkudata raised a lua error and a longjump was executed.
    std::stringstream result;
    result << "c++ function: " << std::hex << d;
    lua_pushstring(L, result.str().c_str());
    return 1;
}

void register_metatable ( lua_State* L )
{
    luaL_newmetatable(L, cpp_function);
    lua_pushcfunction(L, intf_dispatcher);
    lua_setfield(L, -2, "__call");
    lua_pushcfunction(L, intf_cleanup);
    lua_setfield(L, -2, "__gc");
    lua_pushcfunction(L, intf_tostring);
    lua_setfield(L, -2, "__tostring");
    lua_pushvalue(L, -1); //make a copy of this table, set it to be its own __index table
    lua_setfield(L, -2, "__index");

    lua_pop(L, 1);
}

void push_function( lua_State* L, const lua_function & f )
{
    void * p = lua_newuserdata(L, sizeof(lua_function));
    luaL_setmetatable(L, cpp_function);
    new (p) lua_function(f);
}
Run Code Online (Sandbox Code Playgroud)

  • 方法 1 效果很好,似乎 `luaL_register` 已被弃用(在头文件 `lauxlib.h` 中,它仅在定义了 `LUA_COMPAT_MODULE` 时才定义。对我有用的是:`lua_register(L, "callback_1", &amp;dispatch&lt;&amp;my_class ::callback_1&gt;);` (2认同)