[Date Prev][Date Next][Thread Prev][Thread Next]
[Date Index]
[Thread Index]
- Subject: My implementation of multiple inheritance from C++
- From: David Demelier <demelier.david@...>
- Date: Sat, 29 Jun 2013 20:37:30 +0200
Hi there,
I would like some comments on my multiple inheritance implementation
from C++. But first, I would like to thanks levi on #lua@freenode.net
who helped me much on this :-).
First, let's explain how I did.
Let say I have a base object, here it's "Object". I create a metatable
in the registry named "Object" and then add a table of it's method
into its __index field.
So when I try to access any field, it will lookup over the __index
table of method, this is much easy and the most common usage.
Now, I create a subclass Widget with some methods, I create a
metatable again, plus a table of its method to __index just like
before. This class will derive from Object.
The only thing change now is, I create a new metatable and add
"parents" field set to table which contains "Object", then I add a
__index field that points to a special lookup function. This function
will iterate over all "parents" and call getfield on it. Finally I set
this new table as the metatable of the Widget's method tables (aka
__index).
To summary :
Object is a metatable store in the registry, it has a __index field
set to a table of functions.
Widget is also a metatable stored in the registry, also has an __index
field set to table of functions, however this __index table has a
metatable with "parents" set to { "Object" } and has a __index
function that lookup over parents.
I've attached my C++ implementation, I didn't have much ideas about
classes so sorry about the non-sense of them but we have :
Object
^
|
Widget
^
|
Button Foo
^ ^
| |
Multiple
And with the attached file, if you push a userdata with "Multiple" you
are able to call any method from any classes that inherits from.
I would like some comments on my implementation as it is a very
important part of my future project, so I want to be sure it's good,
secure and performant.
Can you please tell me :
1. Any security issue?
2. Performance issue?
3. Too complex implementation?
Any comments are welcome :-)
The essential functions to check are createClass, createMetatable,
createInheritance and the lookup function.
Regards,
--
Demelier David
#include <iostream>
#include <string>
#include <vector>
#include <lua.hpp>
using namespace std;
/* -------------------------------------------------------
* LuaClass helpers
* ------------------------------------------------------- */
struct LuaClass {
std::string name;
std::vector<std::string> inherits;
const luaL_Reg *methods;
const luaL_Reg *metamethods;
LuaClass()
: methods(nullptr)
, metamethods(nullptr)
{
}
};
static int lookup(lua_State *L)
{
string key;
luaL_checktype(L, 1, LUA_TTABLE);
key = luaL_checkstring(L, 2);
// Do not have "inherits"?
luaL_getmetafield(L, 1, "inherits");
if (lua_type(L, -1) == LUA_TNIL)
return 1;
// Iterate over them
lua_pushnil(L);
while (lua_next(L, -2)) {
luaL_getmetatable(L, lua_tostring(L, -1));
lua_getfield(L, -1, "__index");
lua_getfield(L, -1, key.c_str());
// Found?
if (lua_type(L, -1) != LUA_TNIL) {
lua_insert(L, 3);
lua_settop(L, 3);
return 1;
}
lua_pop(L, 4);
}
lua_pop(L, 1);
lua_pushnil(L);
return 1;
}
/**
* This function will update the metatable's __index field of the object to
* add a new metatable with a __index function to lookup parents.
*
* Example:
*
* +-----------------+ +-----------------+
* | Metatable A | | Methods |
* +-----------------+ +-----------------+
* | __index -> | | methodA() |
* +-----------------+ | methodB() |
* +-----------------+
*
* After:
* +-----------------+ +-----------------+
* | Metatable A | | Methods |
* +-----------------+ +-----------------+
* | __index -> | | __index F (1.) | -> lookup function
* +-----------------+ | inherits T (2.) | -> { "B" }
* +-----------------+
* | methodA() |
* | methodB() |
* +-----------------+
*
* The table of methods of A's __index is updated with a new metatable that
* has a __index field set to a lookup function (figure 1). This lookup function
* will iterate over the inherits table of classes (figure 2) and check if that
* class has a __index[key] where key is the one wanted.
*
* So a typical usage:
*
* object:method()
*
* 1. The __index table of the object metatable check if the table methods has
* the `method' key.
* 2. If the key is not present, it use the __index of that table of methods and
* iterate over the parents, so here, B's __index table of methods.
* 3. And if not found, go to 2.
*/
static void createInheritance(lua_State *L, const LuaClass & lc)
{
luaL_getmetatable(L, lc.name.c_str());
lua_getfield(L, -1, "__index");
lua_createtable(L, 1, 1);
lua_pushcfunction(L, lookup);
lua_setfield(L, -2, "__index");
lua_createtable(L, lc.inherits.size(), lc.inherits.size());
int i = 1;
for (const string & s : lc.inherits) {
lua_pushstring(L, s.c_str());
lua_rawseti(L, -2, i++);
}
lua_setfield(L, -2, "inherits");
lua_setmetatable(L, -2);
lua_pop(L, 2);
}
/**
* This function will create a metatable for the object in the registry. Then
* we add a table of methods into it's __index field.
*/
static bool createMetatable(lua_State *L, const LuaClass & lcs)
{
if (lcs.methods == nullptr)
return false;
luaL_newmetatable(L, lcs.name.c_str());
// Add optional metamethods, except __index
if (lcs.metamethods != nullptr)
luaL_setfuncs(L, lcs.metamethods, 0);
// Add methods
lua_createtable(L, 0, 0);
luaL_setfuncs(L, lcs.methods, 0);
lua_setfield(L, -2, "__index");
lua_pop(L, 1);
return true;
}
void createClass(lua_State *L, const LuaClass & lcs)
{
if (createMetatable(L, lcs) && lcs.inherits.size() > 0)
createInheritance(L, lcs);
}
/* --------------------------------------------------------
* Object class
* -------------------------------------------------------- */
class Object {
public:
Object() { }
~Object() { }
virtual string typeOf() = 0;
};
/**
* This function will check in the registry for the metatable passed
* as the first parameter and then the function in its __index[key].
*
* If found, it pushes this function and optional all arguments on the
* stack then call lua_call.
*
* Example of use: object:call("SpecificClass, "SpecificMethod", a1, a2, ...)
*/
static int l_call(lua_State *L)
{
string cls, key;
luaL_checktype(L, 1, LUA_TUSERDATA);
cls = luaL_checkstring(L, 2);
key = luaL_checkstring(L, 3);
luaL_getmetatable(L, cls.c_str());
lua_getfield(L, -1, "__index");
lua_getfield(L, -1, key.c_str());
// Move the userdata just above cls / key
lua_pushvalue(L, 1);
lua_insert(L, 4);
// Move the function (or nil) from top
lua_insert(L, 4);
lua_pop(L, 2);
// Finally call everything
lua_call(L, lua_gettop(L) - 4, LUA_MULTRET);
return lua_gettop(L) - 3;
}
static int l_typeOf(lua_State *L)
{
Object *o = *(Object **)lua_touserdata(L, 1);
lua_pushstring(L, o->typeOf().c_str());
return 1;
}
static const luaL_Reg objectMethods[] = {
{ "call", l_call },
{ "typeOf", l_typeOf },
{ nullptr, nullptr }
};
static int luaopen_object(lua_State *L)
{
LuaClass lc;
lc.name = "Object";
lc.methods = objectMethods;
createClass(L, lc);
return 0;
}
/* --------------------------------------------------------
* Widget class
* -------------------------------------------------------- */
class Widget : public Object {
public:
Widget() { }
~Widget() { }
void draw()
{
puts("Drawing a widget");
}
virtual string typeOf()
{
return "Widget";
}
};
static int l_widgetDraw(lua_State *L)
{
Widget *w = *(Widget **)lua_touserdata(L, 1);
w->draw();
return 0;
}
static const luaL_Reg widgetMethods[] = {
{ "draw", l_widgetDraw },
{ nullptr, nullptr }
};
static int luaopen_widget(lua_State *L)
{
LuaClass lc;
lc.name = "Widget";
lc.methods = widgetMethods;
// Inherits from "Object"
lc.inherits.push_back("Object");
createClass(L, lc);
return 0;
}
/* --------------------------------------------------------
* Button class
* -------------------------------------------------------- */
class Button : public Widget {
private:
std::string m_text;
public:
Button()
: m_text("Button")
{
}
~Button() { }
std::string getText()
{
return m_text;
}
void setText(std::string text)
{
m_text = text;
}
virtual string typeOf()
{
return "Button";
}
};
static int l_buttonGetText(lua_State *L)
{
Button *b = *(Button **)lua_touserdata(L, 1);
lua_pushstring(L, b->getText().c_str());
return 1;
}
static int l_buttonSetText(lua_State *L)
{
Button *b = *(Button **)lua_touserdata(L, 1);
string text = luaL_checkstring(L, 2);
b->setText(text);
return 0;
}
static const luaL_Reg buttonMethods[] = {
{ "getText", l_buttonGetText },
{ "setText", l_buttonSetText },
{ nullptr, nullptr },
};
static int luaopen_button(lua_State *L)
{
LuaClass lc;
lc.name = "Button";
lc.methods = buttonMethods;
// Inherits from "Widget" (implicit "Object")
lc.inherits.push_back("Widget");
createClass(L, lc);
return 0;
}
/* --------------------------------------------------------
* Foo class
* -------------------------------------------------------- */
class Foo {
public:
Foo() { }
~Foo() { }
void speak()
{
std::cout << "Foo is speaking" << std::endl;
}
virtual string typeOf()
{
return "Foo";
}
};
static int l_fooSpeak(lua_State *L)
{
Foo *foo = *(Foo **)lua_touserdata(L, 1);
foo->speak();
return 0;
}
static const luaL_Reg fooMethods[] = {
{ "speak", l_fooSpeak },
{ nullptr, nullptr },
};
static int luaopen_foo(lua_State *L)
{
LuaClass lc;
lc.name = "Foo";
lc.methods = fooMethods;
createClass(L, lc);
return 0;
}
/* --------------------------------------------------------
* Multiple class < Foo & Button
* -------------------------------------------------------- */
class Multiple
: public Foo
, public Button
{
public:
Multiple() { }
~Multiple() { }
void kill()
{
std::cout << "Multiple is killing" << std::endl;
}
virtual std::string typeOf()
{
return "Multiple";
}
};
static int l_multipleKill(lua_State *L)
{
Multiple *m = *(Multiple **)lua_touserdata(L, 1);
m->kill();
return 0;
}
static const luaL_Reg multipleMethods[] = {
{ "kill", l_multipleKill },
{ nullptr, nullptr },
};
static int luaopen_multiple(lua_State *L)
{
LuaClass lc;
lc.name = "Multiple";
lc.methods = multipleMethods;
// Inherits from "Button" and "Foo"
lc.inherits.push_back("Foo");
lc.inherits.push_back("Button");
createClass(L, lc);
return 0;
}
/* --------------------------------------------------------
* Just a helper function on the scope of the test
* -------------------------------------------------------- */
static int createIt(lua_State *L)
{
Multiple **m = (Multiple **)lua_newuserdata(L, sizeof (void *));
*m = new Multiple();
luaL_setmetatable(L, "Multiple");
return 1;
}
int main(void)
{
lua_State *L = luaL_newstate();
luaL_requiref(L, "_G", luaopen_base, 1);
luaL_requiref(L, "package", luaopen_package, 1);
luaL_requiref(L, "debug", luaopen_debug, 1);
luaL_requiref(L, "object", luaopen_object, 1);
luaL_requiref(L, "widget", luaopen_widget, 1);
luaL_requiref(L, "button", luaopen_button, 1);
luaL_requiref(L, "foo", luaopen_foo, 1);
luaL_requiref(L, "multiple", luaopen_multiple, 1);
lua_pop(L, 8);
lua_pushcfunction(L, createIt);
lua_setglobal(L, "createIt");
if (luaL_dofile(L, "test.lua") != LUA_OK) {
cerr << lua_tostring(L, -1) << endl;
}
return 0;
}