I use lua as a scripting language for my 3d engine. I have lua "classes" for several objects and now I want to use properties instead of getters and setters. So instead of something like this
local oldState = ui:GetChild("Panel1"):GetVisible()
ui:GetChild("Panel1"):SetVisible(not oldState)
I would just
ui.Panel1.visible = not ui.Panel1.visible
The problem is my C++ code for creating metatables and instanced overrides the __index method. Here it is by the way:
Create a metatable:
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());
}
Instantiate the class (the lua object only holds a pointer to an actual data that is stored in C++ code):
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;
}
The question is how can I have both methods and properties. If I just add __index
to CLUAScript::RegisterClass
funcs array it is never called. And I cannot imagine a way to remove its redefinition in CLUAScript::NewInstanceClass
.
If this code is not enough, here is the links to files working with lua: lua helper class, functions for UI, functions for Objects, and testing lua script
The question is how can I have both methods and properties.
Broadly speaking, methods are just properties that happen to resolve to functions.
If I just add __index to RegisterClass funcs array it is never called.
This is the actual problem, right? The rest of your post distracts from the real issue.
According to the docs, luaL_newlib creates a new table. So does luaL_newmetatable. You're creating two tables in RegisterClass, which makes no sense. You need to create only the metatable, and it's this metatable that you need to add your __index
and __newindex
metamethods to.
You won't be able to have __index
simply point to a table of funcs (the shortcut way to implement class methods), not if you want to manually marshal data to and from your C++ class instance properties. It needs to be a function that distinguishes method access (value comes from class-scope) and property access (value comes from instance-scope).
Here's an example of how your method/property access would work in Lua. The specifics are different using the C API, but the approach would be the same:
-- 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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With