【问题标题】:Lua: methods and properties when exporting class from cLua:从c导出类时的方法和属性
【发布时间】:2015-01-12 17:25:03
【问题描述】:

我使用 lua 作为我的 3d 引擎的脚本语言。我有几个对象的lua“类”,现在我想使用属性而不是getter和setter。所以不要像这样

local oldState = ui:GetChild("Panel1"):GetVisible()
ui:GetChild("Panel1"):SetVisible(not oldState)

我会的

ui.Panel1.visible = not ui.Panel1.visible

问题是我用于创建元表和实例化的 C++ 代码覆盖了 __index 方法。顺便说一句:

  1. 创建元表:

    void CLUAScript::RegisterClass(const luaL_Reg funcs[], std::string const& className)
    {
        luaL_newmetatable(m_lua_state, std::string("Classes." + className).c_str());
        luaL_newlib( m_lua_state, funcs);
        lua_setglobal(m_lua_state, className.c_str());
    }
    
  2. 实例化类(lua 对象只包含一个指向存储在 C++ 代码中的实际数据的指针):

    int CLUAScript::NewInstanceClass(void* instance, std::string const& className)
    {
        if (!instance)
        {
            lua_pushnil(m_lua_state);
            return 1;
        }
    
        luaL_checktype(m_lua_state, 1, LUA_TTABLE);
    
        lua_newtable(m_lua_state);
    
        lua_pushvalue(m_lua_state,1);       
        lua_setmetatable(m_lua_state, -2);
    
        lua_pushvalue(m_lua_state,1);
        lua_setfield(m_lua_state, 1, "__index");  
    
        void **s = (void **)lua_newuserdata(m_lua_state, sizeof(void *));
    
        *s = instance;
        luaL_getmetatable(m_lua_state, std::string("Classes." + className).c_str());
        lua_setmetatable(m_lua_state, -2);
        lua_setfield(m_lua_state, -2, "__self"); 
    
        return 1; 
    }
    

问题是我怎样才能同时拥有方法和属性。如果我只是将__index 添加到CLUAScript::RegisterClass funcs 数组,它永远不会被调用。而且我无法想象有一种方法可以删除它在CLUAScript::NewInstanceClass 中的重新定义。

如果这段代码还不够,这里是使用 lua 的文件的链接: lua helper class, functions for UI, functions for Objects,和 testing lua script

【问题讨论】:

  • 为了帮助阐明C++代码和lua代码之间的联系,ui是什么,ui:GetChild("property_name")返回什么?例如,ui 是来自 C++ 领域的用户数据,还是NewInstanceClass 返回的带有__self = udata_object 设置的某个表?
  • “ui”是 UI 元素的一个实例,例如按钮、面板或整个屏幕。每个元素都可以有子元素,可以使用 GetChild(name) 方法进行访问。这些子元素也将是 UI 元素。所以这是一个表,它包含一个指向 c++ 实例的指针作为用户数据,以及 NewInstanceClass 函数返回的元表中的一些方法。在这种情况下,“ui”是一个局部变量,代表整个屏幕(UI 树的根元素)。
  • 如果你在 lua 中创建 GetChildGetVisible 独立函数会怎样?然后,您可以将ui.Panel1 作为语法糖的一种形式,转换为GetChild(ui, "Panel1")
  • 问题不是所有的方法都可以用属性代替。这就是为什么我想对同一个实体同时使用方法和属性。至于全局函数,我认为这不是很好的解决方案,因为不同的类可以有相似的方法,我要么必须检查每次调用接收到的对象的类型,然后做不同的事情,要么为每个类创建不同的函数类,这将导致这些函数的名称复杂。
  • 对属性和方法有什么限制吗?例如,属性和方法可以同名吗?是否可以将新方法和属性从 lua 脚本分配给实例?

标签: c++ oop methods properties lua


【解决方案1】:

问题是我怎样才能同时拥有方法和属性。

从广义上讲,方法只是碰巧解析为函数的属性。

如果我只是将 __index 添加到 RegisterClass funcs 数组中,它将永远不会被调用。

这才是真正的问题,对吧?您帖子的其余部分分散了实际问题的注意力。

根据文档,luaL_newlib creates a new table。 luaL_newmetatable 也是如此。您在 RegisterClass 中创建了两个表,这没有任何意义。您只需要创建元表,您需要将__index__newindex 元方法添加到此元表。

你不能让__index 简单地指向一个函数表(实现类方法的快捷方式),而不是如果你想手动编组数据进出你的 C++ 类实例属性。它需要是一个区分方法访问(值来自类范围)和属性访问(值来自实例范围)的函数。


这是一个示例,说明您的方法/属性访问如何在 Lua 中工作。使用 C API 的细节有所不同,但方法是相同的:

-- This is the metatable you create in RegisterClass for the C++ class 'Foo'
Foo = { }

-- This is pretty close to how your __index metamethod would work in the C++ code,
-- except you're going to have to write code that resolves which field on the C++ object
-- corresponds to 'key', if any, and push that onto the stack.
function Foo.__index(instance,key)
    local method = rawget(Foo,key)
    if method then
        return method
    end
    return instance.properties[key]
end

-- This is pretty close, too, except that if you want users to be able to add properties to the Lua object
-- that coexist with the C++ object properties, you'll need to write the value to the right place.
function Foo.__newindex(instance,key,value)
    instance.properties[key] = value
end

-- this doesn't have to be a method on the metatable
function Foo:new(state)
    return setmetatable({ properties = state}, self)
end

-- example of a class method
function Foo:dump()
    print('dump:', self.x, self.y)
end

-- simulation of the userdata, an instance of your C++ class
cppClassInstance = {
    x = 10,
    y = 20,
}

obj = Foo:new(cppClassInstance)
print(obj.x) -- read `x`, which is resolved to cppClassInstance.x
obj.x = 5150 -- write to 'x', which writes to cppClassInstance.x
print(obj.x) -- witness our change
obj:dump() -- call a class method

【讨论】:

    猜你喜欢
    • 2014-09-30
    • 1970-01-01
    • 1970-01-01
    • 2019-05-03
    • 2013-12-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多