如何向Lua注册C ++嵌套类

How to register C++ nested class to Lua

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

但是,如果我想绑定一个嵌套的类怎么办?

考虑以下C ++代码:

1
2
3
4
5
6
7
8
9
10
11
class Foo {
public:
    Foo(){}
    void do_something();

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

和Lua注册:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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()

1
2
foo = Foo()
foo:do_something()

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

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

本质上,我想在Foo元表中而不是全局地注册Bar方法。 我是否需要另一个呼叫luaL_requiref还是可以在一个luaL_requiref中进行?

谢谢!


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

是的,您可以在一个电话中完成。

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

我假设您不想将BarFooFoo分开加载而没有Bar,因此package.loaded中的单个"Foo"条目就足够了。同样,也不需要全局Bar变量。

因此,只需将其作为Foo的字段即可。

附言确保曾经调用过析构函数:通常,如果将lua_newuserdata与新放置位置一起使用,则需要使用__gc元方法。

EDIT2:修改luaopen_Foo方法(对于构造函数,请注意__call,而不是__index。为此,我个人更喜欢new,但如果要将其创建为local f = Foo(),则需要__call):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
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
}

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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
    }
}

请注意不同之处:luaL_requiref从Lua内部调用函数,以确保在执行后正确进行堆栈清理,例如您可以在其中放置一些垃圾,唯一需要确保的是您的顶级值是您想要与return 1;一起生成的值。但是,如果直接调用该函数,则没有那么奢侈。因此,请确保仅在堆栈顶部添加一个值。