//------------------------------------------------------------------------------ /* https://github.com/vinniefalco/LuaBridge Copyright 2016, Robin Gareus Copyright 2012, Vinnie Falco Copyright 2007, Nathan Reed License: The MIT License (http://www.opensource.org/licenses/mit-license.php) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ //============================================================================== #ifdef LUABINDINGDOC #include #include #include #include #include #include #include #include template std::string type_name() { typedef typename std::remove_reference::type TR; std::unique_ptr own ( abi::__cxa_demangle(typeid(TR).name(), nullptr, nullptr, nullptr), std::free ); std::string r = own != nullptr ? own.get() : typeid(TR).name(); if (std::is_const::value) r += " const"; if (std::is_volatile::value) r += " volatile"; if (std::is_lvalue_reference::value) r += "&"; else if (std::is_rvalue_reference::value) r += "&&"; return r; } //#define LUADOCOUT #ifdef LUADOCOUT // lua #define KEYSTA "[\"" #define KEYEND "\"] = " #else // JSON #define KEYSTA "\"" #define KEYEND "\" : " #endif #define CLASSDOC(TYPE, LUANAME, DECL, PARENTDECL) \ if (LuaBindingDoc::printBindings ()) { \ std::cout << "{ " << KEYSTA << "type" << KEYEND << " \"" << TYPE << "\",\n"; \ std::cout << " " << KEYSTA << "lua" << KEYEND << " \"" << LUANAME << "\",\n"; \ std::cout << " " << KEYSTA << "decl" << KEYEND << " \"" << DECL << "\",\n"; \ std::cout << " " << KEYSTA << "parent" << KEYEND << "\"" << PARENTDECL << "\"\n"; \ std::cout << "},\n"; \ } #define PRINTDOC(TYPE, LUANAME, RETVAL, DECL) \ if (LuaBindingDoc::printBindings ()) { \ std::cout << "{ " << KEYSTA << "type" << KEYEND << " \"" << TYPE << "\",\n"; \ std::cout << " " << KEYSTA << "lua" << KEYEND << " \"" << LUANAME << "\",\n"; \ if (!(RETVAL).empty()) { \ std::cout << " " << KEYSTA << "ret" << KEYEND << " \"" << (RETVAL) << "\",\n"; \ } \ std::cout << " " << KEYSTA << "decl" << KEYEND << " \"" << DECL << "\"\n"; \ std::cout << "},\n"; \ } #define FUNDOC(TYPE, NAME, FUNCTOR) \ PRINTDOC(TYPE, _name << NAME, \ type_name< typename FuncTraits ::ReturnType >(), \ type_name< typename FuncTraits ::DeclType >()) #define DATADOC(TYPE, NAME, FUNCTOR) \ PRINTDOC(TYPE, _name << NAME, \ std::string(), \ type_name< decltype(FUNCTOR) >())\ #else #define CLASSDOC(TYPE, LUANAME, DECL, PARENTDECL) #define PRINTDOC(TYPE, LUANAME, RETVAL, DECL) #define FUNDOC(TYPE, NAME, FUNCTOR) #define DATADOC(TYPE, NAME, FUNCTOR) #endif /** Provides C++ to Lua registration capabilities. This class is not instantiated directly, call `getGlobalNamespace` to start the registration process. */ class Namespace { private: Namespace& operator= (Namespace const& other); lua_State* const L; int mutable m_stackSize; private: //============================================================================ /** Error reporting. VF: This function looks handy, why aren't we using it? */ #if 0 static int luaError (lua_State* L, std::string message) { assert (lua_isstring (L, lua_upvalueindex (1))); std::string s; // Get information on the caller's caller to format the message, // so the error appears to originate from the Lua source. lua_Debug ar; int result = lua_getstack (L, 2, &ar); if (result != 0) { lua_getinfo (L, "Sl", &ar); s = ar.short_src; if (ar.currentline != -1) { // poor mans int to string to avoid . lua_pushnumber (L, ar.currentline); s = s + ":" + lua_tostring (L, -1) + ": "; lua_pop (L, 1); } } s = s + message; return luaL_error (L, s.c_str ()); } #endif //---------------------------------------------------------------------------- /** Pop the Lua stack. */ void pop (int n) const { if (m_stackSize >= n && lua_gettop (L) >= n) { lua_pop (L, n); m_stackSize -= n; } else { throw std::logic_error ("invalid stack"); } } private: /** Factored base to reduce template instantiations. */ class ClassBase { private: ClassBase& operator= (ClassBase const& other); protected: friend class Namespace; lua_State* const L; int mutable m_stackSize; #ifdef LUABINDINGDOC std::string _name; const Namespace* _parent; #endif protected: //-------------------------------------------------------------------------- /** __index metamethod for a class. This implements member functions, data members, and property members. Functions are stored in the metatable and const metatable. Data members and property members are in the __propget table. If the key is not found, the search proceeds up the hierarchy of base classes. */ static int indexMetaMethod (lua_State* L) { int result = 0; assert (lua_isuserdata (L, 1)); // warn on security bypass lua_getmetatable (L, 1); // get metatable for object for (;;) { lua_pushvalue (L, 2); // push key arg2 lua_rawget (L, -2); // lookup key in metatable if (lua_iscfunction (L, -1)) // ensure its a cfunction { lua_remove (L, -2); // remove metatable result = 1; break; } else if (lua_isnil (L, -1)) { lua_pop (L, 1); } else { lua_pop (L, 2); throw std::logic_error ("not a cfunction"); } rawgetfield (L, -1, "__propget"); // get __propget table if (lua_istable (L, -1)) // ensure it is a table { lua_pushvalue (L, 2); // push key arg2 lua_rawget (L, -2); // lookup key in __propget lua_remove (L, -2); // remove __propget if (lua_iscfunction (L, -1)) // ensure its a cfunction { lua_remove (L, -2); // remove metatable lua_pushvalue (L, 1); // push class arg1 lua_call (L, 1, 1); result = 1; break; } else if (lua_isnil (L, -1)) { lua_pop (L, 1); } else { lua_pop (L, 2); // We only put cfunctions into __propget. throw std::logic_error ("not a cfunction"); } } else { lua_pop (L, 2); // __propget is missing, or not a table. throw std::logic_error ("missing __propget table"); } // Repeat the lookup in the __parent metafield, // or return nil if the field doesn't exist. rawgetfield (L, -1, "__parent"); if (lua_istable (L, -1)) { // Remove metatable and repeat the search in __parent. lua_remove (L, -2); } else if (lua_isnil (L, -1)) { result = 1; break; } else { lua_pop (L, 2); throw std::logic_error ("__parent is not a table"); } } return result; } //-------------------------------------------------------------------------- /** __newindex metamethod for classes. This supports writable variables and properties on class objects. The corresponding object is passed in the first parameter to the set function. */ static int newindexMetaMethod (lua_State* L) { int result = 0; lua_getmetatable (L, 1); for (;;) { // Check __propset rawgetfield (L, -1, "__propset"); if (!lua_isnil (L, -1)) { lua_pushvalue (L, 2); lua_rawget (L, -2); if (!lua_isnil (L, -1)) { // found it, call the setFunction. assert (lua_isfunction (L, -1)); lua_pushvalue (L, 1); lua_pushvalue (L, 3); lua_call (L, 2, 0); result = 0; break; } lua_pop (L, 1); } lua_pop (L, 1); // Repeat the lookup in the __parent metafield. rawgetfield (L, -1, "__parent"); if (lua_isnil (L, -1)) { // Either the property or __parent must exist. result = luaL_error (L, "no member named '%s'", lua_tostring (L, 2)); } lua_remove (L, -2); } return result; } //-------------------------------------------------------------------------- /** Create the const table. */ void createConstTable (char const* name) { lua_newtable (L); lua_pushvalue (L, -1); lua_setmetatable (L, -2); lua_pushboolean (L, 1); lua_rawsetp (L, -2, getIdentityKey ()); lua_pushstring (L, (std::string ("const ") + name).c_str ()); rawsetfield (L, -2, "__type"); lua_pushcfunction (L, &indexMetaMethod); rawsetfield (L, -2, "__index"); lua_pushcfunction (L, &newindexMetaMethod); rawsetfield (L, -2, "__newindex"); lua_newtable (L); rawsetfield (L, -2, "__propget"); if (Security::hideMetatables ()) { lua_pushnil (L); rawsetfield (L, -2, "__metatable"); } } //-------------------------------------------------------------------------- /** Create the class table. The Lua stack should have the const table on top. */ void createClassTable (char const* name) { lua_newtable (L); lua_pushvalue (L, -1); lua_setmetatable (L, -2); lua_pushboolean (L, 1); lua_rawsetp (L, -2, getIdentityKey ()); lua_pushstring (L, name); rawsetfield (L, -2, "__type"); lua_pushcfunction (L, &indexMetaMethod); rawsetfield (L, -2, "__index"); lua_pushcfunction (L, &newindexMetaMethod); rawsetfield (L, -2, "__newindex"); lua_newtable (L); rawsetfield (L, -2, "__propget"); lua_newtable (L); rawsetfield (L, -2, "__propset"); lua_pushvalue (L, -2); rawsetfield (L, -2, "__const"); // point to const table lua_pushvalue (L, -1); rawsetfield (L, -3, "__class"); // point const table to class table if (Security::hideMetatables ()) { lua_pushnil (L); rawsetfield (L, -2, "__metatable"); } } //-------------------------------------------------------------------------- /** Create the static table. The Lua stack should have: -1 class table -2 const table -3 enclosing namespace */ void createStaticTable (char const* name) { lua_newtable (L); lua_newtable (L); lua_pushvalue (L, -1); lua_setmetatable (L, -3); lua_insert (L, -2); rawsetfield (L, -5, name); #if 0 lua_pushlightuserdata (L, this); lua_pushcclosure (L, &tostringMetaMethod, 1); rawsetfield (L, -2, "__tostring"); #endif lua_pushcfunction (L, &CFunc::indexMetaMethod); rawsetfield (L, -2, "__index"); lua_pushcfunction (L, &CFunc::newindexMetaMethod); rawsetfield (L, -2, "__newindex"); lua_newtable (L); rawsetfield (L, -2, "__propget"); lua_newtable (L); rawsetfield (L, -2, "__propset"); lua_pushvalue (L, -2); rawsetfield (L, -2, "__class"); // point to class table if (Security::hideMetatables ()) { lua_pushnil (L); rawsetfield (L, -2, "__metatable"); } } //========================================================================== /** lua_CFunction to construct a class object wrapped in a container. */ template static int ctorContainerProxy (lua_State* L) { typedef typename ContainerTraits ::Type T; ArgList args (L); T* const p = Constructor ::call (args); UserdataSharedHelper ::push (L, p); return 1; } //-------------------------------------------------------------------------- /** lua_CFunction to construct a class object in-place in the userdata. */ template static int ctorPlacementProxy (lua_State* L) { ArgList args (L); Constructor ::call (UserdataValue ::place (L), args); return 1; } //-------------------------------------------------------------------------- /** Pop the Lua stack. */ void pop (int n) const { if (m_stackSize >= n && lua_gettop (L) >= n) { lua_pop (L, n); m_stackSize -= n; } else { throw std::logic_error ("invalid stack"); } } public: //-------------------------------------------------------------------------- explicit ClassBase (lua_State* L_) : L (L_) , m_stackSize (0) { } //-------------------------------------------------------------------------- /** Copy Constructor. */ ClassBase (ClassBase const& other) : L (other.L) , m_stackSize (0) #ifdef LUABINDINGDOC , _name (other._name) , _parent (other._parent) #endif { m_stackSize = other.m_stackSize; other.m_stackSize = 0; } ~ClassBase () { pop (m_stackSize); } }; //============================================================================ // // Class // //============================================================================ /** Provides a class registration in a lua_State. After contstruction the Lua stack holds these objects: -1 static table -2 class table -3 const table -4 (enclosing namespace) */ template class Class : virtual public ClassBase { public: //========================================================================== /** Register a new class or add to an existing class registration. */ Class (char const* name, Namespace const* parent) : ClassBase (parent->L) { #ifdef LUABINDINGDOC _parent = parent; _name = parent->_name + name + ":"; #endif PRINTDOC ("[C] Class", parent->_name << name, std::string(), type_name ()) m_stackSize = parent->m_stackSize + 3; parent->m_stackSize = 0; assert (lua_istable (L, -1)); rawgetfield (L, -1, name); if (lua_isnil (L, -1)) { lua_pop (L, 1); createConstTable (name); lua_pushcfunction (L, &CFunc::gcMetaMethod ); rawsetfield (L, -2, "__gc"); createClassTable (name); lua_pushcfunction (L, &CFunc::gcMetaMethod ); rawsetfield (L, -2, "__gc"); createStaticTable (name); // Map T back to its tables. lua_pushvalue (L, -1); lua_rawsetp (L, LUA_REGISTRYINDEX, ClassInfo ::getStaticKey ()); lua_pushvalue (L, -2); lua_rawsetp (L, LUA_REGISTRYINDEX, ClassInfo ::getClassKey ()); lua_pushvalue (L, -3); lua_rawsetp (L, LUA_REGISTRYINDEX, ClassInfo ::getConstKey ()); } else { lua_pop (L, 1); lua_rawgetp (L, LUA_REGISTRYINDEX, ClassInfo ::getStaticKey ()); rawgetfield (L, -1, "__class"); rawgetfield (L, -1, "__const"); // Reverse the top 3 stack elements lua_insert (L, -3); lua_insert (L, -2); } } //========================================================================== /** Derive a new class. */ Class (char const* name, Namespace const* parent, void const* const staticKey) : ClassBase (parent->L) { #ifdef LUABINDINGDOC _parent = parent; _name = parent->_name + name + ":"; #endif m_stackSize = parent->m_stackSize + 3; parent->m_stackSize = 0; assert (lua_istable (L, -1)); createConstTable (name); lua_pushcfunction (L, &CFunc::gcMetaMethod ); rawsetfield (L, -2, "__gc"); createClassTable (name); lua_pushcfunction (L, &CFunc::gcMetaMethod ); rawsetfield (L, -2, "__gc"); createStaticTable (name); lua_rawgetp (L, LUA_REGISTRYINDEX, staticKey); assert (lua_istable (L, -1)); rawgetfield (L, -1, "__class"); assert (lua_istable (L, -1)); rawgetfield (L, -1, "__const"); assert (lua_istable (L, -1)); rawsetfield (L, -6, "__parent"); rawsetfield (L, -4, "__parent"); rawsetfield (L, -2, "__parent"); lua_pushvalue (L, -1); lua_rawsetp (L, LUA_REGISTRYINDEX, ClassInfo ::getStaticKey ()); lua_pushvalue (L, -2); lua_rawsetp (L, LUA_REGISTRYINDEX, ClassInfo ::getClassKey ()); lua_pushvalue (L, -3); lua_rawsetp (L, LUA_REGISTRYINDEX, ClassInfo ::getConstKey ()); } //-------------------------------------------------------------------------- /** Continue registration in the enclosing namespace. */ Namespace endClass () { return Namespace (this); } //-------------------------------------------------------------------------- /** Add or replace a static data member. */ template Class & addStaticData (char const* name, U* pu, bool isWritable = true) { DATADOC ("Static Data Member", name, pu) assert (lua_istable (L, -1)); rawgetfield (L, -1, "__propget"); assert (lua_istable (L, -1)); lua_pushlightuserdata (L, pu); lua_pushcclosure (L, &CFunc::getVariable , 1); rawsetfield (L, -2, name); lua_pop (L, 1); rawgetfield (L, -1, "__propset"); assert (lua_istable (L, -1)); if (isWritable) { lua_pushlightuserdata (L, pu); lua_pushcclosure (L, &CFunc::setVariable , 1); } else { lua_pushstring (L, name); lua_pushcclosure (L, &CFunc::readOnlyError, 1); } rawsetfield (L, -2, name); lua_pop (L, 1); return *this; } //-------------------------------------------------------------------------- #if 0 // unused /** Add or replace a static property member. If the set function is null, the property is read-only. */ template Class & addStaticProperty (char const* name, U (*get)(), void (*set)(U) = 0) { typedef U (*get_t)(); typedef void (*set_t)(U); assert (lua_istable (L, -1)); rawgetfield (L, -1, "__propget"); assert (lua_istable (L, -1)); new (lua_newuserdata (L, sizeof (get))) get_t (get); lua_pushcclosure (L, &CFunc::Call ::f, 1); rawsetfield (L, -2, name); lua_pop (L, 1); rawgetfield (L, -1, "__propset"); assert (lua_istable (L, -1)); if (set != 0) { new (lua_newuserdata (L, sizeof (set))) set_t (set); lua_pushcclosure (L, &CFunc::Call ::f, 1); } else { lua_pushstring (L, name); lua_pushcclosure (L, &CFunc::readOnlyError, 1); } rawsetfield (L, -2, name); lua_pop (L, 1); return *this; } #endif //-------------------------------------------------------------------------- /** Add or replace a static member function. */ template Class & addStaticFunction (char const* name, FP const fp) { FUNDOC ("Static Member Function", name, FP) new (lua_newuserdata (L, sizeof (fp))) FP (fp); lua_pushcclosure (L, &CFunc::Call ::f, 1); rawsetfield (L, -2, name); return *this; } //-------------------------------------------------------------------------- /** Add or replace a lua_CFunction. */ Class & addStaticCFunction (char const* name, int (*const fp)(lua_State*)) { DATADOC ("Static C Function", name, fp) lua_pushcfunction (L, fp); rawsetfield (L, -2, name); return *this; } //-------------------------------------------------------------------------- /** Add or replace a data member. */ template Class & addData (char const* name, const U T::* mp, bool isWritable = true) { DATADOC ("Data Member", name, mp) typedef const U T::*mp_t; // Add to __propget in class and const tables. { rawgetfield (L, -2, "__propget"); rawgetfield (L, -4, "__propget"); new (lua_newuserdata (L, sizeof (mp_t))) mp_t (mp); lua_pushcclosure (L, &CFunc::getProperty , 1); lua_pushvalue (L, -1); rawsetfield (L, -4, name); rawsetfield (L, -2, name); lua_pop (L, 2); } if (isWritable) { // Add to __propset in class table. rawgetfield (L, -2, "__propset"); assert (lua_istable (L, -1)); new (lua_newuserdata (L, sizeof (mp_t))) mp_t (mp); lua_pushcclosure (L, &CFunc::setProperty , 1); rawsetfield (L, -2, name); lua_pop (L, 1); } return *this; } #if 0 // unused //-------------------------------------------------------------------------- /** Add or replace a property member. */ template Class & addProperty (char const* name, TG (T::* get) () const, void (T::* set) (TS)) { // Add to __propget in class and const tables. { rawgetfield (L, -2, "__propget"); rawgetfield (L, -4, "__propget"); typedef TG (T::*get_t) () const; new (lua_newuserdata (L, sizeof (get_t))) get_t (get); lua_pushcclosure (L, &CFunc::CallConstMember ::f, 1); lua_pushvalue (L, -1); rawsetfield (L, -4, name); rawsetfield (L, -2, name); lua_pop (L, 2); } { // Add to __propset in class table. rawgetfield (L, -2, "__propset"); assert (lua_istable (L, -1)); typedef void (T::* set_t) (TS); new (lua_newuserdata (L, sizeof (set_t))) set_t (set); lua_pushcclosure (L, &CFunc::CallMember ::f, 1); rawsetfield (L, -2, name); lua_pop (L, 1); } return *this; } // read-only template Class & addProperty (char const* name, TG (T::* get) () const) { // Add to __propget in class and const tables. rawgetfield (L, -2, "__propget"); rawgetfield (L, -4, "__propget"); typedef TG (T::*get_t) () const; new (lua_newuserdata (L, sizeof (get_t))) get_t (get); lua_pushcclosure (L, &CFunc::CallConstMember ::f, 1); lua_pushvalue (L, -1); rawsetfield (L, -4, name); rawsetfield (L, -2, name); lua_pop (L, 2); return *this; } //-------------------------------------------------------------------------- /** Add or replace a property member, by proxy. When a class is closed for modification and does not provide (or cannot provide) the function signatures necessary to implement get or set for a property, this will allow non-member functions act as proxies. Both the get and the set functions require a T const* and T* in the first argument respectively. */ template Class & addProperty (char const* name, TG (*get) (T const*), void (*set) (T*, TS)) { // Add to __propget in class and const tables. { rawgetfield (L, -2, "__propget"); rawgetfield (L, -4, "__propget"); typedef TG (*get_t) (T const*); new (lua_newuserdata (L, sizeof (get_t))) get_t (get); lua_pushcclosure (L, &CFunc::Call ::f, 1); lua_pushvalue (L, -1); rawsetfield (L, -4, name); rawsetfield (L, -2, name); lua_pop (L, 2); } if (set != 0) { // Add to __propset in class table. rawgetfield (L, -2, "__propset"); assert (lua_istable (L, -1)); typedef void (*set_t) (T*, TS); new (lua_newuserdata (L, sizeof (set_t))) set_t (set); lua_pushcclosure (L, &CFunc::Call ::f, 1); rawsetfield (L, -2, name); lua_pop (L, 1); } return *this; } // read-only template Class & addProperty (char const* name, TG (*get) (T const*)) { // Add to __propget in class and const tables. rawgetfield (L, -2, "__propget"); rawgetfield (L, -4, "__propget"); typedef TG (*get_t) (T const*); new (lua_newuserdata (L, sizeof (get_t))) get_t (get); lua_pushcclosure (L, &CFunc::Call ::f, 1); lua_pushvalue (L, -1); rawsetfield (L, -4, name); rawsetfield (L, -2, name); lua_pop (L, 2); return *this; } #endif //-------------------------------------------------------------------------- /** Add or replace a member function. */ template Class & addFunction (char const* name, MemFn mf) { FUNDOC("Member Function", name, MemFn) CFunc::CallMemberFunctionHelper ::isConstMemberFunction>::add (L, name, mf); return *this; } template Class & addPtrFunction (char const* name, MemFn mf) { FUNDOC("Member Pointer Function", name, MemFn) CFunc::CallMemberPtrFunctionHelper ::add (L, name, mf); return *this; } template Class & addWPtrFunction (char const* name, MemFn mf) { FUNDOC("Member Weak Pointer Function", name, MemFn) CFunc::CallMemberWPtrFunctionHelper ::add (L, name, mf); return *this; } template Class & addRefFunction (char const* name, MemFn mf) { FUNDOC("Member Function RefReturn", name, MemFn) CFunc::CallMemberRefFunctionHelper ::isConstMemberFunction>::add (L, name, mf); return *this; } //-------------------------------------------------------------------------- /** Add or replace a member lua_CFunction. */ Class & addCFunction (char const* name, int (T::*mfp)(lua_State*)) { DATADOC ("C Function", name, mfp) typedef int (T::*MFP)(lua_State*); assert (lua_istable (L, -1)); new (lua_newuserdata (L, sizeof (mfp))) MFP (mfp); lua_pushcclosure (L, &CFunc::CallMemberCFunction ::f, 1); rawsetfield (L, -3, name); // class table return *this; } // custom callback - extend existing classes // with non-class member functions (e.g STL iterator) Class & addExtCFunction (char const* name, int (*const fp)(lua_State*)) { DATADOC ("Ext C Function", name, fp) assert (lua_istable (L, -1)); lua_pushcclosure (L, fp, 0); rawsetfield (L, -3, name); // class table return *this; } //-------------------------------------------------------------------------- /** Add or replace a const member lua_CFunction. */ Class & addCFunction (char const* name, int (T::*mfp)(lua_State*) const) { DATADOC ("Const C Member Function", name, mfp) typedef int (T::*MFP)(lua_State*) const; assert (lua_istable (L, -1)); new (lua_newuserdata (L, sizeof (mfp))) MFP (mfp); lua_pushcclosure (L, &CFunc::CallConstMemberCFunction ::f, 1); lua_pushvalue (L, -1); rawsetfield (L, -5, name); // const table rawsetfield (L, -3, name); // class table return *this; } /** Add or replace a static const data */ template Class & addConst (char const* name, const U val) { DATADOC ("Constant/Enum Member", name, val) assert (lua_istable (L, -1)); rawgetfield (L, -1, "__propget"); // static new (lua_newuserdata (L, sizeof (val))) U (val); lua_pushcclosure (L, &CFunc::getConst , 1); rawsetfield (L, -2, name); lua_pop (L, 1); rawgetfield (L, -1, "__propset"); // static lua_pushstring (L, name); lua_pushcclosure (L, &CFunc::readOnlyError, 1); rawsetfield (L, -2, name); lua_pop (L, 1); return *this; } //-------------------------------------------------------------------------- /** Add or replace a primary Constructor. The primary Constructor is invoked when calling the class type table like a function. The template parameter should be a function pointer type that matches the desired Constructor (since you can't take the address of a Constructor and pass it as an argument). */ template Class & addConstructor () { FUNDOC("Constructor", "", MemFn) lua_pushcclosure (L, &ctorContainerProxy ::Params, C>, 0); rawsetfield(L, -2, "__call"); return *this; } template Class & addConstructor () { FUNDOC("Constructor", "", MemFn) lua_pushcclosure (L, &ctorPlacementProxy ::Params, T>, 0); rawsetfield(L, -2, "__call"); return *this; } Class & addVoidConstructor () { return addConstructor (); } Class & addEqualCheck () { PRINTDOC("Member Function", _name << "sameinstance", std::string("bool"), std::string("void (*)(" + type_name () + ")")) assert (lua_istable (L, -1)); lua_pushcclosure (L, &CFunc::ClassEqualCheck ::f, 0); rawsetfield (L, -3, "sameinstance"); return *this; } }; /** C Array to/from table */ template class Array : virtual public ClassBase { public: Array (char const* name, Namespace const* parent) : ClassBase (parent->L) { #ifdef LUABINDINGDOC _parent = parent; _name = parent->_name + name + ":"; #endif PRINTDOC ("[C] Array", parent->_name << name, std::string(), type_name () + "*") PRINTDOC ("Ext C Function", _name << "array", std::string(""), "int (*)(lua_State*)") PRINTDOC ("Ext C Function", _name << "get_table", std::string(""), "int (*)(lua_State*)") PRINTDOC ("Ext C Function", _name << "set_table", std::string(""), "int (*)(lua_State*)") PRINTDOC("Member Function", _name << "sameinstance", std::string("bool"), std::string("void (*)(" + type_name () + "*)")) m_stackSize = parent->m_stackSize + 3; parent->m_stackSize = 0; #if 0 // don't allow to duplicates handlers for same array-type assert (lua_istable (L, -1)); lua_rawgetp (L, LUA_REGISTRYINDEX, ClassInfo ::getClassKey ()); if (lua_istable (L, -1)) { lua_pushnil (L); lua_pushnil (L); return; } lua_pop (L, 1); #endif assert (lua_istable (L, -1)); rawgetfield (L, -1, name); if (lua_isnil (L, -1)) { lua_pop (L, 1); // register array access in global namespace luaL_newmetatable (L, typeid(T).name()); lua_pushcclosure (L, CFunc::array_index, 0); lua_setfield(L, -2, "__index"); lua_pushcclosure (L, CFunc::array_newindex, 0); lua_setfield(L, -2, "__newindex"); lua_pop (L, 1); createConstTable (name); lua_pushcfunction (L, &CFunc::gcMetaMethod ); rawsetfield (L, -2, "__gc"); createClassTable (name); lua_pushcfunction (L, &CFunc::gcMetaMethod ); rawsetfield (L, -2, "__gc"); createStaticTable (name); // Map T back to its tables. lua_pushvalue (L, -1); lua_rawsetp (L, LUA_REGISTRYINDEX, ClassInfo ::getStaticKey ()); lua_pushvalue (L, -2); lua_rawsetp (L, LUA_REGISTRYINDEX, ClassInfo ::getClassKey ()); lua_pushvalue (L, -3); lua_rawsetp (L, LUA_REGISTRYINDEX, ClassInfo ::getConstKey ()); assert (lua_istable (L, -1)); lua_pushcclosure (L, &CFunc::getArray , 0); rawsetfield (L, -3, "array"); // class table lua_pushcclosure (L, &CFunc::getTable , 0); rawsetfield (L, -3, "get_table"); // class table lua_pushcclosure (L, &CFunc::setTable , 0); rawsetfield (L, -3, "set_table"); // class table lua_pushcclosure (L, &CFunc::ClassEqualCheck ::f, 0); rawsetfield (L, -3, "sameinstance"); } else { lua_pushnil (L); lua_pushnil (L); } } Namespace endArray () { return Namespace (this); } }; /** Boost Weak & Shared Pointer Class Wrapper */ template class WSPtrClass : virtual public ClassBase { public: WSPtrClass (char const* name, Namespace const* parent) : ClassBase (parent->L) , weak (name, parent) , shared (name, parent) { #ifdef LUABINDINGDOC _parent = parent; _name = parent->_name + name + ":"; #endif PRINTDOC ("[C] Weak/Shared Pointer Class", parent->_name + name, std::string(), type_name ()) m_stackSize = weak.m_stackSize; parent->m_stackSize = weak.m_stackSize = shared.m_stackSize = 0; lua_pop (L, 3); } WSPtrClass (char const* name, Namespace const* parent, void const* const sharedkey, void const* const weakkey) : ClassBase (parent->L) , weak (name, parent, weakkey) , shared (name, parent, sharedkey) { #ifdef LUABINDINGDOC _parent = parent; _name = parent->_name + name + ":"; #endif m_stackSize = weak.m_stackSize; parent->m_stackSize = weak.m_stackSize = shared.m_stackSize = 0; lua_pop (L, 3); } template WSPtrClass & addFunction (char const* name, MemFn mf) { FUNDOC ("Weak/Shared Pointer Function", name, MemFn) set_weak_class (); CFunc::CallMemberWPtrFunctionHelper ::add (L, name, mf); set_shared_class (); CFunc::CallMemberPtrFunctionHelper ::add (L, name, mf); return *this; } template WSPtrClass & addRefFunction (char const* name, MemFn mf) { FUNDOC ("Weak/Shared Pointer Function RefReturn", name, MemFn) set_weak_class (); CFunc::CallMemberRefWPtrFunctionHelper ::add (L, name, mf); set_shared_class (); CFunc::CallMemberRefPtrFunctionHelper ::add (L, name, mf); return *this; } template WSPtrClass & addConstructor () { FUNDOC ("Weak/Shared Pointer Constructor", "", MemFn) set_weak_class (); lua_pushcclosure (L, &weak. template ctorPlacementProxy ::Params, boost::weak_ptr >, 0); rawsetfield(L, -2, "__call"); set_shared_class (); lua_pushcclosure (L, &shared. template ctorPlacementProxy ::Params, boost::shared_ptr >, 0); rawsetfield(L, -2, "__call"); return *this; } WSPtrClass & addVoidConstructor () { return addConstructor (); } WSPtrClass & addExtCFunction (char const* name, int (*const fp)(lua_State*)) { DATADOC ("Weak/Shared Ext C Function", name, fp) set_weak_class (); assert (lua_istable (L, -1)); lua_pushcclosure (L, fp, 0); rawsetfield (L, -3, name); // class table set_shared_class (); assert (lua_istable (L, -1)); lua_pushcclosure (L, fp, 0); rawsetfield (L, -3, name); // class table return *this; } template WSPtrClass & addCast (char const* name) { PRINTDOC("Weak/Shared Pointer Cast", _name << name, type_name< U >(), type_name< U >() << " (" << type_name< T >() << "::*)()") // TODO weak ptr set_shared_class (); assert (lua_istable (L, -1)); lua_pushcclosure (L, &CFunc::CastMemberPtr ::f, 0); rawsetfield (L, -3, name); // class table return *this; } WSPtrClass & addNullCheck () { PRINTDOC("Weak/Shared Null Check", _name << "isnil", std::string("bool"), std::string("void (*)()")) set_weak_class (); assert (lua_istable (L, -1)); lua_pushcclosure (L, &CFunc::WPtrNullCheck ::f, 0); rawsetfield (L, -3, "isnil"); // class table set_shared_class (); assert (lua_istable (L, -1)); lua_pushcclosure (L, &CFunc::PtrNullCheck ::f, 0); rawsetfield (L, -3, "isnil"); // class table return *this; } Namespace endClass () { return Namespace (this); } private: void set_weak_class () { lua_pop (L, 3); lua_rawgetp (L, LUA_REGISTRYINDEX, ClassInfo >::getStaticKey ()); rawgetfield (L, -1, "__class"); rawgetfield (L, -1, "__const"); lua_insert (L, -3); lua_insert (L, -2); } void set_shared_class () { lua_pop (L, 3); lua_rawgetp (L, LUA_REGISTRYINDEX, ClassInfo >::getStaticKey ()); rawgetfield (L, -1, "__class"); rawgetfield (L, -1, "__const"); lua_insert (L, -3); lua_insert (L, -2); } Class > weak; Class > shared; }; private: //---------------------------------------------------------------------------- /** Open the global namespace for registrations. */ explicit Namespace (lua_State* L_) : L (L_) , m_stackSize (0) #ifdef LUABINDINGDOC , _name ("") , _parent (0) #endif { lua_getglobal (L, "_G"); ++m_stackSize; } #ifdef LUABINDINGDOC std::string _name; Namespace const * _parent; #endif //---------------------------------------------------------------------------- /** Open a namespace for registrations. The namespace is created if it doesn't already exist. The parent namespace is at the top of the Lua stack. */ Namespace (char const* name, Namespace const* parent) : L (parent->L) , m_stackSize (0) #ifdef LUABINDINGDOC , _name (parent->_name + name + ":") , _parent (parent) #endif { m_stackSize = parent->m_stackSize + 1; parent->m_stackSize = 0; assert (lua_istable (L, -1)); rawgetfield (L, -1, name); if (lua_isnil (L, -1)) { lua_pop (L, 1); lua_newtable (L); lua_pushvalue (L, -1); lua_setmetatable (L, -2); lua_pushcfunction (L, &CFunc::indexMetaMethod); rawsetfield (L, -2, "__index"); lua_pushcfunction (L, &CFunc::newindexMetaMethod); rawsetfield (L, -2, "__newindex"); lua_newtable (L); rawsetfield (L, -2, "__propget"); lua_newtable (L); rawsetfield (L, -2, "__propset"); lua_pushvalue (L, -1); rawsetfield (L, -3, name); #if 0 lua_pushcfunction (L, &tostringMetaMethod); rawsetfield (L, -2, "__tostring"); #endif } } //---------------------------------------------------------------------------- /** Creates a continued registration from a child namespace. */ explicit Namespace (Namespace const* child) : L (child->L) , m_stackSize (0) #ifdef LUABINDINGDOC , _name (child->_parent ? child->_parent->_name : "") , _parent (child->_parent ? child->_parent->_parent : NULL) #endif { m_stackSize = child->m_stackSize - 1; child->m_stackSize = 1; child->pop (1); // It is not necessary or valid to call // endNamespace() for the global namespace! // assert (m_stackSize != 0); } //---------------------------------------------------------------------------- /** Creates a continued registration from a child class. */ explicit Namespace (ClassBase const* child) : L (child->L) , m_stackSize (0) #ifdef LUABINDINGDOC , _name (child->_parent ? child->_parent->_name : "") , _parent (child->_parent ? child->_parent->_parent : NULL) #endif { m_stackSize = child->m_stackSize - 3; child->m_stackSize = 3; child->pop (3); } public: //---------------------------------------------------------------------------- /** Copy Constructor. Ownership of the stack is transferred to the new object. This happens when the compiler emits temporaries to hold these objects while chaining registrations across namespaces. */ Namespace (Namespace const& other) : L (other.L) { m_stackSize = other.m_stackSize; other.m_stackSize = 0; #ifdef LUABINDINGDOC _name = other._name; _parent = other._parent; #endif } //---------------------------------------------------------------------------- /** Closes this namespace registration. */ ~Namespace () { pop (m_stackSize); } //---------------------------------------------------------------------------- /** Open the global namespace. */ static Namespace getGlobalNamespace (lua_State* L) { return Namespace (L); } //---------------------------------------------------------------------------- /** Open a new or existing namespace for registrations. */ Namespace beginNamespace (char const* name) { return Namespace (name, this); } //---------------------------------------------------------------------------- /** Continue namespace registration in the parent. Do not use this on the global namespace. */ Namespace endNamespace () { return Namespace (this); } //---------------------------------------------------------------------------- /** Add or replace a variable. */ template Namespace& addVariable (char const* name, T* pt, bool isWritable = true) { assert (lua_istable (L, -1)); rawgetfield (L, -1, "__propget"); assert (lua_istable (L, -1)); lua_pushlightuserdata (L, pt); lua_pushcclosure (L, &CFunc::getVariable , 1); rawsetfield (L, -2, name); lua_pop (L, 1); rawgetfield (L, -1, "__propset"); assert (lua_istable (L, -1)); if (isWritable) { lua_pushlightuserdata (L, pt); lua_pushcclosure (L, &CFunc::setVariable , 1); } else { lua_pushstring (L, name); lua_pushcclosure (L, &CFunc::readOnlyError, 1); } rawsetfield (L, -2, name); lua_pop (L, 1); return *this; } template Namespace& addConst (char const* name, const U val) { DATADOC ("Constant/Enum", name, val) assert (lua_istable (L, -1)); rawgetfield (L, -1, "__propget"); new (lua_newuserdata (L, sizeof (val))) U (val); lua_pushcclosure (L, &CFunc::getConst , 1); rawsetfield (L, -2, name); lua_pop (L, 1); rawgetfield (L, -1, "__propset"); assert (lua_istable (L, -1)); lua_pushstring (L, name); lua_pushcclosure (L, &CFunc::readOnlyError, 1); rawsetfield (L, -2, name); lua_pop (L, 1); return *this; } //---------------------------------------------------------------------------- /** Add or replace a property. If the set function is omitted or null, the property is read-only. */ template Namespace& addProperty (char const* name, TG (*get) (), void (*set)(TS) = 0) { assert (lua_istable (L, -1)); rawgetfield (L, -1, "__propget"); assert (lua_istable (L, -1)); typedef TG (*get_t) (); new (lua_newuserdata (L, sizeof (get_t))) get_t (get); lua_pushcclosure (L, &CFunc::Call ::f, 1); rawsetfield (L, -2, name); lua_pop (L, 1); rawgetfield (L, -1, "__propset"); assert (lua_istable (L, -1)); if (set != 0) { typedef void (*set_t) (TS); new (lua_newuserdata (L, sizeof (set_t))) set_t (set); lua_pushcclosure (L, &CFunc::Call ::f, 1); } else { lua_pushstring (L, name); lua_pushcclosure (L, &CFunc::readOnlyError, 1); } rawsetfield (L, -2, name); lua_pop (L, 1); return *this; } //---------------------------------------------------------------------------- /** Add or replace a free function. */ template Namespace& addFunction (char const* name, FP const fp) { FUNDOC ("Free Function", name, FP) assert (lua_istable (L, -1)); new (lua_newuserdata (L, sizeof (fp))) FP (fp); lua_pushcclosure (L, &CFunc::Call ::f, 1); rawsetfield (L, -2, name); return *this; } template Namespace& addRefFunction (char const* name, FP const fp) { FUNDOC ("Free Function RefReturn", name, FP) assert (lua_istable (L, -1)); new (lua_newuserdata (L, sizeof (fp))) FP (fp); lua_pushcclosure (L, &CFunc::CallRef ::f, 1); rawsetfield (L, -2, name); return *this; } //---------------------------------------------------------------------------- /** Add or replace a array type */ template Namespace registerArray (char const* name) { return Array (name, this).endArray(); } //---------------------------------------------------------------------------- /** Add or replace a lua_CFunction. */ Namespace& addCFunction (char const* name, int (*const fp)(lua_State*)) { DATADOC ("Free C Function", name, fp) lua_pushcfunction (L, fp); rawsetfield (L, -2, name); return *this; } //---------------------------------------------------------------------------- /** Open a new or existing class for registrations. */ template Class beginClass (char const* name) { return Class (name, this); } /** weak & shared pointer class */ template WSPtrClass beginWSPtrClass (char const* name) { return WSPtrClass (name, this) .addNullCheck(); } //---------------------------------------------------------------------------- template Class > beginStdMap (char const* name) { typedef std::map LT; typedef std::pair T; return beginClass (name) .addVoidConstructor () .addFunction ("empty", <::empty) .addFunction ("size", <::size) .addFunction ("clear", (void (LT::*)())<::clear) .addFunction ("count", (void (LT::*)())<::count) .addExtCFunction ("add", &CFunc::tableToMap) .addExtCFunction ("iter", &CFunc::mapIter) .addExtCFunction ("table", &CFunc::mapToTable); } template Class > beginStdSet (char const* name) { typedef std::set LT; return beginClass (name) .addVoidConstructor () .addFunction ("clear", (void (LT::*)())<::clear) .addFunction ("empty", <::empty) .addFunction ("size", <::size) .addExtCFunction ("add", &CFunc::tableToSet) .addExtCFunction ("iter", &CFunc::setIter) .addExtCFunction ("table", &CFunc::setToTable); } template Class > beginStdBitSet (char const* name) { typedef std::bitset BS; return beginClass (name) .addVoidConstructor () .addFunction ("reset", (BS& (BS::*)())&BS::reset) .addFunction ("set", (BS& (BS::*)(size_t, bool))&BS::set) .addFunction ("count", &BS::count) .addFunction ("any", &BS::any) .addFunction ("none", &BS::none) .addFunction ("test", &BS::test) .addFunction ("size", &BS::size) .addExtCFunction ("add", &CFunc::tableToBitSet) .addExtCFunction ("table", &CFunc::bitSetToTable); } template Class > beginConstStdList (char const* name) { typedef std::list LT; return beginClass (name) .addVoidConstructor () .addFunction ("empty", <::empty) .addFunction ("size", <::size) .addFunction ("reverse", <::reverse) .addExtCFunction ("iter", &CFunc::listIter) .addExtCFunction ("table", &CFunc::listToTable); } template Class > beginStdList (char const* name) { typedef std::list LT; return beginConstStdList (name) .addFunction ("unique", (void (LT::*)())<::unique) .addFunction ("push_back", (void (LT::*)(const T&))<::push_back) .addExtCFunction ("add", &CFunc::tableToList); } template Class > beginStdVector (char const* name) { typedef std::vector LT; typedef typename std::vector::reference T_REF; typedef typename std::vector::size_type T_SIZE; return beginClass (name) .addVoidConstructor () .addFunction ("empty", <::empty) .addFunction ("size", <::size) .addFunction ("push_back", (void (LT::*)(const T&))<::push_back) .addFunction ("at", (T_REF (LT::*)(T_SIZE))<::at) .addExtCFunction ("add", &CFunc::tableToList) .addExtCFunction ("iter", &CFunc::listIter) .addExtCFunction ("table", &CFunc::listToTable); } //---------------------------------------------------------------------------- template Class > > beginPtrStdList (char const* name) { typedef std::list LT; return beginClass > (name) .addVoidConstructor () .addPtrFunction ("empty", <::empty) .addPtrFunction ("size", <::size) .addPtrFunction ("reverse", <::reverse) .addPtrFunction ("unique", (void (LT::*)())<::unique) .addPtrFunction ("push_back", (void (LT::*)(const T&))<::push_back) .addExtCFunction ("add", &CFunc::ptrTableToList) .addExtCFunction ("iter", &CFunc::ptrListIter) .addExtCFunction ("table", &CFunc::ptrListToTable); } template Class > > beginPtrStdVector (char const* name) { typedef std::vector LT; typedef typename std::vector::reference T_REF; typedef typename std::vector::size_type T_SIZE; return beginClass > (name) .addVoidConstructor () .addPtrFunction ("empty", <::empty) .addPtrFunction ("empty", <::empty) .addPtrFunction ("size", <::size) .addPtrFunction ("push_back", (void (LT::*)(const T&))<::push_back) .addPtrFunction ("at", (T_REF (LT::*)(T_SIZE))<::at) .addExtCFunction ("add", &CFunc::ptrTableToList) .addExtCFunction ("iter", &CFunc::ptrListIter) .addExtCFunction ("table", &CFunc::ptrListToTable); } //---------------------------------------------------------------------------- /** Derive a new class for registrations. To continue registrations for the class later, use beginClass(). Do not call deriveClass() again. */ template Class deriveClass (char const* name) { CLASSDOC ("[C] Derived Class", _name << name, type_name (), type_name ()) return Class (name, this, ClassInfo ::getStaticKey ()); } template WSPtrClass deriveWSPtrClass (char const* name) { CLASSDOC ("[C] Derived Class", _name << name, type_name >(), type_name >()) CLASSDOC ("[C] Derived Class", _name << name, type_name >(), type_name >()) CLASSDOC ("[C] Derived Pointer Class", _name << name, type_name (), type_name ()) return WSPtrClass (name, this, ClassInfo >::getStaticKey (), ClassInfo >::getStaticKey ()) .addNullCheck(); } }; //------------------------------------------------------------------------------ /** Retrieve the global namespace. It is recommended to put your namespace inside the global namespace, and then add your classes and functions to it, rather than adding many classes and functions directly to the global namespace. */ inline Namespace getGlobalNamespace (lua_State* L) { return Namespace::getGlobalNamespace (L); } #undef KEYSTA #undef KEYEND #undef CLASSDOC #undef PRINTDOC #undef FUNDOC #undef DATADOC /* vim: set et sw=2: */