如何注册c++嵌套类到Lua

How to register C++ nested class to Lua

本文关键字:嵌套 Lua c++ 何注册 注册      更新时间:2023-10-16

我目前能够将c++类绑定到Lua,包装在我使用luaL_requiref加载的模块中,其中包含适当的静态开放函数,用于luaL_newmetatable, luaL_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()

基本上,我想在Foo元表中注册Bar方法,而不是全局注册。我是否需要对luaL_requiref进行另一次调用,或者我可以在单个luaL_requiref中进行调用?

谢谢!

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

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

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

我假设您不想在没有Bar的情况下从FooFoo单独加载Bar,因此package.loaded中的单个"Foo"条目应该足够了。同样,也不需要全局Bar变量。

所以,简单地把它作为你的Foo的一个字段,就是这样。

注:确保你的析构函数被调用:通常,如果你使用lua_newuserdata和位置new,你需要使用__gc元方法。

EDIT2:修改luaopen_Foo方法(注意__call,而不是__index的构造函数)。我个人更喜欢new用于此目的,但如果您想将它们创建为local f = Foo(),那么您需要__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
}

如果你曾经好奇过,luaL_requiref函数(带有一些伪代码):

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;一起。但是,如果直接调用该函数,就没有这种奢侈了。所以要确保它只在堆栈顶部添加一个值