I once have thought that I can override a class method in lua so that when I call that function in C++, it will do what has overriden in lua. I mean, like this :
C++ class
class Person {
public:
Person(); // ctr
virtual void shout(); // Meant to be overriden
};
Assume that I have that class binded to lua so that in lua, I can use the object :
--Lua code
p = Person:new()
p:shout()
What I'm trying to achieve is something like this :
Lua file
--luafile.lua
p = Person:new() --instantiate
--override shout()
p.shout = function(self) print("OVERRIDEN!") end
C++ code
int main() {
lua_State* l = lua_open();
luaL_loadlibs(l);
bind_person_class(l);
luaL_dofile("luafile.lua");
Person* p = (Person*) get_userdata_in_global(l, "p"); // get the created person in lua
p->shout(); // expecting "OVERRIDEN" to be printed on screen
lua_close(l);
return 0;
}
In the code above, you can see that I'm trying to override the Person's method in lua and expect the overriden method to be called from c++. However, when i try it, the overriden method is not executed. What I'm trying to achieve is the overriden method is executed in C++. How do you achieve this?
===================
I've thought a way to achieve this, but I'm not sure if this is good. My idea is the exported class should have a string representing the global variable name in lua that is used to hold the instance of this class. Like this:
class Person {
public:
Person();
string luaVarName; // lua's global variable to hold this class
virtual void shout() {
luaL_dostring(luaVarName + ":shoutScript()"); // now shout will call shoutScript() in lua
}
};
So, in lua, the object is responsible to implement shoutScript() and assign global var to object :
--LUA
p = Person:new()
p.shoutScript = function(self) print("OVERRIDEN") end
p.luaVarName = "p"
With codes above, I can achieve what I want (haven't tested it, though). But, is there any other, proper way to achieve what I want?
What we did in lqt, the automated binding of Qt to Lua, is that for each class that we bind, which has virtual methods, we create a proxy "shell" class, which registers itself into the Lua state.
So for your (simplified) class:
class Person {
public:
virtual void shout(); // Meant to be overriden
};
We generate the following class:
class lqt_shell_Person : public Person {
lua_State *L;
public:
lqt_shell_Person(lua_State *L); // registers itself into the Lua state
virtual void shout();
};
We represent these objects in Lua using userdata. Each has it's own environment table, to which we point the __newindex
and __index
metamethods (__index
function looks up in the environment and then in the class table). Using this, the user can store custom fields on the objects. He can also implement virtual functions, like this:
p = Person.new()
function p:shout() print("Hello world!") end
In our lqt_shell_Person::shout
method, we first fetch the arguments, and then check whether there is a function shout
in the environment table of the userdata. If there is, we call it with the arguments. If there isn't any, we call the original function. In case of abstract methods, we throw a Lua error.
Hope you find this useful.
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