如何将C ++嵌套类注册到Lua

时间:2016-07-02 17:21:04

标签: c++ lua

我目前能够将C ++类绑定到Lua,包含在我使用luaL_requiref加载的模块中,并使用适当的静态打开函数执行luaL_newmetatableluaL_setfuncs等处理。效果很好。

但是如果我想绑定一个嵌套类呢?

考虑以下C ++代码:

class Foo {
public:
    Foo(){}
    void do_something();

    class Bar {
    public:
        Bar(){}
        void do_something_else();
    };
};

和Lua注册:

int foo_new(lua_State* L) {
    new(lua_newuserdata(L, sizeof(foo)))foo();
    luaL_setmetatable(L, "Foo");
    return 1;
}
int foo_do_something(lua_State* L) {
    Foo* foo = (Foo*)luaL_checkudata(L, 1, "Foo");
    foo->do_something();
    return 0;
}
int luaopen_foo(lua_State* L) {
    const luaL_Reg functions[] = {
        {"__index", foo_new},
        {"do_something", foo_do_something},
        {nullptr, nullptr}
    };
    if( luaL_newmetatable(L, "Foo") ) {
        luaL_setfuncs(L, functions, 0);
        lua_pushvalue(L, -1);
        lua_setfield(L, -2, "__index");
    }
    return 1;
}

...

luaL_requiref(L, "Foo", luaopen_foo, 1);

我可以在Lua中访问Foo::do_something()

foo = Foo()
foo:do_something()

现在问题:如何在Lua中注册Foo::Bar嵌套类,以便我可以这样访问它:

bar = Foo.Bar()
bar:do_something_else()

基本上,我想在Bar metatable而不是全局注册Foo方法。我是否需要再次拨打luaL_requiref,或者我可以在一个luaL_requiref中进行此操作吗?

谢谢!

1 个答案:

答案 0 :(得分:0)

编辑:好的,现在它是一个完全不同的问题。

是的,您可以通过一次通话完成。

luaL_requiref函数只是调用带有铃声和口哨声的传递函数,例如检查模块是否尚未加载(并更新package.loaded表),注册相应的全局值等等。

我认为您不希望在BarFoo与[{1}}分开加载Foo,因此只需Bar个条目"Foo"应该足够了。同样,全局package.loaded变量也不需要。

所以,只需将其作为Bar的一个字段,即可。

P.S。确保您的析构函数被调用:通常,如果您将Foo与展示位置新用,则需要使用lua_newuserdata元方法。

EDIT2:修改你的luaopen_Foo方法(注意__gc,而不是__call为构造函数。我个人更喜欢__index用于此目的,但如果你想创建它们{{1那么你需要new):

local f = Foo()

如果你好奇,luaL_requiref函数(带有一些伪代码)是什么:

__call

请注意区别:int luaopen_foo(lua_State* L) { static const luaL_Reg functions[] = { {"__call" , foo_new}, {"do_something" , foo_do_something}, {nullptr , nullptr} }; if (luaL_newmetatable(L, "Foo")) { luaL_setfuncs(L, functions, 0); lua_pushvalue(L, -1); lua_setfield(L, -2, "__index"); // ================= // Here, your "Foo" metatable is on top of your stack // about to be returned as the result of your luaopen_Foo. // Simply put another table above and set if as a Foo.Bar if (luaopen_FooBar(L)) lua_setfield(L, -2, "Bar"); } return 1; } int luaopen_FooBar(lua_State * L) { static const luaL_Reg functions[] = { {"__call" , foo_bar_new}, {"do_something_else" , foo_bar_do_something_else}, {nullptr , nullptr} }; // luaL_newmetatable puts its result on top of the stack // - exactly what we want for lua_setfield if (luaL_newmetatable(L, "Foo::Bar")) { luaL_setfuncs(L, functions, 0); lua_pushvalue(L, -1); lua_setfield(L, -2, "__index"); } return 1; // Indicate the result is on top of the stack } 从Lua中调用您的函数,这可确保在执行后正确清理堆栈,例如:你可以在那里放一些垃圾,你必须要确保你的顶级值是你想要与void luaL_requiref(lua_State *L, const char *name, lua_CFunction openf, int set_global) { if (!try_get_already_loaded_module(modname)) { lua_pushcfunction(L, openf); // Call the openf function lua_pushstring(L, modname); // with modname as its argument lua_call(L, 1, 1); memorize_the_result_for_future(modname); } if (set_global) { lua_pushvalue(L, -1); // copy the module lua_setglobal(L, modname); // set _G[modname] = module } } 一起产生的。但是,如果直接调用该函数,那么你就不会有这种奢侈。因此,请确保仅在堆栈顶部添加单个值