Lua:从c导出class时的方法和属性
Lua: methods and properties when exporting class from c
我使用 lua 作为我的 3d 引擎的脚本语言。我有几个对象的 lua "classes",现在我想使用属性而不是 getter 和 setter。所以不是这样的
local oldState = ui:GetChild("Panel1"):GetVisible()
ui:GetChild("Panel1"):SetVisible(not oldState)
我会
ui.Panel1.visible = not ui.Panel1.visible
问题是我用于创建元表和实例的 C++ 代码覆盖了 __index 方法。顺便说一句:
创建元表:
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());
}
实例化 class(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
The question is how can I have both methods and properties.
从广义上讲,方法只是恰好解析为函数的属性。
If I just add __index to RegisterClass funcs array it is never called.
这才是真正的问题吧?您的其他 post 分散了实际问题的注意力。
根据文档,luaL_newlib creates a new table。 luaL_newmetatable 也是如此。您在 RegisterClass 中创建了两个 table,这是没有意义的。您只需要创建 metatable,而您需要将 __index
和 __newindex
元方法添加到的就是这个 metatable。
您将无法让 __index
简单地指向一个 table 函数(实现 class 方法的快捷方式),如果您想手动编组则不行数据进出您的 C++ class 实例属性。它需要是一个区分方法访问(值来自class-scope)和属性访问(值来自instance-scope)的函数。
这是您的 method/property 访问权限在 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
我使用 lua 作为我的 3d 引擎的脚本语言。我有几个对象的 lua "classes",现在我想使用属性而不是 getter 和 setter。所以不是这样的
local oldState = ui:GetChild("Panel1"):GetVisible()
ui:GetChild("Panel1"):SetVisible(not oldState)
我会
ui.Panel1.visible = not ui.Panel1.visible
问题是我用于创建元表和实例的 C++ 代码覆盖了 __index 方法。顺便说一句:
创建元表:
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()); }
实例化 class(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
The question is how can I have both methods and properties.
从广义上讲,方法只是恰好解析为函数的属性。
If I just add __index to RegisterClass funcs array it is never called.
这才是真正的问题吧?您的其他 post 分散了实际问题的注意力。
根据文档,luaL_newlib creates a new table。 luaL_newmetatable 也是如此。您在 RegisterClass 中创建了两个 table,这是没有意义的。您只需要创建 metatable,而您需要将 __index
和 __newindex
元方法添加到的就是这个 metatable。
您将无法让 __index
简单地指向一个 table 函数(实现 class 方法的快捷方式),如果您想手动编组则不行数据进出您的 C++ class 实例属性。它需要是一个区分方法访问(值来自class-scope)和属性访问(值来自instance-scope)的函数。
这是您的 method/property 访问权限在 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