/* * Baltisot * Copyright (C) 1999-2008 Nicolas "Pixel" Noble * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __BLUA_H__ #define __BLUA_H__ #include struct lua_State; extern "C" { void do_lua_lock(lua_State *); void do_lua_unlock(lua_State *); } #define lua_lock(L) do_lua_lock(L) #define lua_unlock(L) do_lua_unlock(L) #include #include #include #include #include #undef isnumber class LuaPrinter : public Base { public: virtual void puts(const char *); }; class Lua; class LuaObject : public Base { public: LuaObject() : wantdestruct(false), pushed(false) {} virtual void push(Lua *) throw (GeneralException); static void * getme(Lua *, int = 1); void pushdestruct(Lua *) throw (GeneralException); protected: virtual void pushmembers(Lua *) = 0; void pushme(Lua *, void * = 0, const String & = "", bool = true); static void pushit(Lua *, const String &, lua_CFunction); static void pushmeta(Lua *, const String &, lua_CFunction); bool wantdestruct, pushed; }; typedef int (*openlualib_t)(lua_State * L); //!Basic LUA engine. /*! This will create an LUA context, and provide mechanisms to interact with it. */ class Lua : public Base { public: Lua(); Lua(const Lua &) throw (GeneralException); virtual ~Lua(); void open_base(); void open_table(); void open_io(bool safe = true); void open_string(); void open_math(); void open_debug(); void open_dir(); int wrap_open(openlualib_t); void declarefunc(const String &, lua_CFunction, int = LUA_GLOBALSINDEX); void call(const String &, int = LUA_GLOBALSINDEX, int = 0, int = 0); void call(int = 0, int = 0) throw (GeneralException); void push(); void push(lua_Number); void push(const String &); void push(bool); void push(const char *, int size = -1); void push(void *); void push(lua_CFunction, int = 0); void pop(int = 1); int next(int = -2); void copy(int = -1); void remove(int = 1); void insert(int = 1); void replace(int = 1); void newtable(); void * newuser(size_t); void settable(int = -3, bool raw = false); void gettable(int = -2, bool raw = false); void setvar(); int gettop(); void getglobal(const String &) throw (GeneralException); void push_lua_context(); void error(const String &); int status(); int type(int = -1); bool isnil(int = -1); bool isboolean(int = -1); bool isnumber(int = -1); bool isstring(int = -1); bool istable(int = -1); bool isfunction(int = -1); bool iscfunction(int = -1); bool isuserdata(int = -1); bool islightuserdata(int = -1); bool isobject(int = -1); bool toboolean(int = -1); lua_Number tonumber(int = -1); String tostring(int = -1); lua_CFunction tocfunction(int = -1); void * touserdata(int = -1); Lua * tothread(int = -1); String escape_string(const String &); void load(Handle *, bool docall = true) throw (GeneralException); void load(const String &, bool docall = true) throw (GeneralException); void dump(Handle *, bool strip = true, int listing = 0); void dumpvars(Handle *, const String &, int = -1); Lua * thread(bool saveit = true); Lua * thread(const String &, int nargs = 0, bool saveit = true); Lua * thread(Handle *, int nargs = 0, bool saveit = true); void weaken(); int yield(int nresults = 0); bool resume(int nargs = 0) throw (GeneralException); bool resume(const String &, int nargs = 0); bool resume(Handle *, int nargs = 0); static Lua * find(lua_State *) throw (GeneralException); void showstack(int level = M_INFO); void showerror(); int getmetatable(int = -1); int setmetatable(int = -2); int sethook(lua_Hook func, int mask, int count); void do_break(); virtual void lock(); virtual void unlock(); bool is_protected(); void openlib(const String & libname, const struct luaL_reg *l, int nup); void setgcthreshold(int = 0); int getgcthreshold(); int getgccount(); Lua * Father(); void SetPrinter(LuaPrinter *); void puts(const char * msg); template T * recast(int n = 1) { Base * b; T * r; b = (Base *) LuaObject::getme(this, n); if (!b) { error("Base object required; got null."); } r = dynamic_cast(b); if (!r) { error(String("Object not compatible; expecting ") + typeid(r).name() + " but got *" + typeid(*b).name() + " instead."); } return r; } protected: virtual Lua * spawn_from_thread(lua_State *); Lua(lua_State *); private: void setup_state(lua_State *); lua_State * L; static std::map lualist; bool _protected, _is_thread; void dumpvars_r(Handle *, int, int = 0) throw (GeneralException); Lua * father; LuaPrinter * lprinter; friend class LuaStatics; }; class LuaException : public GeneralException { public: LuaException(String); protected: LuaException(); }; enum Lua_types_t { BLUA_OBJECT = 0x01, BLUA_TABLE = 0x02, BLUA_BOOLEAN = 0x04, BLUA_NUMBER = 0x08, BLUA_STRING = 0x10, BLUA_FUNCTION = 0x20, BLUA_NIL = 0x40, BLUA_USERDATA = 0x80, BLUA_ANY = 0xff, }; #define MAX_TYPE 8 #define MAXARGS 32 struct lua_functypes_t { int number; const char * name; int minargs, maxargs; int argtypes[MAXARGS]; }; #define DECLARE_METHOD(classname, enumvar) static int method_##enumvar(lua_State * L) { \ return LuaHelpers::method_multiplex( \ enumvar, \ L, \ sLua_##classname::classname##_proceed, \ 0, \ classname##_methods, \ true); \ } #define DECLARE_FUNCTION(classname, enumvar) static int function_##enumvar(lua_State * L) { \ return LuaHelpers::method_multiplex( \ enumvar, \ L, \ 0, \ sLua_##classname::classname##_proceed_statics, \ classname##_functions, \ false); \ } #define PUSH_METHOD(classname, enumvar) pushit( \ L, \ classname##_methods[enumvar].name, \ sLua_##classname::method_##enumvar) #define PUSH_METAMETHOD(classname, enumvar) pushmeta( \ L, \ String("__") + classname##_methods[enumvar].name, \ sLua_##classname::method_##enumvar) #define PUSH_FUNCTION(classname, enumvar) L->declarefunc( \ classname##_functions[enumvar].name, \ sLua_##classname::function_##enumvar) #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!"); \ } \ i++; \ } \ } #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!"); \ } \ i++; \ } \ } template class LuaHelpers : public Base { public: static int method_multiplex(int caller, lua_State * __L, int (*proceed)(Lua * L, int n, T * obj, int caller), int (*proceed_static)(Lua * L, int n, int caller), lua_functypes_t * tab, bool method) { Lua * L = Lua::find(__L); int add = method ? 1 : 0; int n = L->gettop() - add; T * obj = 0; int i, j, mask; bool invalid = false, arg_valid; if (method) obj = (T *) LuaObject::getme(L); if ((n < tab[caller].minargs) || (n > tab[caller].maxargs)) { invalid = true; } else { for (i = 0; i < tab[caller].maxargs && !invalid; i++) { if (n >= (i + 1)) { arg_valid = false; for (j = 0; j < MAX_TYPE && !arg_valid; j++) { mask = 1 << j; if (tab[caller].argtypes[i] & mask) { switch(mask) { case BLUA_OBJECT: if (L->istable(i + 1 + add)) { L->push("__obj"); L->gettable(i + 1 + add); arg_valid = L->isuserdata(); L->pop(); } else { arg_valid = L->isnil(i + 1 + add); } break; case BLUA_TABLE: arg_valid = L->istable(i + 1 + add); break; case BLUA_BOOLEAN: arg_valid = L->isboolean(i + 1 + add); break; case BLUA_NUMBER: arg_valid = L->isnumber(i + 1 + add); break; case BLUA_STRING: arg_valid = L->isstring(i + 1 + add); break; case BLUA_FUNCTION: arg_valid = L->isfunction(i + 1 + add); break; case BLUA_NIL: arg_valid = L->isnil(i + 1 + add); break; case BLUA_USERDATA: arg_valid = L->isuserdata(i + 1 + add) || L->islightuserdata(i + 1 + add); break; } } } invalid = !arg_valid; } } } if (invalid) { if (method) { L->error(String("Invalid arguments to method `") + typeid(T).name() + "::" + tab[caller].name + "'"); } else { L->error(String("Invalid arguments to function `") + typeid(T).name() + " " + tab[caller].name + "'"); } } if (method) { return proceed(L, n, obj, caller); } else { return proceed_static(L, n, caller); } } }; template T * lua_recast(Lua * L, int n = 1) { return L->recast(n); } /*******************************\ |** Let's have a sample of use **| \*******************************/ #ifdef THIS_IS_A_SAMPLE_WHICH_DOES_NOT_COMPILE Luacdfile::Luacdfile(cdfile * h) : LuaHandle(h) { } enum cdfile_methods_t { CDFILE_XXX = 0, CDFILE_YYY }; enum cdfile_functions_t { CDFILE_NEWCDFILE = 0, }; struct lua_functypes_t cdutils_methods[] = { { CDFILE_XXX, "xxx", 1, 1, {LUA_OBJECT} }, { CDFILE_YYY, "yyy", 0, 2, {LUA_NUMBER, LUA_NUMBER} }, { -1, 0, 0, 0, 0 } }; struct lua_functypes_t cdfile_functions[] = { { CDFILE_NEWCDFILE, "cdfile", 1, 4, {LUA_OBJECT, LUA_ANY, LUA_NUMBER, LUA_NUMBER} }, { -1, 0, 0, 0, 0 } }; class sLua_cdfile : public Base { public: static int newcdfile(lua_State * L); DECLARE_METHOD(cdfile, CDFILE_XXX); DECLARE_METHOD(cdfile, CDFILE_YYY); DECLARE_FUNCTION(cdfile, CDFILE_NEWCDFILE); private: static int cdfile_proceed(Lua * L, int n, cdfile * obj, int caller); static int cdfile_proceed_statics(Lua * L, int n, int caller); }; void Luacdfile::pushmembers(Lua * L) { { LuaHandle::pushmembers(L); or pushme(L, SomeObject); } PUSH_METHOD(cdfile, CDFILE_XXX); PUSH_METHOD(cdfile, CDFILE_YYY); } void Luacdfile::pushstatics(Lua * L) { CHECK_METHODS(cdfile); CHECK_FUNCTIONS(cdfile); PUSH_FUNCTION(cdfile, CDFILE_NEWCDFILE); } int sLua_cdfile::cdfile_proceed(Lua * L, int n, cdfile * cdfile, int caller) { int r = 0; SomeObj * obj; int arg1 = DEFAULT1, arg2 = DEFAULT2; switch(caller) { case CDFILE_XXX: obj = (SomeObj *) LuaObject::getme(L, 2); cdfile->xxx(obj); break; case CDFILE_YYY: if (n >= 1) arg1 = L->tonumber(2); if (n >= 2) arg2 = L->tonumber(3); L->push((lua_Number) cdfile->yyy(arg1, arg2)); r = 1; break; } return r; } int sLua_cdfile::cdfile_proceed_statics(Lua * L, int n, int caller) { int r = 0; switch(caller) { case CDFILE_NEWCDFILE: /****TODO****/ break; } return r; } #endif #endif