From 223f2d727e148aa31c4c8c5b45a21e0760fad833 Mon Sep 17 00:00:00 2001 From: "Nicolas \"Pixel\" Noble" Date: Sun, 9 Sep 2012 20:49:03 -0700 Subject: Got LuaObjects working again, and in a quite better shape I would add. --- includes/BLua.h | 66 ++++++++++++++++--------- src/BLua.cc | 69 +++++++++++++------------- tests/test-Lua.cc | 145 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 223 insertions(+), 57 deletions(-) diff --git a/includes/BLua.h b/includes/BLua.h index 12e921f..3e255f2 100644 --- a/includes/BLua.h +++ b/includes/BLua.h @@ -12,28 +12,45 @@ namespace Balau { class Lua; -class LuaObject { +class LuaObjectBase { public: - virtual ~LuaObject() { } + virtual void destroy() { } + void detach() { m_detached = true; } + protected: + bool isDetached() { return m_detached; } + private: + bool m_detached = false; +}; + +template +class LuaObject : public LuaObjectBase { + public: + LuaObject(T * obj) : m_obj(obj) { } + virtual void destroy() { if (!isDetached() && m_obj) delete m_obj; detach(); } + T * getObj() { return m_obj; } + private: + T * m_obj; }; class LuaObjectFactory { public: - LuaObjectFactory() : m_wantsDestruct(false), m_pushed(false) { } + LuaObjectFactory() { } virtual ~LuaObjectFactory() { } virtual void push(Lua & L); void pushDestruct(Lua & L); template static T * getMe(Lua & L, int idx = 1); protected: - virtual void pushMembers(Lua & L) = 0; - void pushMe(Lua & L, void * obj = 0, const char * name = NULL, bool isObj = true); + virtual void pushObjectAndMembers(Lua & L) = 0; + template + void pushObj(Lua & L, T * obj, const char * name = NULL); static void pushIt(Lua & L, const char * name, lua_CFunction func); static void pushMeta(Lua & L, const char * name, lua_CFunction func); - static void * getMeInternal(Lua & L, int idx); + static LuaObjectBase * getMeInternal(Lua & L, int idx); friend class Lua; private: - bool m_wantsDestruct, m_pushed; + void pushMe(Lua & L, LuaObjectBase * o, const char * name = NULL); + bool m_wantsDestruct = false, m_pushed = false; LuaObjectFactory & operator=(const LuaObjectFactory &) = delete; LuaObjectFactory(const LuaObjectFactory &) = delete; }; @@ -61,6 +78,7 @@ class Lua { void open_debug(); void open_bit(); void open_jit(); + void open_ffi(); int wrap_open(openlualib_t open) { int n = gettop(); int r = open(L); while (n < gettop()) remove(n); return r; } void openlib(const String & libname, const struct luaL_reg *l, int nup) { luaL_openlib(L, libname.to_charp(), l, nup); } @@ -136,19 +154,19 @@ class Lua { template T * recast(int n = 1) { - LuaObject * b; - T * r; + LuaObjectBase * b; + LuaObject * r; - b = (LuaObject *) LuaObjectFactory::getMeInternal(*this, n); + b = LuaObjectFactory::getMeInternal(*this, n); if (!b) error("LuaObject base object required; got null."); - r = dynamic_cast(b); + r = dynamic_cast *>(b); if (!r) error(String("Object not compatible; expecting ") + ClassName(r).c_str() + " but got *" + ClassName(b).c_str() + " instead."); - return r; + return r->getObj(); } lua_State * getState() { return L; } @@ -215,32 +233,30 @@ struct lua_functypes_t { false); \ } -#define PUSH_METHOD(classname, enumvar) pushit( \ +#define PUSH_METHOD(classname, enumvar) pushIt( \ L, \ classname##_methods[enumvar].name, \ sLua_##classname::method_##enumvar) -#define PUSH_METAMETHOD(classname, enumvar) pushmeta( \ +#define PUSH_METAMETHOD(classname, enumvar) pushMeta( \ L, \ String("__") + classname##_methods[enumvar].name, \ sLua_##classname::method_##enumvar) -#define PUSH_FUNCTION(classname, enumvar) L->declarefunc( \ +#define PUSH_FUNCTION(classname, enumvar) L.declareFunc( \ classname##_functions[enumvar].name, \ sLua_##classname::function_##enumvar) -#define PUSH_SUBFUNCTION(classname, enumvar, array) L->declarefunc( \ +#define PUSH_SUBFUNCTION(classname, enumvar, array) L.declareFunc( \ classname##_functions[enumvar].name, \ sLua_##classname::function_##enumvar, \ array) - #define CHECK_METHODS(classname) { \ int i = 0; \ while (classname##_methods[i].number != -1) { \ - if (i != classname##_methods[i].number) { \ - throw GeneralException("Data of " #classname "_methods inconsistants!"); \ - } \ + lua_functypes_t & func = classname##_methods[i]; \ + AAssert(i == func.number, "Mismatched method in class " #classname); \ i++; \ } \ } @@ -248,9 +264,8 @@ struct lua_functypes_t { #define CHECK_FUNCTIONS(classname) { \ int i = 0; \ while (classname##_functions[i].number != -1) { \ - if (i != classname##_functions[i].number) { \ - throw GeneralException("Data of " #classname "_functions inconsistants!"); \ - } \ + lua_functypes_t & func = classname##_functions[i]; \ + AAssert(i == func.number, "Mismatched function in class " #classname); \ i++; \ } \ } @@ -339,4 +354,9 @@ class LuaHelpers { template T * lua_recast(Lua & L, int n = 1) { return L.recast(n); } +template +void LuaObjectFactory::pushObj(Lua & L, T * obj, const char * name) { + pushMe(L, new (L.newuser(sizeof(LuaObject))) LuaObject(obj), name); +} + }; diff --git a/src/BLua.cc b/src/BLua.cc index 2114124..21a7452 100644 --- a/src/BLua.cc +++ b/src/BLua.cc @@ -223,30 +223,23 @@ int Balau::LuaStatics::callwrap(lua_State * __L, lua_CFunction func) { return 0; } -struct ObjData { - void * ptr; - bool isObj; -}; - int Balau::LuaStatics::collector(lua_State * __L) { Lua L(__L); - ObjData * u = (ObjData *) L.touserdata(); - if (u->isObj) { - LuaObject * obj = (LuaObject *) u->ptr; - delete obj; - } else { - free(u->ptr); - } - u->ptr = NULL; + LuaObjectBase * o = (LuaObjectBase *) L.touserdata(); + o->destroy(); return 0; } int Balau::LuaStatics::destructor(lua_State * __L) { Lua L(__L); L.push("__obj"); - L.gettable(-2, true); + L.copy(); + L.gettable(-3, true); collector(__L); L.pop(); + L.push(); + L.settable(-3, true); + L.pop(); return 0; } @@ -372,6 +365,12 @@ void Balau::Lua::open_jit() { while (n < gettop()) remove(n); } +void Balau::Lua::open_ffi() { + int n = gettop(); + luaopen_ffi(L); + while (n < gettop()) remove(n); +} + void Balau::Lua::open_bit() { int n = gettop(); luaopen_bit(L); @@ -779,6 +778,15 @@ void Balau::Lua::showstack(int level) { case LUA_TFUNCTION: t = "(Function)"; break; + case LUA_TUSERDATA: + t = "(Userdata)"; + break; + case LUA_TLIGHTUSERDATA: + t = "(Lightuserdata)"; + break; + case LUA_TTHREAD: + t = "(Thread)"; + break; default: t = "Unknown"; } @@ -796,42 +804,41 @@ void Balau::Lua::showerror() { void Balau::LuaObjectFactory::push(Lua & L) { AAssert(!(m_pushed && m_wantsDestruct), "Error: object is owned by the LUA script and can not be pushed."); L.newtable(); - pushMembers(L); + pushObjectAndMembers(L); m_pushed = true; } -void Balau::LuaObjectFactory::pushMe(Lua & L, void * o, const char * objname, bool obj) { - ObjData * u; +void Balau::LuaObjectFactory::pushMe(Lua & L, LuaObjectBase * o, const char * objname) { L.push("__obj"); - u = (ObjData *) L.newuser(sizeof(ObjData)); - u->ptr = o; - u->isObj = obj; + L.insert(-2); + pushMeta(L, "__gc", LuaStatics::collector); L.settable(-3, true); if (objname && *objname) { L.push("__objname"); L.push(objname); L.settable(-3, true); } + pushIt(L, "destroy", LuaStatics::destructor); + if (!m_wantsDestruct) + o->detach(); } -void * Balau::LuaObjectFactory::getMeInternal(Lua & L, int i) { - ObjData * u = NULL; +Balau::LuaObjectBase * Balau::LuaObjectFactory::getMeInternal(Lua & L, int i) { + LuaObjectBase * o; if (L.istable(i)) { L.push("__obj"); L.gettable(i, true); - if (!(u = (ObjData *) L.touserdata())) + if (!(o = (LuaObjectBase *) L.touserdata())) L.error("Table is not an object."); - if (!u->ptr) - L.error("Object already destroyed."); L.pop(); } else if (L.isnil(i)) { - u = NULL; + o = NULL; } else { L.error("Not an object (not even a table)."); } - return u ? u->ptr : NULL; + return o; } void Balau::LuaObjectFactory::pushIt(Lua & L, const char * s, lua_CFunction f) { @@ -851,12 +858,6 @@ void Balau::LuaObjectFactory::pushMeta(Lua & L, const char * s, lua_CFunction f) void Balau::LuaObjectFactory::pushDestruct(Lua & L) { AAssert(!m_pushed, "Error: can't push destructor, object already pushed"); - push(L); - L.push("__obj"); - L.gettable(-2, true); - pushMeta(L, "__gc", LuaStatics::collector); - L.pop(); - pushIt(L, "destroy", LuaStatics::destructor); - m_wantsDestruct = true; + push(L); } diff --git a/tests/test-Lua.cc b/tests/test-Lua.cc index 8ba3785..39ccbee 100644 --- a/tests/test-Lua.cc +++ b/tests/test-Lua.cc @@ -3,6 +3,107 @@ using namespace Balau; +static int objGotDestroyed = 0; + +static int callCount = 0; + +class ObjectTest { + public: + ObjectTest() { Printer::log(M_DEBUG, "ObjectTest at %p created.", this); } + ~ObjectTest() { Printer::log(M_DEBUG, "ObjectTest at %p destroyed.", this); objGotDestroyed++; } + void someMethod1() { Printer::log(M_DEBUG, "ObjectTest::someMethod1() called on %p.", this); callCount++; } + int someMethod2(int p) { Printer::log(M_DEBUG, "ObjectTest::someMethod2() called on %p.", this); callCount++; return p * 2; } + static void someFunction() { Printer::log(M_DEBUG, "ObjectTest::someFunction() called."); callCount++; } +}; + +enum ObjectTest_methods_t { + OBJECTTEST_SOMEMETHOD1, + OBJECTTEST_SOMEMETHOD2, +}; + +enum ObjectTest_functions_t { + OBJECTTEST_CREATEOBJECTTEST, + OBJECTTEST_SOMEFUNCTION, +}; + +struct lua_functypes_t ObjectTest_methods[] = { + { OBJECTTEST_SOMEMETHOD1, "someMethod1", 0, 0, { } }, + { OBJECTTEST_SOMEMETHOD2, "someMethod2", 1, 1, { BLUA_NUMBER } }, + { -1, 0, 0, 0, 0 }, +}; + +struct lua_functypes_t ObjectTest_functions[] = { + { OBJECTTEST_CREATEOBJECTTEST, "createObjectTest", 0, 0, { } }, + { OBJECTTEST_SOMEFUNCTION, "ObjectTestFunction", 0, 0, { } }, + { -1, 0, 0, 0, 0 }, +}; + +class sLua_ObjectTest { + public: + DECLARE_METHOD(ObjectTest, OBJECTTEST_SOMEMETHOD1); + DECLARE_METHOD(ObjectTest, OBJECTTEST_SOMEMETHOD2); + + DECLARE_FUNCTION(ObjectTest, OBJECTTEST_CREATEOBJECTTEST); + DECLARE_FUNCTION(ObjectTest, OBJECTTEST_SOMEFUNCTION); + private: + static int ObjectTest_proceed(Lua & L, int n, ObjectTest * obj, int caller); + static int ObjectTest_proceed_statics(Lua & L, int n, int caller); +}; + +class LuaObjectTestFactory : public LuaObjectFactory { + public: + LuaObjectTestFactory(ObjectTest * obj) : m_obj(obj) { } + static void pushStatics(Lua & L) { + CHECK_METHODS(ObjectTest); + CHECK_FUNCTIONS(ObjectTest); + + PUSH_FUNCTION(ObjectTest, OBJECTTEST_CREATEOBJECTTEST); + PUSH_FUNCTION(ObjectTest, OBJECTTEST_SOMEFUNCTION); + } + private: + void pushObjectAndMembers(Lua & L) { + pushObj(L, m_obj, "ObjectTest"); + + PUSH_METHOD(ObjectTest, OBJECTTEST_SOMEMETHOD1); + PUSH_METHOD(ObjectTest, OBJECTTEST_SOMEMETHOD2); + } + ObjectTest * m_obj; +}; + +int sLua_ObjectTest::ObjectTest_proceed(Lua & L, int n, ObjectTest * obj, int caller) { + switch (caller) { + case OBJECTTEST_SOMEMETHOD1: + obj->someMethod1(); + break; + + case OBJECTTEST_SOMEMETHOD2: + L.push((lua_Number) obj->someMethod2(L.tonumber(-1))); + return 1; + break; + } + + return 0; +} + +int sLua_ObjectTest::ObjectTest_proceed_statics(Lua & L, int n, int caller) { + switch (caller) { + case OBJECTTEST_CREATEOBJECTTEST: + { + ObjectTest * ot = new ObjectTest; + LuaObjectTestFactory factory(ot); + factory.pushDestruct(L); + } + return 1; + break; + + case OBJECTTEST_SOMEFUNCTION: + ObjectTest::someFunction(); + break; + } + + return 0; +} + void MainTask::Do() { Printer::log(M_STATUS, "Test::Lua running."); @@ -18,6 +119,9 @@ void MainTask::Do() { L.open_debug(); L.open_bit(); L.open_jit(); + L.open_ffi(); + + LuaObjectTestFactory::pushStatics(L); TAssert(L.gettop() == 0); L.load("return 42"); @@ -27,5 +131,46 @@ void MainTask::Do() { L.pop(); TAssert(L.gettop() == 0); + L.push("obj"); + { + ObjectTest * ot = new ObjectTest; + LuaObjectTestFactory factory(ot); + factory.pushDestruct(L); + } + L.settable(LUA_GLOBALSINDEX); + + L.load("return type(obj)"); + TAssert(L.gettop() == 1); + String t = L.tostring(); + TAssert(t == "table"); + L.pop(); + + TAssert(callCount == 0); + + L.load("obj:someMethod1()"); + TAssert(L.gettop() == 0); + + TAssert(callCount == 1); + + L.load("return obj:someMethod2(21)"); + TAssert(L.gettop() == 1); + TAssert(L.tonumber() == 42); + L.pop(); + + TAssert(callCount == 2); + + L.load("ObjectTestFunction()"); + TAssert(L.gettop() == 0); + + TAssert(callCount == 3); + + TAssert(objGotDestroyed == 0); + L.load("obj2 = createObjectTest() obj2:destroy()"); + TAssert(objGotDestroyed == 1); + L.load("createObjectTest() collectgarbage('collect')"); + TAssert(objGotDestroyed == 2); + L.close(); + TAssert(objGotDestroyed == 3); + Printer::log(M_STATUS, "Test::Lua passed."); } -- cgit v1.2.3