From 6d33394fb6087ade24030acd77e71c7a805082d3 Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic Date: Fri, 20 Jun 2014 12:15:24 +0000 Subject: [PATCH] Added LuaBridge and exposed few classes as example (nw) --- .gitattributes | 15 + src/emu/luaengine.c | 54 +- src/emu/luaengine.h | 1 - src/lib/lua/bridge/LuaBridge.h | 142 +++ src/lib/lua/bridge/detail/CFunctions.h | 442 ++++++++ src/lib/lua/bridge/detail/ClassInfo.h | 73 ++ src/lib/lua/bridge/detail/Constructor.h | 204 ++++ src/lib/lua/bridge/detail/FuncTraits.h | 852 +++++++++++++++ src/lib/lua/bridge/detail/Iterator.h | 114 ++ src/lib/lua/bridge/detail/LuaException.h | 113 ++ src/lib/lua/bridge/detail/LuaHelpers.h | 143 +++ src/lib/lua/bridge/detail/LuaRef.h | 1215 ++++++++++++++++++++++ src/lib/lua/bridge/detail/Namespace.h | 1136 ++++++++++++++++++++ src/lib/lua/bridge/detail/Stack.h | 469 +++++++++ src/lib/lua/bridge/detail/TypeList.h | 174 ++++ src/lib/lua/bridge/detail/TypeTraits.h | 125 +++ src/lib/lua/bridge/detail/Userdata.h | 817 +++++++++++++++ src/lib/lua/bridge/detail/dump.h | 28 + 18 files changed, 6093 insertions(+), 24 deletions(-) create mode 100644 src/lib/lua/bridge/LuaBridge.h create mode 100644 src/lib/lua/bridge/detail/CFunctions.h create mode 100644 src/lib/lua/bridge/detail/ClassInfo.h create mode 100644 src/lib/lua/bridge/detail/Constructor.h create mode 100644 src/lib/lua/bridge/detail/FuncTraits.h create mode 100644 src/lib/lua/bridge/detail/Iterator.h create mode 100644 src/lib/lua/bridge/detail/LuaException.h create mode 100644 src/lib/lua/bridge/detail/LuaHelpers.h create mode 100644 src/lib/lua/bridge/detail/LuaRef.h create mode 100644 src/lib/lua/bridge/detail/Namespace.h create mode 100644 src/lib/lua/bridge/detail/Stack.h create mode 100644 src/lib/lua/bridge/detail/TypeList.h create mode 100644 src/lib/lua/bridge/detail/TypeTraits.h create mode 100644 src/lib/lua/bridge/detail/Userdata.h create mode 100644 src/lib/lua/bridge/detail/dump.h diff --git a/.gitattributes b/.gitattributes index e204c61de48..220f734f222 100644 --- a/.gitattributes +++ b/.gitattributes @@ -3956,6 +3956,21 @@ src/lib/libjpeg/jutils.c svneol=native#text/plain src/lib/libjpeg/jversion.h svneol=native#text/plain src/lib/libjpeg/libjpeg.txt svneol=native#text/plain src/lib/lua/Makefile svneol=native#text/plain +src/lib/lua/bridge/LuaBridge.h svneol=native#text/plain +src/lib/lua/bridge/detail/CFunctions.h svneol=native#text/plain +src/lib/lua/bridge/detail/ClassInfo.h svneol=native#text/plain +src/lib/lua/bridge/detail/Constructor.h svneol=native#text/plain +src/lib/lua/bridge/detail/FuncTraits.h svneol=native#text/plain +src/lib/lua/bridge/detail/Iterator.h svneol=native#text/plain +src/lib/lua/bridge/detail/LuaException.h svneol=native#text/plain +src/lib/lua/bridge/detail/LuaHelpers.h svneol=native#text/plain +src/lib/lua/bridge/detail/LuaRef.h svneol=native#text/plain +src/lib/lua/bridge/detail/Namespace.h svneol=native#text/plain +src/lib/lua/bridge/detail/Stack.h svneol=native#text/plain +src/lib/lua/bridge/detail/TypeList.h svneol=native#text/plain +src/lib/lua/bridge/detail/TypeTraits.h svneol=native#text/plain +src/lib/lua/bridge/detail/Userdata.h svneol=native#text/plain +src/lib/lua/bridge/detail/dump.h svneol=native#text/plain src/lib/lua/lapi.c svneol=native#text/plain src/lib/lua/lapi.h svneol=native#text/plain src/lib/lua/lauxlib.c svneol=native#text/plain diff --git a/src/emu/luaengine.c b/src/emu/luaengine.c index ef4e5ee131d..0d6d47553b0 100644 --- a/src/emu/luaengine.c +++ b/src/emu/luaengine.c @@ -16,6 +16,7 @@ #include "lua/lua.hpp" #include "lua/lib/lualibs.h" #include "web/mongoose.h" +#include "lua/bridge/LuaBridge.h" //************************************************************************** // LUA ENGINE @@ -341,28 +342,6 @@ int lua_engine::l_emu_start(lua_State *L) return 1; } -//------------------------------------------------- -// luaopen_emu - connect emu section lib -//------------------------------------------------- - -int lua_engine::luaopen_emu(lua_State *L) -{ - static const struct luaL_Reg emu_funcs [] = { - { "gamename", l_emu_gamename }, - { "keypost", l_emu_keypost }, - { "hook_output", l_emu_hook_output }, - { "time", l_emu_time }, - { "wait", l_emu_wait }, - { "after", l_emu_after }, - { "exit", l_emu_exit }, - { "start", l_emu_start }, - { NULL, NULL } /* sentinel */ - }; - - luaL_newlib(L, emu_funcs); - return 1; -} - int lua_engine::luaopen_ioport(lua_State *L) { static const struct luaL_Reg ioport_funcs [] = { @@ -466,7 +445,6 @@ lua_engine::lua_engine() luaL_openlibs(m_lua_state); /* open libraries */ luaopen_lsqlite3(m_lua_state); - luaL_requiref(m_lua_state, "emu", luaopen_emu, 1); luaopen_ioport(m_lua_state); @@ -515,6 +493,36 @@ void lua_engine::update_machine() void lua_engine::initialize() { + luabridge::getGlobalNamespace (m_lua_state) + .beginNamespace ("emu") + .addCFunction ("gamename", l_emu_gamename ) + .addCFunction ("keypost", l_emu_keypost ) + .addCFunction ("hook_output", l_emu_hook_output ) + .addCFunction ("time", l_emu_time ) + .addCFunction ("wait", l_emu_wait ) + .addCFunction ("after", l_emu_after ) + .addCFunction ("exit", l_emu_exit ) + .addCFunction ("start", l_emu_start ) + .beginClass ("manager") + .addFunction ("machine", &machine_manager::machine) + .addFunction ("options", &machine_manager::options) + .endClass () + .beginClass ("machine") + .addFunction ("exit", &running_machine::schedule_exit) + .addFunction ("hard_reset", &running_machine::schedule_hard_reset) + .addFunction ("soft_reset", &running_machine::schedule_soft_reset) + .addFunction ("system", &running_machine::system) + .endClass () + .beginClass ("game_driver") + .addData ("name", &game_driver::name) + .addData ("description", &game_driver::description) + .addData ("year", &game_driver::year) + .addData ("manufacturer", &game_driver::manufacturer) + .endClass () + .endNamespace (); + luabridge::push (m_lua_state, machine_manager::instance()); + lua_setglobal(m_lua_state, "manager"); + mg_start_thread(::serve_lua, this); } diff --git a/src/emu/luaengine.h b/src/emu/luaengine.h index 89421466e51..df5faa5d479 100644 --- a/src/emu/luaengine.h +++ b/src/emu/luaengine.h @@ -86,7 +86,6 @@ private: void resume(void *L, INT32 param); void report_errors(int status); void start(); - static int luaopen_emu(lua_State *L); static int luaopen_ioport(lua_State *L); void close(); diff --git a/src/lib/lua/bridge/LuaBridge.h b/src/lib/lua/bridge/LuaBridge.h new file mode 100644 index 00000000000..1928e9a1f03 --- /dev/null +++ b/src/lib/lua/bridge/LuaBridge.h @@ -0,0 +1,142 @@ +//------------------------------------------------------------------------------ +/* + https://github.com/vinniefalco/LuaBridge + + 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. +*/ +//============================================================================== + +#ifndef LUABRIDGE_LUABRIDGE_HEADER +#define LUABRIDGE_LUABRIDGE_HEADER + +// All #include dependencies are listed here +// instead of in the individual header files. +// +#include +#include +#include +#include +#include + +#define LUABRIDGE_MAJOR_VERSION 2 +#define LUABRIDGE_MINOR_VERSION 0 +#define LUABRIDGE_VERSION 200 + +namespace luabridge +{ + +// Forward declaration +// +template +struct Stack; + +#include "detail/LuaHelpers.h" + +#include "detail/TypeTraits.h" +#include "detail/TypeList.h" +#include "detail/FuncTraits.h" +#include "detail/Constructor.h" +#include "detail/Stack.h" +#include "detail/ClassInfo.h" + +class LuaRef; + +#include "detail/LuaException.h" +#include "detail/LuaRef.h" +#include "detail/Iterator.h" + +//------------------------------------------------------------------------------ +/** + security options. +*/ +class Security +{ +public: + static bool hideMetatables () + { + return getSettings().hideMetatables; + } + + static void setHideMetatables (bool shouldHide) + { + getSettings().hideMetatables = shouldHide; + } + +private: + struct Settings + { + Settings () : hideMetatables (true) + { + } + + bool hideMetatables; + }; + + static Settings& getSettings () + { + static Settings settings; + return settings; + } +}; + +#include "detail/Userdata.h" +#include "detail/CFunctions.h" +#include "detail/Namespace.h" + +//------------------------------------------------------------------------------ +/** + Push an object onto the Lua stack. +*/ +template +inline void push (lua_State* L, T t) +{ + Stack ::push (L, t); +} + +//------------------------------------------------------------------------------ +/** + Set a global value in the lua_State. + + @note This works on any type specialized by `Stack`, including `LuaRef` and + its table proxies. +*/ +template +inline void setGlobal (lua_State* L, T t, char const* name) +{ + push (L, t); + lua_setglobal (L, name); +} + +//------------------------------------------------------------------------------ +/** + Change whether or not metatables are hidden (on by default). +*/ +inline void setHideMetatables (bool shouldHide) +{ + Security::setHideMetatables (shouldHide); +} + +} + +#endif diff --git a/src/lib/lua/bridge/detail/CFunctions.h b/src/lib/lua/bridge/detail/CFunctions.h new file mode 100644 index 00000000000..0497b6bb4b9 --- /dev/null +++ b/src/lib/lua/bridge/detail/CFunctions.h @@ -0,0 +1,442 @@ +//------------------------------------------------------------------------------ +/* + https://github.com/vinniefalco/LuaBridge + + Copyright 2012, Vinnie Falco + + 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. +*/ +//============================================================================== + +// We use a structure so we can define everything in the header. +// +struct CFunc +{ + //---------------------------------------------------------------------------- + /** + __index metamethod for a namespace or class static members. + + This handles: + Retrieving functions and class static methods, stored in the metatable. + Reading global and class static data, stored in the __propget table. + Reading global and class properties, stored in the __propget table. + */ + static int indexMetaMethod (lua_State* L) + { + int result = 0; + lua_getmetatable (L, 1); // push metatable of arg1 + for (;;) + { + lua_pushvalue (L, 2); // push key arg2 + lua_rawget (L, -2); // lookup key in metatable + if (lua_isnil (L, -1)) // not found + { + lua_pop (L, 1); // discard nil + rawgetfield (L, -1, "__propget"); // lookup __propget in metatable + lua_pushvalue (L, 2); // push key arg2 + lua_rawget (L, -2); // lookup key in __propget + lua_remove (L, -2); // discard __propget + if (lua_iscfunction (L, -1)) + { + lua_remove (L, -2); // discard metatable + lua_pushvalue (L, 1); // push arg1 + lua_call (L, 1, 1); // call cfunction + result = 1; + break; + } + else + { + assert (lua_isnil (L, -1)); + lua_pop (L, 1); // discard nil and fall through + } + } + else + { + assert (lua_istable (L, -1) || lua_iscfunction (L, -1)); + lua_remove (L, -2); + result = 1; + break; + } + + rawgetfield (L, -1, "__parent"); + if (lua_istable (L, -1)) + { + // Remove metatable and repeat the search in __parent. + lua_remove (L, -2); + } + else + { + // Discard metatable and return nil. + assert (lua_isnil (L, -1)); + lua_remove (L, -2); + result = 1; + break; + } + } + + return result; + } + + //---------------------------------------------------------------------------- + /** + __newindex metamethod for a namespace or class static members. + + The __propset table stores proxy functions for assignment to: + Global and class static data. + Global and class properties. + */ + static int newindexMetaMethod (lua_State* L) + { + int result = 0; + lua_getmetatable (L, 1); // push metatable of arg1 + for (;;) + { + rawgetfield (L, -1, "__propset"); // lookup __propset in metatable + assert (lua_istable (L, -1)); + lua_pushvalue (L, 2); // push key arg2 + lua_rawget (L, -2); // lookup key in __propset + lua_remove (L, -2); // discard __propset + if (lua_iscfunction (L, -1)) // ensure value is a cfunction + { + lua_remove (L, -2); // discard metatable + lua_pushvalue (L, 3); // push new value arg3 + lua_call (L, 1, 0); // call cfunction + result = 0; + break; + } + else + { + assert (lua_isnil (L, -1)); + lua_pop (L, 1); + } + + rawgetfield (L, -1, "__parent"); + if (lua_istable (L, -1)) + { + // Remove metatable and repeat the search in __parent. + lua_remove (L, -2); + } + else + { + assert (lua_isnil (L, -1)); + lua_pop (L, 2); + result = luaL_error (L,"no writable variable '%s'", lua_tostring (L, 2)); + } + } + + return result; + } + + //---------------------------------------------------------------------------- + /** + lua_CFunction to report an error writing to a read-only value. + + The name of the variable is in the first upvalue. + */ + static int readOnlyError (lua_State* L) + { + std::string s; + + s = s + "'" + lua_tostring (L, lua_upvalueindex (1)) + "' is read-only"; + + return luaL_error (L, s.c_str ()); + } + + //---------------------------------------------------------------------------- + /** + lua_CFunction to get a variable. + + This is used for global variables or class static data members. + + The pointer to the data is in the first upvalue. + */ + template + static int getVariable (lua_State* L) + { + assert (lua_islightuserdata (L, lua_upvalueindex (1))); + T const* ptr = static_cast (lua_touserdata (L, lua_upvalueindex (1))); + assert (ptr != 0); + Stack ::push (L, *ptr); + return 1; + } + + //---------------------------------------------------------------------------- + /** + lua_CFunction to set a variable. + + This is used for global variables or class static data members. + + The pointer to the data is in the first upvalue. + */ + template + static int setVariable (lua_State* L) + { + assert (lua_islightuserdata (L, lua_upvalueindex (1))); + T* ptr = static_cast (lua_touserdata (L, lua_upvalueindex (1))); + assert (ptr != 0); + *ptr = Stack ::get (L, 1); + return 0; + } + + //---------------------------------------------------------------------------- + /** + lua_CFunction to call a function with a return value. + + This is used for global functions, global properties, class static methods, + and class static properties. + + The function pointer is in the first upvalue. + */ + template ::ReturnType> + struct Call + { + typedef typename FuncTraits ::Params Params; + static int f (lua_State* L) + { + assert (isfulluserdata (L, lua_upvalueindex (1))); + FnPtr const& fnptr = *static_cast (lua_touserdata (L, lua_upvalueindex (1))); + assert (fnptr != 0); + ArgList args (L); + Stack ::ReturnType>::push (L, FuncTraits ::call (fnptr, args)); + return 1; + } + }; + + //---------------------------------------------------------------------------- + /** + lua_CFunction to call a function with no return value. + + This is used for global functions, global properties, class static methods, + and class static properties. + + The function pointer is in the first upvalue. + */ + template + struct Call + { + typedef typename FuncTraits ::Params Params; + static int f (lua_State* L) + { + assert (isfulluserdata (L, lua_upvalueindex (1))); + FnPtr const& fnptr = *static_cast (lua_touserdata (L, lua_upvalueindex (1))); + assert (fnptr != 0); + ArgList args (L); + FuncTraits ::call (fnptr, args); + return 0; + } + }; + + //---------------------------------------------------------------------------- + /** + lua_CFunction to call a class member function with a return value. + + The member function pointer is in the first upvalue. + The class userdata object is at the top of the Lua stack. + */ + template ::ReturnType> + struct CallMember + { + typedef typename FuncTraits ::ClassType T; + typedef typename FuncTraits ::Params Params; + + static int f (lua_State* L) + { + assert (isfulluserdata (L, lua_upvalueindex (1))); + T* const t = Userdata::get (L, 1, false); + MemFnPtr const& fnptr = *static_cast (lua_touserdata (L, lua_upvalueindex (1))); + assert (fnptr != 0); + ArgList args (L); + Stack ::push (L, FuncTraits ::call (t, fnptr, args)); + return 1; + } + }; + + template ::ReturnType> + struct CallConstMember + { + typedef typename FuncTraits ::ClassType T; + typedef typename FuncTraits ::Params Params; + + static int f (lua_State* L) + { + assert (isfulluserdata (L, lua_upvalueindex (1))); + T const* const t = Userdata::get (L, 1, true); + MemFnPtr const& fnptr = *static_cast (lua_touserdata (L, lua_upvalueindex (1))); + assert (fnptr != 0); + ArgList args(L); + Stack ::push (L, FuncTraits ::call (t, fnptr, args)); + return 1; + } + }; + + //---------------------------------------------------------------------------- + /** + lua_CFunction to call a class member function with no return value. + + The member function pointer is in the first upvalue. + The class userdata object is at the top of the Lua stack. + */ + template + struct CallMember + { + typedef typename FuncTraits ::ClassType T; + typedef typename FuncTraits ::Params Params; + + static int f (lua_State* L) + { + assert (isfulluserdata (L, lua_upvalueindex (1))); + T* const t = Userdata::get (L, 1, false); + MemFnPtr const& fnptr = *static_cast (lua_touserdata (L, lua_upvalueindex (1))); + assert (fnptr != 0); + ArgList args (L); + FuncTraits ::call (t, fnptr, args); + return 0; + } + }; + + template + struct CallConstMember + { + typedef typename FuncTraits ::ClassType T; + typedef typename FuncTraits ::Params Params; + + static int f (lua_State* L) + { + assert (isfulluserdata (L, lua_upvalueindex (1))); + T const* const t = Userdata::get (L, 1, true); + MemFnPtr const& fnptr = *static_cast (lua_touserdata (L, lua_upvalueindex (1))); + assert (fnptr != 0); + ArgList args (L); + FuncTraits ::call (t, fnptr, args); + return 0; + } + }; + + //-------------------------------------------------------------------------- + /** + lua_CFunction to call a class member lua_CFunction. + + The member function pointer is in the first upvalue. + The class userdata object is at the top of the Lua stack. + */ + template + struct CallMemberCFunction + { + static int f (lua_State* L) + { + assert (isfulluserdata (L, lua_upvalueindex (1))); + typedef int (T::*MFP)(lua_State* L); + T* const t = Userdata::get (L, 1, false); + MFP const& fnptr = *static_cast (lua_touserdata (L, lua_upvalueindex (1))); + assert (fnptr != 0); + return (t->*fnptr) (L); + } + }; + + template + struct CallConstMemberCFunction + { + static int f (lua_State* L) + { + assert (isfulluserdata (L, lua_upvalueindex (1))); + typedef int (T::*MFP)(lua_State* L); + T const* const t = Userdata::get (L, 1, true); + MFP const& fnptr = *static_cast (lua_touserdata (L, lua_upvalueindex (1))); + assert (fnptr != 0); + return (t->*fnptr) (L); + } + }; + + //-------------------------------------------------------------------------- + + // SFINAE Helpers + + template + struct CallMemberFunctionHelper + { + static void add (lua_State* L, char const* name, MemFnPtr mf) + { + new (lua_newuserdata (L, sizeof (MemFnPtr))) MemFnPtr (mf); + lua_pushcclosure (L, &CallConstMember ::f, 1); + lua_pushvalue (L, -1); + rawsetfield (L, -5, name); // const table + rawsetfield (L, -3, name); // class table + } + }; + + template + struct CallMemberFunctionHelper + { + static void add (lua_State* L, char const* name, MemFnPtr mf) + { + new (lua_newuserdata (L, sizeof (MemFnPtr))) MemFnPtr (mf); + lua_pushcclosure (L, &CallMember ::f, 1); + rawsetfield (L, -3, name); // class table + } + }; + + //-------------------------------------------------------------------------- + /** + __gc metamethod for a class. + */ + template + static int gcMetaMethod (lua_State* L) + { + Userdata* const ud = Userdata::getExact (L, 1); + ud->~Userdata (); + return 0; + } + + //-------------------------------------------------------------------------- + /** + lua_CFunction to get a class data member. + + The pointer-to-member is in the first upvalue. + The class userdata object is at the top of the Lua stack. + */ + template + static int getProperty (lua_State* L) + { + C const* const c = Userdata::get (L, 1, true); + T C::** mp = static_cast (lua_touserdata (L, lua_upvalueindex (1))); + Stack ::push (L, c->**mp); + return 1; + } + + //-------------------------------------------------------------------------- + /** + lua_CFunction to set a class data member. + + The pointer-to-member is in the first upvalue. + The class userdata object is at the top of the Lua stack. + */ + template + static int setProperty (lua_State* L) + { + C* const c = Userdata::get (L, 1, false); + T C::** mp = static_cast (lua_touserdata (L, lua_upvalueindex (1))); + c->**mp = Stack ::get (L, 2); + return 0; + } +}; diff --git a/src/lib/lua/bridge/detail/ClassInfo.h b/src/lib/lua/bridge/detail/ClassInfo.h new file mode 100644 index 00000000000..8d580694777 --- /dev/null +++ b/src/lib/lua/bridge/detail/ClassInfo.h @@ -0,0 +1,73 @@ +//------------------------------------------------------------------------------ +/* + https://github.com/vinniefalco/LuaBridge + + Copyright 2012, Vinnie Falco + + 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. +*/ +//============================================================================== + +/** Unique Lua registry keys for a class. + + Each registered class inserts three keys into the registry, whose + values are the corresponding static, class, and const metatables. This + allows a quick and reliable lookup for a metatable from a template type. +*/ +template +class ClassInfo +{ +public: + /** Get the key for the static table. + + The static table holds the static data members, static properties, and + static member functions for a class. + */ + static void const* getStaticKey () + { + static char value; + return &value; + } + + /** Get the key for the class table. + + The class table holds the data members, properties, and member functions + of a class. Read-only data and properties, and const member functions are + also placed here (to save a lookup in the const table). + */ + static void const* getClassKey () + { + static char value; + return &value; + } + + /** Get the key for the const table. + + The const table holds read-only data members and properties, and const + member functions of a class. + */ + static void const* getConstKey () + { + static char value; + return &value; + } +}; + diff --git a/src/lib/lua/bridge/detail/Constructor.h b/src/lib/lua/bridge/detail/Constructor.h new file mode 100644 index 00000000000..7b2dad05986 --- /dev/null +++ b/src/lib/lua/bridge/detail/Constructor.h @@ -0,0 +1,204 @@ +//------------------------------------------------------------------------------ +/* + https://github.com/vinniefalco/LuaBridge + + 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. +*/ +//============================================================================== + +#ifndef LUABRIDGE_CONSTRUCTOR_HEADER +#define LUABRIDGE_CONSTRUCTOR_HEADER + +/* +* Constructor generators. These templates allow you to call operator new and +* pass the contents of a type/value list to the Constructor. Like the +* function pointer containers, these are only defined up to 8 parameters. +*/ + +/** Constructor generators. + + These templates call operator new with the contents of a type/value + list passed to the Constructor with up to 8 parameters. Two versions + of call() are provided. One performs a regular new, the other performs + a placement new. +*/ +template +struct Constructor {}; + +template +struct Constructor +{ + static T* call (TypeListValues const&) + { + return new T; + } + static T* call (void* mem, TypeListValues const&) + { + return new (mem) T; + } +}; + +template +struct Constructor > +{ + static T* call (const TypeListValues > &tvl) + { + return new T(tvl.hd); + } + static T* call (void* mem, const TypeListValues > &tvl) + { + return new (mem) T(tvl.hd); + } +}; + +template +struct Constructor > > +{ + static T* call (const TypeListValues > > &tvl) + { + return new T(tvl.hd, tvl.tl.hd); + } + static T* call (void* mem, const TypeListValues > > &tvl) + { + return new (mem) T(tvl.hd, tvl.tl.hd); + } +}; + +template +struct Constructor > > > +{ + static T* call (const TypeListValues > > > &tvl) + { + return new T(tvl.hd, tvl.tl.hd, tvl.tl.tl.hd); + } + static T* call (void* mem, const TypeListValues > > > &tvl) + { + return new (mem) T(tvl.hd, tvl.tl.hd, tvl.tl.tl.hd); + } +}; + +template +struct Constructor > > > > +{ + static T* call (const TypeListValues > > > > &tvl) + { + return new T(tvl.hd, tvl.tl.hd, tvl.tl.tl.hd, tvl.tl.tl.tl.hd); + } + static T* call (void* mem, const TypeListValues > > > > &tvl) + { + return new (mem) T(tvl.hd, tvl.tl.hd, tvl.tl.tl.hd, tvl.tl.tl.tl.hd); + } +}; + +template +struct Constructor > > > > > +{ + static T* call (const TypeListValues > > > > > &tvl) + { + return new T(tvl.hd, tvl.tl.hd, tvl.tl.tl.hd, tvl.tl.tl.tl.hd, + tvl.tl.tl.tl.tl.hd); + } + static T* call (void* mem, const TypeListValues > > > > > &tvl) + { + return new (mem) T(tvl.hd, tvl.tl.hd, tvl.tl.tl.hd, tvl.tl.tl.tl.hd, + tvl.tl.tl.tl.tl.hd); + } +}; + +template +struct Constructor > > > > > > +{ + static T* call (const TypeListValues > > > > > > &tvl) + { + return new T(tvl.hd, tvl.tl.hd, tvl.tl.tl.hd, tvl.tl.tl.tl.hd, + tvl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.hd); + } + static T* call (void* mem, const TypeListValues > > > > > > &tvl) + { + return new (mem) T(tvl.hd, tvl.tl.hd, tvl.tl.tl.hd, tvl.tl.tl.tl.hd, + tvl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.hd); + } +}; + +template +struct Constructor > > > > > > > +{ + static T* call (const TypeListValues > > > > > > > &tvl) + { + return new T(tvl.hd, tvl.tl.hd, tvl.tl.tl.hd, tvl.tl.tl.tl.hd, + tvl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.hd, + tvl.tl.tl.tl.tl.tl.tl.hd); + } + static T* call (void* mem, const TypeListValues > > > > > > > &tvl) + { + return new (mem) T(tvl.hd, tvl.tl.hd, tvl.tl.tl.hd, tvl.tl.tl.tl.hd, + tvl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.hd, + tvl.tl.tl.tl.tl.tl.tl.hd); + } +}; + +template +struct Constructor > > > > > > > > +{ + static T* call (const TypeListValues > > > > > > > > &tvl) + { + return new T(tvl.hd, tvl.tl.hd, tvl.tl.tl.hd, tvl.tl.tl.tl.hd, + tvl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.hd, + tvl.tl.tl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.tl.tl.hd); + } + static T* call (void* mem, const TypeListValues > > > > > > > > &tvl) + { + return new (mem) T(tvl.hd, tvl.tl.hd, tvl.tl.tl.hd, tvl.tl.tl.tl.hd, + tvl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.hd, + tvl.tl.tl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.tl.tl.hd); + } +}; + +#endif diff --git a/src/lib/lua/bridge/detail/FuncTraits.h b/src/lib/lua/bridge/detail/FuncTraits.h new file mode 100644 index 00000000000..7eccb61832b --- /dev/null +++ b/src/lib/lua/bridge/detail/FuncTraits.h @@ -0,0 +1,852 @@ +//------------------------------------------------------------------------------ +/* + https://github.com/vinniefalco/LuaBridge + + Copyright 2012, Vinnie Falco + + 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. +*/ +//============================================================================== + +/** + Since the throw specification is part of a function signature, the FuncTraits + family of templates needs to be specialized for both types. The + LUABRIDGE_THROWSPEC macro controls whether we use the 'throw ()' form, or + 'noexcept' (if C++11 is available) to distinguish the functions. +*/ +#if defined (__APPLE_CPP__) || defined(__APPLE_CC__) || defined(__clang__) || defined(__GNUC__) || \ + (defined (_MSC_VER) && (_MSC_VER >= 1700)) +// Do not define LUABRIDGE_THROWSPEC since the Xcode and gcc compilers do not +// distinguish the throw specification in the function signature. +#else +// Visual Studio 10 and earlier pay too much mind to useless throw() spec. +// +# define LUABRIDGE_THROWSPEC throw() +#endif + +//============================================================================== +/** + Traits for function pointers. + + There are three types of functions: global, non-const member, and const + member. These templates determine the type of function, which class type it + belongs to if it is a class member, the const-ness if it is a member + function, and the type information for the return value and argument list. + + Expansions are provided for functions with up to 8 parameters. This can be + manually extended, or expanded to an arbitrary amount using C++11 features. +*/ +template +struct FuncTraits +{ +}; + +/* Ordinary function pointers. */ + +template +struct FuncTraits +{ + static bool const isMemberFunction = false; + typedef D DeclType; + typedef R ReturnType; + typedef None Params; + static R call (D fp, TypeListValues ) + { + return fp (); + } +}; + +template +struct FuncTraits +{ + static bool const isMemberFunction = false; + typedef D DeclType; + typedef R ReturnType; + typedef TypeList Params; + static R call (D fp, TypeListValues tvl) + { + return fp (tvl.hd); + } +}; + +template +struct FuncTraits +{ + static bool const isMemberFunction = false; + typedef D DeclType; + typedef R ReturnType; + typedef TypeList > Params; + static R call (D fp, TypeListValues tvl) + { + return fp (tvl.hd, tvl.tl.hd); + } +}; + +template +struct FuncTraits +{ + static bool const isMemberFunction = false; + typedef D DeclType; + typedef R ReturnType; + typedef TypeList > > Params; + static R call (D fp, TypeListValues tvl) + { + return fp (tvl.hd, tvl.tl.hd, tvl.tl.tl.hd); + } +}; + +template +struct FuncTraits +{ + static bool const isMemberFunction = false; + typedef D DeclType; + typedef R ReturnType; + typedef TypeList > > > Params; + static R call (D fp, TypeListValues tvl) + { + return fp (tvl.hd, tvl.tl.hd, tvl.tl.tl.hd, tvl.tl.tl.tl.hd); + } +}; + +template +struct FuncTraits +{ + static bool const isMemberFunction = false; + typedef D DeclType; + typedef R ReturnType; + typedef TypeList > > > > Params; + static R call (D fp, TypeListValues tvl) + { + return fp (tvl.hd, tvl.tl.hd, tvl.tl.tl.hd, tvl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.hd); + } +}; + +template +struct FuncTraits +{ + static bool const isMemberFunction = false; + typedef D DeclType; + typedef R ReturnType; + typedef TypeList > > > > > Params; + static R call (D fp, TypeListValues tvl) + { + return fp (tvl.hd, tvl.tl.hd, tvl.tl.tl.hd, tvl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.hd); + } +}; + +template +struct FuncTraits +{ + static bool const isMemberFunction = false; + typedef D DeclType; + typedef R ReturnType; + typedef TypeList > > > > > > Params; + static R call (D fp, TypeListValues tvl) + { + return fp (tvl.hd, tvl.tl.hd, tvl.tl.tl.hd, tvl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.tl.hd); + } +}; + +template +struct FuncTraits +{ + static bool const isMemberFunction = false; + typedef D DeclType; + typedef R ReturnType; + typedef TypeList > > > > > > > Params; + static R call (D fp, TypeListValues tvl) + { + return fp (tvl.hd, tvl.tl.hd, tvl.tl.tl.hd, tvl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.tl.tl.hd); + } +}; + +/* Non-const member function pointers. */ + +template +struct FuncTraits +{ + static bool const isMemberFunction = true; + static bool const isConstMemberFunction = false; + typedef D DeclType; + typedef T ClassType; + typedef R ReturnType; + typedef None Params; + static R call (T* obj, D fp, TypeListValues ) + { + return (obj->*fp)(); + } +}; + +template +struct FuncTraits +{ + static bool const isMemberFunction = true; + static bool const isConstMemberFunction = false; + typedef D DeclType; + typedef T ClassType; + typedef R ReturnType; + typedef TypeList Params; + static R call (T* obj, D fp, TypeListValues tvl) + { + return (obj->*fp)(tvl.hd); + } +}; + +template +struct FuncTraits +{ + static bool const isMemberFunction = true; + static bool const isConstMemberFunction = false; + typedef D DeclType; + typedef T ClassType; + typedef R ReturnType; + typedef TypeList > Params; + static R call (T* obj, D fp, TypeListValues tvl) + { + return (obj->*fp)(tvl.hd, tvl.tl.hd); + } +}; + +template +struct FuncTraits +{ + static bool const isMemberFunction = true; + static bool const isConstMemberFunction = false; + typedef D DeclType; + typedef T ClassType; + typedef R ReturnType; + typedef TypeList > > Params; + static R call (T* obj, D fp, TypeListValues tvl) + { + return (obj->*fp)(tvl.hd, tvl.tl.hd, tvl.tl.tl.hd); + } +}; + +template +struct FuncTraits +{ + static bool const isMemberFunction = true; + static bool const isConstMemberFunction = false; + typedef D DeclType; + typedef T ClassType; + typedef R ReturnType; + typedef TypeList > > > Params; + static R call (T* obj, D fp, TypeListValues tvl) + { + return (obj->*fp)(tvl.hd, tvl.tl.hd, tvl.tl.tl.hd, tvl.tl.tl.tl.hd); + } +}; + +template +struct FuncTraits +{ + static bool const isMemberFunction = true; + static bool const isConstMemberFunction = false; + typedef D DeclType; + typedef T ClassType; + typedef R ReturnType; + typedef TypeList > > > > Params; + static R call (T* obj, D fp, TypeListValues tvl) + { + return (obj->*fp)(tvl.hd, tvl.tl.hd, tvl.tl.tl.hd, tvl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.hd); + } +}; + +template +struct FuncTraits +{ + static bool const isMemberFunction = true; + static bool const isConstMemberFunction = false; + typedef D DeclType; + typedef T ClassType; + typedef R ReturnType; + typedef TypeList > > > > > Params; + static R call (T* obj, D fp, TypeListValues tvl) + { + return (obj->*fp)(tvl.hd, tvl.tl.hd, tvl.tl.tl.hd, tvl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.hd); + } +}; + +template +struct FuncTraits +{ + static bool const isMemberFunction = true; + static bool const isConstMemberFunction = false; + typedef D DeclType; + typedef T ClassType; + typedef R ReturnType; + typedef TypeList > > > > > > Params; + static R call (T* obj, D fp, TypeListValues tvl) + { + return (obj->*fp)(tvl.hd, tvl.tl.hd, tvl.tl.tl.hd, tvl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.tl.hd); + } +}; + +template +struct FuncTraits +{ + static bool const isMemberFunction = true; + static bool const isConstMemberFunction = false; + typedef D DeclType; + typedef T ClassType; + typedef R ReturnType; + typedef TypeList > > > > > > > Params; + static R call (T* obj, D fp, TypeListValues tvl) + { + return (obj->*fp)(tvl.hd, tvl.tl.hd, tvl.tl.tl.hd, tvl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.tl.tl.hd); + } +}; + +/* Const member function pointers. */ + +template +struct FuncTraits +{ + static bool const isMemberFunction = true; + static bool const isConstMemberFunction = true; + typedef D DeclType; + typedef T ClassType; + typedef R ReturnType; + typedef None Params; + static R call (T const* obj, D fp, TypeListValues ) + { + return (obj->*fp)(); + } +}; + +template +struct FuncTraits +{ + static bool const isMemberFunction = true; + static bool const isConstMemberFunction = true; + typedef D DeclType; + typedef T ClassType; + typedef R ReturnType; + typedef TypeList Params; + static R call (T const* obj, D fp, TypeListValues tvl) + { + return (obj->*fp)(tvl.hd); + } +}; + +template +struct FuncTraits +{ + static bool const isMemberFunction = true; + static bool const isConstMemberFunction = true; + typedef D DeclType; + typedef T ClassType; + typedef R ReturnType; + typedef TypeList > Params; + static R call (T const* obj, R (T::*fp) (P1, P2) const, + TypeListValues tvl) + { + return (obj->*fp)(tvl.hd, tvl.tl.hd); + } +}; + +template +struct FuncTraits +{ + static bool const isMemberFunction = true; + static bool const isConstMemberFunction = true; + typedef D DeclType; + typedef T ClassType; + typedef R ReturnType; + typedef TypeList > > Params; + static R call (T const* obj, D fp, TypeListValues tvl) + { + return (obj->*fp)(tvl.hd, tvl.tl.hd, tvl.tl.tl.hd); + } +}; + +template +struct FuncTraits +{ + static bool const isMemberFunction = true; + static bool const isConstMemberFunction = true; + typedef D DeclType; + typedef T ClassType; + typedef R ReturnType; + typedef TypeList > > > Params; + static R call (T const* obj, D fp, TypeListValues tvl) + { + return (obj->*fp)(tvl.hd, tvl.tl.hd, tvl.tl.tl.hd, tvl.tl.tl.tl.hd); + } +}; + +template +struct FuncTraits +{ + static bool const isMemberFunction = true; + static bool const isConstMemberFunction = true; + typedef D DeclType; + typedef T ClassType; + typedef R ReturnType; + typedef TypeList > > > > Params; + static R call (T const* obj, D fp, TypeListValues tvl) + { + return (obj->*fp)(tvl.hd, tvl.tl.hd, tvl.tl.tl.hd, tvl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.hd); + } +}; + +template +struct FuncTraits +{ + static bool const isMemberFunction = true; + static bool const isConstMemberFunction = true; + typedef D DeclType; + typedef T ClassType; + typedef R ReturnType; + typedef TypeList > > > > > Params; + static R call (T const* obj, D fp, TypeListValues tvl) + { + return (obj->*fp)(tvl.hd, tvl.tl.hd, tvl.tl.tl.hd, tvl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.hd); + } +}; + +template +struct FuncTraits +{ + static bool const isMemberFunction = true; + static bool const isConstMemberFunction = true; + typedef D DeclType; + typedef T ClassType; + typedef R ReturnType; + typedef TypeList > > > > > > Params; + static R call (T const* obj, D fp, TypeListValues tvl) + { + return (obj->*fp)(tvl.hd, tvl.tl.hd, tvl.tl.tl.hd, tvl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.tl.hd); + } +}; + +template +struct FuncTraits +{ + static bool const isMemberFunction = true; + static bool const isConstMemberFunction = true; + typedef D DeclType; + typedef T ClassType; + typedef R ReturnType; + typedef TypeList > > > > > > > Params; + static R call (T const* obj, D fp, TypeListValues tvl) + { + return (obj->*fp)(tvl.hd, tvl.tl.hd, tvl.tl.tl.hd, tvl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.tl.tl.hd); + } +}; + +#if defined (LUABRIDGE_THROWSPEC) + +/* Ordinary function pointers. */ + +template +struct FuncTraits +{ + static bool const isMemberFunction = false; + typedef D DeclType; + typedef R ReturnType; + typedef None Params; + static R call (D fp, TypeListValues const&) + { + return fp (); + } +}; + +template +struct FuncTraits +{ + static bool const isMemberFunction = false; + typedef D DeclType; + typedef R ReturnType; + typedef TypeList Params; + static R call (D fp, TypeListValues tvl) + { + return fp (tvl.hd); + } +}; + +template +struct FuncTraits +{ + static bool const isMemberFunction = false; + typedef D DeclType; + typedef R ReturnType; + typedef TypeList > Params; + static R call (D fp, TypeListValues tvl) + { + return fp (tvl.hd, tvl.tl.hd); + } +}; + +template +struct FuncTraits +{ + static bool const isMemberFunction = false; + typedef D DeclType; + typedef R ReturnType; + typedef TypeList > > Params; + static R call (D fp, TypeListValues tvl) + { + return fp (tvl.hd, tvl.tl.hd, tvl.tl.tl.hd); + } +}; + +template +struct FuncTraits +{ + static bool const isMemberFunction = false; + typedef D DeclType; + typedef R ReturnType; + typedef TypeList > > > Params; + static R call (D fp, TypeListValues tvl) + { + return fp (tvl.hd, tvl.tl.hd, tvl.tl.tl.hd, tvl.tl.tl.tl.hd); + } +}; + +template +struct FuncTraits +{ + static bool const isMemberFunction = false; + typedef D DeclType; + typedef R ReturnType; + typedef TypeList > > > > Params; + static R call (D fp, TypeListValues tvl) + { + return fp (tvl.hd, tvl.tl.hd, tvl.tl.tl.hd, tvl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.hd); + } +}; + +template +struct FuncTraits +{ + static bool const isMemberFunction = false; + typedef D DeclType; + typedef R ReturnType; + typedef TypeList > > > > > Params; + static R call (D fp, TypeListValues tvl) + { + return fp (tvl.hd, tvl.tl.hd, tvl.tl.tl.hd, tvl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.hd); + } +}; + +template +struct FuncTraits +{ + static bool const isMemberFunction = false; + typedef D DeclType; + typedef R ReturnType; + typedef TypeList > > > > > > Params; + static R call (D fp, TypeListValues tvl) + { + return fp (tvl.hd, tvl.tl.hd, tvl.tl.tl.hd, tvl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.tl.hd); + } +}; + +template +struct FuncTraits +{ + static bool const isMemberFunction = false; + typedef D DeclType; + typedef R ReturnType; + typedef TypeList > > > > > > > Params; + static R call (D fp, TypeListValues tvl) + { + return fp (tvl.hd, tvl.tl.hd, tvl.tl.tl.hd, tvl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.tl.tl.hd); + } +}; + +/* Non-const member function pointers with THROWSPEC. */ + +template +struct FuncTraits +{ + static bool const isMemberFunction = true; + static bool const isConstMemberFunction = false; + typedef D DeclType; + typedef T ClassType; + typedef R ReturnType; + typedef None Params; + static R call (T* obj, D fp, TypeListValues const&) + { + return (obj->*fp)(); + } +}; + +template +struct FuncTraits +{ + static bool const isMemberFunction = true; + static bool const isConstMemberFunction = false; + typedef D DeclType; + typedef T ClassType; + typedef R ReturnType; + typedef TypeList Params; + static R call (T* obj, D fp, TypeListValues tvl) + { + return (obj->*fp)(tvl.hd); + } +}; + +template +struct FuncTraits +{ + static bool const isMemberFunction = true; + static bool const isConstMemberFunction = false; + typedef D DeclType; + typedef T ClassType; + typedef R ReturnType; + typedef TypeList > Params; + static R call (T* obj, D fp, TypeListValues tvl) + { + return (obj->*fp)(tvl.hd, tvl.tl.hd); + } +}; + +template +struct FuncTraits +{ + static bool const isMemberFunction = true; + static bool const isConstMemberFunction = false; + typedef D DeclType; + typedef T ClassType; + typedef R ReturnType; + typedef TypeList > > Params; + static R call (T* obj, D fp, TypeListValues tvl) + { + return (obj->*fp)(tvl.hd, tvl.tl.hd, tvl.tl.tl.hd); + } +}; + +template +struct FuncTraits +{ + static bool const isMemberFunction = true; + static bool const isConstMemberFunction = false; + typedef D DeclType; + typedef T ClassType; + typedef R ReturnType; + typedef TypeList > > > Params; + static R call (T* obj, D fp, TypeListValues tvl) + { + return (obj->*fp)(tvl.hd, tvl.tl.hd, tvl.tl.tl.hd, tvl.tl.tl.tl.hd); + } +}; + +template +struct FuncTraits +{ + static bool const isMemberFunction = true; + static bool const isConstMemberFunction = false; + typedef D DeclType; + typedef T ClassType; + typedef R ReturnType; + typedef TypeList > > > > Params; + static R call (T* obj, D fp, TypeListValues tvl) + { + return (obj->*fp)(tvl.hd, tvl.tl.hd, tvl.tl.tl.hd, tvl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.hd); + } +}; + +template +struct FuncTraits +{ + static bool const isMemberFunction = true; + static bool const isConstMemberFunction = false; + typedef D DeclType; + typedef T ClassType; + typedef R ReturnType; + typedef TypeList > > > > > Params; + static R call (T* obj, D fp, TypeListValues tvl) + { + return (obj->*fp)(tvl.hd, tvl.tl.hd, tvl.tl.tl.hd, tvl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.hd); + } +}; + +template +struct FuncTraits +{ + static bool const isMemberFunction = true; + static bool const isConstMemberFunction = false; + typedef D DeclType; + typedef T ClassType; + typedef R ReturnType; + typedef TypeList > > > > > > Params; + static R call (T* obj, D fp, TypeListValues tvl) + { + return (obj->*fp)(tvl.hd, tvl.tl.hd, tvl.tl.tl.hd, tvl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.tl.hd); + } +}; + +template +struct FuncTraits +{ + static bool const isMemberFunction = true; + static bool const isConstMemberFunction = false; + typedef D DeclType; + typedef T ClassType; + typedef R ReturnType; + typedef TypeList > > > > > > > Params; + static R call (T* obj, D fp, TypeListValues tvl) + { + return (obj->*fp)(tvl.hd, tvl.tl.hd, tvl.tl.tl.hd, tvl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.tl.tl.hd); + } +}; + +/* Const member function pointers with THROWSPEC. */ + +template +struct FuncTraits +{ + static bool const isMemberFunction = true; + static bool const isConstMemberFunction = true; + typedef D DeclType; + typedef T ClassType; + typedef R ReturnType; + typedef None Params; + static R call (T const* obj, D fp, TypeListValues ) + { + return (obj->*fp)(); + } +}; + +template +struct FuncTraits +{ + static bool const isMemberFunction = true; + static bool const isConstMemberFunction = true; + typedef D DeclType; + typedef T ClassType; + typedef R ReturnType; + typedef TypeList Params; + static R call (T const* obj, D fp, TypeListValues tvl) + { + return (obj->*fp)(tvl.hd); + } +}; + +template +struct FuncTraits +{ + static bool const isMemberFunction = true; + static bool const isConstMemberFunction = true; + typedef D DeclType; + typedef T ClassType; + typedef R ReturnType; + typedef TypeList > Params; + static R call (T const* obj, D fp, TypeListValues tvl) + { + return (obj->*fp)(tvl.hd, tvl.tl.hd); + } +}; + +template +struct FuncTraits +{ + static bool const isMemberFunction = true; + static bool const isConstMemberFunction = true; + typedef D DeclType; + typedef T ClassType; + typedef R ReturnType; + typedef TypeList > > Params; + static R call (T const* obj, D fp, TypeListValues tvl) + { + return (obj->*fp)(tvl.hd, tvl.tl.hd, tvl.tl.tl.hd); + } +}; + +template +struct FuncTraits +{ + static bool const isMemberFunction = true; + static bool const isConstMemberFunction = true; + typedef D DeclType; + typedef T ClassType; + typedef R ReturnType; + typedef TypeList > > > Params; + static R call (T const* obj, D fp, TypeListValues tvl) + { + return (obj->*fp)(tvl.hd, tvl.tl.hd, tvl.tl.tl.hd, tvl.tl.tl.tl.hd); + } +}; + +template +struct FuncTraits +{ + static bool const isMemberFunction = true; + static bool const isConstMemberFunction = true; + typedef D DeclType; + typedef T ClassType; + typedef R ReturnType; + typedef TypeList > > > > Params; + static R call (T const* obj, D fp, TypeListValues tvl) + { + return (obj->*fp)(tvl.hd, tvl.tl.hd, tvl.tl.tl.hd, tvl.tl.tl.tl.hd, + tvl.tl.tl.tl.tl.hd); + } +}; + +template +struct FuncTraits +{ + static bool const isMemberFunction = true; + static bool const isConstMemberFunction = true; + typedef D DeclType; + typedef T ClassType; + typedef R ReturnType; + typedef TypeList > > > > > Params; + static R call (T const* obj, D fp, TypeListValues tvl) + { + return (obj->*fp)(tvl.hd, tvl.tl.hd, tvl.tl.tl.hd, tvl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.hd); + } +}; + +template +struct FuncTraits +{ + static bool const isMemberFunction = true; + static bool const isConstMemberFunction = true; + typedef D DeclType; + typedef T ClassType; + typedef R ReturnType; + typedef TypeList > > > > > > Params; + static R call (T const* obj, D fp, TypeListValues tvl) + { + return (obj->*fp)(tvl.hd, tvl.tl.hd, tvl.tl.tl.hd, tvl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.tl.hd); + } +}; + +template +struct FuncTraits +{ + static bool const isMemberFunction = true; + static bool const isConstMemberFunction = true; + typedef D DeclType; + typedef T ClassType; + typedef R ReturnType; + typedef TypeList > > > > > > > Params; + static R call (T const* obj, D fp, TypeListValues tvl) + { + return (obj->*fp)(tvl.hd, tvl.tl.hd, tvl.tl.tl.hd, tvl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.tl.tl.tl.hd); + } +}; + +#endif diff --git a/src/lib/lua/bridge/detail/Iterator.h b/src/lib/lua/bridge/detail/Iterator.h new file mode 100644 index 00000000000..d883fb3989a --- /dev/null +++ b/src/lib/lua/bridge/detail/Iterator.h @@ -0,0 +1,114 @@ +//------------------------------------------------------------------------------ +/* + https://github.com/vinniefalco/LuaBridge + + Copyright 2012, Vinnie Falco + + 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. +*/ +//============================================================================== + +/** Allows table iteration. +*/ +class Iterator +{ +private: + lua_State* m_L; + LuaRef m_table; + LuaRef m_key; + LuaRef m_value; + + void next () + { + m_table.push(m_L); + m_key.push (m_L); + if (lua_next (m_L, -2)) + { + m_value.pop (m_L); + m_key.pop (m_L); + } + else + { + m_key = Nil(); + m_value = Nil(); + } + lua_pop(m_L, 1); + } + +public: + explicit Iterator (LuaRef table) + : m_L (table.state ()) + , m_table (table) + , m_key (table.state ()) // m_key is nil + , m_value (table.state ()) // m_value is nil + { + next (); // get the first (key, value) pair from table + } + + lua_State* state () const + { + return m_L; + } + + LuaRef operator* () const + { + return m_value; + } + + LuaRef operator-> () const + { + return m_value; + } + + Iterator& operator++ () + { + if (isNil()) + { + // if the iterator reaches the end, do nothing + return *this; + } + else + { + next(); + return *this; + } + } + + inline bool isNil () const + { + return m_key.isNil (); + } + + inline LuaRef key () const + { + return m_key; + } + + inline LuaRef value () const + { + return m_value; + } + +private: + // Don't use postfix increment, it is less efficient + Iterator operator++ (int); +}; + diff --git a/src/lib/lua/bridge/detail/LuaException.h b/src/lib/lua/bridge/detail/LuaException.h new file mode 100644 index 00000000000..a0944961ced --- /dev/null +++ b/src/lib/lua/bridge/detail/LuaException.h @@ -0,0 +1,113 @@ +//------------------------------------------------------------------------------ +/* + https://github.com/vinniefalco/LuaBridge + + Copyright 2012, Vinnie Falco + Copyright 2008, Nigel Atkinson + + 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. +*/ +//============================================================================== + +class LuaException : public std::exception +{ +private: + lua_State* m_L; + std::string m_what; + +public: + //---------------------------------------------------------------------------- + /** + Construct a LuaException after a lua_pcall(). + */ + LuaException (lua_State* L, int /*code*/) + : m_L (L) + { + whatFromStack (); + } + + //---------------------------------------------------------------------------- + + LuaException (lua_State *L, + char const*, + char const*, + long) + : m_L (L) + { + whatFromStack (); + } + + //---------------------------------------------------------------------------- + + ~LuaException() throw () + { + } + + //---------------------------------------------------------------------------- + + char const* what() const throw () + { + return m_what.c_str(); + } + + //============================================================================ + /** + Throw an exception. + + This centralizes all the exceptions thrown, so that we can set + breakpoints before the stack is unwound, or otherwise customize the + behavior. + */ + template + static void Throw (Exception e) + { + throw e; + } + + //---------------------------------------------------------------------------- + /** + Wrapper for lua_pcall that throws. + */ + static void pcall (lua_State* L, int nargs = 0, int nresults = 0, int msgh = 0) + { + int code = lua_pcall (L, nargs, nresults, msgh); + + if (code != LUABRIDGE_LUA_OK) + Throw (LuaException (L, code)); + } + + //---------------------------------------------------------------------------- + +protected: + void whatFromStack () + { + if (lua_gettop (m_L) > 0) + { + char const* s = lua_tostring (m_L, -1); + m_what = s ? s : ""; + } + else + { + // stack is empty + m_what = "missing error"; + } + } +}; diff --git a/src/lib/lua/bridge/detail/LuaHelpers.h b/src/lib/lua/bridge/detail/LuaHelpers.h new file mode 100644 index 00000000000..7bc84eb1642 --- /dev/null +++ b/src/lib/lua/bridge/detail/LuaHelpers.h @@ -0,0 +1,143 @@ +//------------------------------------------------------------------------------ +/* + https://github.com/vinniefalco/LuaBridge + + 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. +*/ +//============================================================================== + +// These are for Lua versions prior to 5.2.0. +// +#if LUA_VERSION_NUM < 502 +inline int lua_absindex (lua_State* L, int idx) +{ + if (idx > LUA_REGISTRYINDEX && idx < 0) + return lua_gettop (L) + idx + 1; + else + return idx; +} + +inline void lua_rawgetp (lua_State* L, int idx, void const* p) +{ + idx = lua_absindex (L, idx); + lua_pushlightuserdata (L, const_cast (p)); + lua_rawget (L,idx); +} + +inline void lua_rawsetp (lua_State* L, int idx, void const* p) +{ + idx = lua_absindex (L, idx); + lua_pushlightuserdata (L, const_cast (p)); + // put key behind value + lua_insert (L, -2); + lua_rawset (L, idx); +} + +#define LUA_OPEQ 1 +#define LUA_OPLT 2 +#define LUA_OPLE 3 + +inline int lua_compare (lua_State* L, int idx1, int idx2, int op) +{ + switch (op) + { + case LUA_OPEQ: + return lua_equal (L, idx1, idx2); + break; + + case LUA_OPLT: + return lua_lessthan (L, idx1, idx2); + break; + + case LUA_OPLE: + return lua_equal (L, idx1, idx2) || lua_lessthan (L, idx1, idx2); + break; + + default: + return 0; + }; +} + +inline int get_length (lua_State* L, int idx) +{ + return int (lua_objlen (L, idx)); +} + +#else +inline int get_length (lua_State* L, int idx) +{ + lua_len (L, idx); + int len = int (luaL_checknumber (L, -1)); + lua_pop (L, 1); + return len; +} + +#endif + +#ifndef LUA_OK +# define LUABRIDGE_LUA_OK 0 +#else +# define LUABRIDGE_LUA_OK LUA_OK +#endif + +/** Get a table value, bypassing metamethods. +*/ +inline void rawgetfield (lua_State* L, int index, char const* key) +{ + assert (lua_istable (L, index)); + index = lua_absindex (L, index); + lua_pushstring (L, key); + lua_rawget (L, index); +} + +/** Set a table value, bypassing metamethods. +*/ +inline void rawsetfield (lua_State* L, int index, char const* key) +{ + assert (lua_istable (L, index)); + index = lua_absindex (L, index); + lua_pushstring (L, key); + lua_insert (L, -2); + lua_rawset (L, index); +} + +/** Returns true if the value is a full userdata (not light). +*/ +inline bool isfulluserdata (lua_State* L, int index) +{ + return lua_isuserdata (L, index) && !lua_islightuserdata (L, index); +} + +/** Test lua_State objects for global equality. + + This can determine if two different lua_State objects really point + to the same global state, such as when using coroutines. + + @note This is used for assertions. +*/ +inline bool equalstates (lua_State* L1, lua_State* L2) +{ + return lua_topointer (L1, LUA_REGISTRYINDEX) == + lua_topointer (L2, LUA_REGISTRYINDEX); +} diff --git a/src/lib/lua/bridge/detail/LuaRef.h b/src/lib/lua/bridge/detail/LuaRef.h new file mode 100644 index 00000000000..e726bcafe7b --- /dev/null +++ b/src/lib/lua/bridge/detail/LuaRef.h @@ -0,0 +1,1215 @@ +//------------------------------------------------------------------------------ +/* + https://github.com/vinniefalco/LuaBridge + + Copyright 2012, Vinnie Falco + Copyright 2008, Nigel Atkinson + + 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. +*/ +//============================================================================== + +//------------------------------------------------------------------------------ +/** + Type tag for representing LUA_TNIL. + + Construct one of these using `Nil()` to represent a Lua nil. This is faster + than creating a reference in the registry to nil. Example: + + LuaRef t (LuaRef::createTable (L)); + ... + t ["k"] = Nil(); // assign nil +*/ +struct Nil +{ +}; + +//------------------------------------------------------------------------------ +/** + Lightweight reference to a Lua object. + + The reference is maintained for the lifetime of the C++ object. +*/ +class LuaRef +{ +private: + class Proxy; + friend struct Stack ; + + //---------------------------------------------------------------------------- + /** + Pop the Lua stack. + + Pops the specified number of stack items on destruction. We use this + when returning objects, to avoid an explicit temporary variable, since + the destructor executes after the return statement. For example: + + template + U cast (lua_State* L) + { + StackPop p (L, 1); + ... + return U (); // dtor called after this line + } + + @note The `StackPop` object must always be a named local variable. + */ + class StackPop + { + public: + /** Create a StackPop object. + + @param count The number of stack entries to pop on destruction. + */ + StackPop (lua_State* L, int count) + : m_L (L) + , m_count (count) + { + } + + ~StackPop () + { + lua_pop (m_L, m_count); + } + + private: + lua_State* m_L; + int m_count; + }; + + //---------------------------------------------------------------------------- + /** + A proxy for representing table values. + */ + class Proxy + { + private: + lua_State* m_L; + int m_tableRef; + int m_keyRef; + + public: + //-------------------------------------------------------------------------- + /** + Construct a Proxy from a table value. + + The table is in the registry, and the key is at the top of the stack. + The key is popped off the stack. + */ + Proxy (lua_State* L, int tableRef) + : m_L (L) + , m_tableRef (tableRef) + , m_keyRef (luaL_ref (L, LUA_REGISTRYINDEX)) + { + } + + //-------------------------------------------------------------------------- + /** + Create a Proxy via copy constructor. + + It is best to avoid code paths that invoke this, because it creates + an extra temporary Lua reference. Typically this is done by passing + the Proxy parameter as a `const` reference. + */ + Proxy (Proxy const& other) + : m_L (other.m_L) + , m_tableRef (other.m_tableRef) + { + // If this assert goes off it means code is taking this path, + // which is better avoided. + // + assert (0); + + lua_rawgeti (m_L, LUA_REGISTRYINDEX, other.m_keyRef); + m_keyRef = luaL_ref (m_L, LUA_REGISTRYINDEX); + } + + //-------------------------------------------------------------------------- + /** + Destroy the proxy. + + This does not destroy the table value. + */ + ~Proxy () + { + luaL_unref (m_L, LUA_REGISTRYINDEX, m_keyRef); + } + + //-------------------------------------------------------------------------- + /** + Return a reference to the table value. + */ + int createRef () const + { + push (m_L); + return luaL_ref (m_L, LUA_REGISTRYINDEX); + } + + //-------------------------------------------------------------------------- + /** + Assign a new value to this table key. + + This may invoke metamethods. + */ + template + Proxy& operator= (T v) + { + StackPop p (m_L, 1); + lua_rawgeti (m_L, LUA_REGISTRYINDEX, m_tableRef); + lua_rawgeti (m_L, LUA_REGISTRYINDEX, m_keyRef); + Stack ::push (m_L, v); + lua_rawset (m_L, -3); + return *this; + } + + //-------------------------------------------------------------------------- + /** + Assign a new value to this table key. + + The assignment is raw, no metamethods are invoked. + */ + template + Proxy& rawset (T v) + { + StackPop p (m_L, 1); + lua_rawgeti (m_L, LUA_REGISTRYINDEX, m_tableRef); + lua_rawgeti (m_L, LUA_REGISTRYINDEX, m_keyRef); + Stack ::push (m_L, v); + lua_settable (m_L, -3); + return *this; + } + + //========================================================================== + // + // This group of member functions mirrors the member functions in LuaRef. + + /** Retrieve the lua_State associated with the table value. + */ + lua_State* state () const + { + return m_L; + } + + //-------------------------------------------------------------------------- + /** + Push the value onto the Lua stack. + */ + void push (lua_State* L) const + { + assert (equalstates (L, m_L)); + lua_rawgeti (L, LUA_REGISTRYINDEX, m_tableRef); + lua_rawgeti (L, LUA_REGISTRYINDEX, m_keyRef); + lua_gettable (L, -2); + lua_remove (L, -2); // remove the table + } + + //-------------------------------------------------------------------------- + /** + Determine the object type. + + The return values are the same as for `lua_type`. + */ + int type () const + { + int result; + push (m_L); + result = lua_type (m_L, -1); + lua_pop (m_L, 1); + return result; + } + + inline bool isNil () const { return type () == LUA_TNIL; } + inline bool isNumber () const { return type () == LUA_TNUMBER; } + inline bool isString () const { return type () == LUA_TSTRING; } + inline bool isTable () const { return type () == LUA_TTABLE; } + inline bool isFunction () const { return type () == LUA_TFUNCTION; } + inline bool isUserdata () const { return type () == LUA_TUSERDATA; } + inline bool isThread () const { return type () == LUA_TTHREAD; } + inline bool isLightUserdata () const { return type () == LUA_TLIGHTUSERDATA; } + + //-------------------------------------------------------------------------- + /** + Perform an explicit conversion. + */ + template + T cast () const + { + StackPop p (m_L, 1); + push (m_L); + + // lua_gettop is used because Userdata::getClass() doesn't handle + // negative stack indexes. + // + return Stack ::get (m_L, lua_gettop (m_L)); + } + + //-------------------------------------------------------------------------- + /** + Universal implicit conversion operator. + + NOTE: Visual Studio 2010 and 2012 have a bug where this function + is not used. See: + + http://social.msdn.microsoft.com/Forums/en-US/vcgeneral/thread/e30b2664-a92d-445c-9db2-e8e0fbde2014 + https://connect.microsoft.com/VisualStudio/feedback/details/771509/correct-code-doesnt-compile + + // This code snippet fails to compile in vs2010,vs2012 + struct S { + template inline operator T () const { return T (); } + }; + int main () { + S () || false; + return 0; + } + */ + template + inline operator T () const + { + return cast (); + } + + //-------------------------------------------------------------------------- + /** + Universal comparison operators. + */ + /** @{ */ + template + bool operator== (T rhs) const + { + StackPop p (m_L, 2); + push (m_L); + Stack ::push (m_L, rhs); + return lua_compare (m_L, -2, -1, LUA_OPEQ) == 1; + } + + template + bool operator< (T rhs) const + { + StackPop p (m_L, 2); + push (m_L); + Stack ::push (m_L, rhs); + return lua_compare (m_L, -2, -1, LUA_OPLT) == 1; + } + + template + bool operator<= (T rhs) const + { + StackPop p (m_L, 2); + push (m_L); + Stack ::push (m_L, rhs); + return lua_compare (m_L, -2, -1, LUA_OPLE) == 1; + } + + template + bool operator> (T rhs) const + { + StackPop p (m_L, 2); + push (m_L); + Stack ::push (m_L, rhs); + return lua_compare (m_L, -1, -2, LUA_OPLT) == 1; + } + + template + bool operator>= (T rhs) const + { + StackPop p (m_L, 2); + push (m_L); + Stack ::push (m_L, rhs); + return lua_compare (m_L, -1, -2, LUA_OPLE) == 1; + } + + template + bool rawequal (T rhs) const + { + StackPop p (m_L, 2); + push (m_L); + Stack ::push (m_L, rhs); + return lua_rawequal (m_L, -1, -2) == 1; + } + /** @} */ + + //-------------------------------------------------------------------------- + /** + Access a table value using a key. + + This invokes metamethods. + */ + template + Proxy operator[] (T key) const + { + return LuaRef (*this) [key]; + } + + //-------------------------------------------------------------------------- + /** + Access a table value using a key. + + The operation is raw, metamethods are not invoked. The result is + passed by value and may not be modified. + */ + template + LuaRef rawget (T key) const + { + StackPop (m_L, 1); + push (m_L); + Stack ::push (m_L, key); + lua_rawget (m_L, -2); + return LuaRef (m_L, FromStack ()); + } + + //-------------------------------------------------------------------------- + /** + Append a value to the table. + + If the table is a sequence this will add another element to it. + */ + template + void append (T v) const + { + push (m_L); + Stack ::push (m_L, v); + luaL_ref (m_L, -2); + lua_pop (m_L, 1); + } + + //-------------------------------------------------------------------------- + /** + Call the length operator. + + This is identical to applying the Lua # operator. + */ + int length () const + { + StackPop p (m_L, 1); + push (m_L); + return get_length (m_L, -1); + } + + //-------------------------------------------------------------------------- + /** + Call Lua code. + + These overloads allow Lua code to be called with up to 8 parameters. + The return value is provided as a LuaRef (which may be LUA_REFNIL). + If an error occurs, a LuaException is thrown. + */ + /** @{ */ + LuaRef const operator() () const + { + push (m_L); + LuaException::pcall (m_L, 0, 1); + return LuaRef (m_L, FromStack ()); + } + + template + LuaRef const operator() (P1 p1) const + { + push (m_L); + Stack ::push (m_L, p1); + LuaException::pcall (m_L, 1, 1); + return LuaRef (m_L, FromStack ()); + } + + template + LuaRef const operator() (P1 p1, P2 p2) const + { + push (m_L); + Stack ::push (m_L, p1); + Stack ::push (m_L, p2); + LuaException::pcall (m_L, 2, 1); + return LuaRef (m_L, FromStack ()); + } + + template + LuaRef const operator() (P1 p1, P2 p2, P3 p3) const + { + push (m_L); + Stack ::push (m_L, p1); + Stack ::push (m_L, p2); + Stack ::push (m_L, p3); + LuaException::pcall (m_L, 3, 1); + return LuaRef (m_L, FromStack ()); + } + + template + LuaRef const operator() (P1 p1, P2 p2, P3 p3, P4 p4) const + { + push (m_L); + Stack ::push (m_L, p1); + Stack ::push (m_L, p2); + Stack ::push (m_L, p3); + Stack ::push (m_L, p4); + LuaException::pcall (m_L, 4, 1); + return LuaRef (m_L, FromStack ()); + } + + template + LuaRef const operator() (P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) const + { + push (m_L); + Stack ::push (m_L, p1); + Stack ::push (m_L, p2); + Stack ::push (m_L, p3); + Stack ::push (m_L, p4); + Stack ::push (m_L, p5); + LuaException::pcall (m_L, 5, 1); + return LuaRef (m_L, FromStack ()); + } + + template + LuaRef const operator() (P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6) const + { + push (m_L); + Stack ::push (m_L, p1); + Stack ::push (m_L, p2); + Stack ::push (m_L, p3); + Stack ::push (m_L, p4); + Stack ::push (m_L, p5); + Stack ::push (m_L, p6); + LuaException::pcall (m_L, 6, 1); + return LuaRef (m_L, FromStack ()); + } + + template + LuaRef const operator() (P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7) const + { + push (m_L); + Stack ::push (m_L, p1); + Stack ::push (m_L, p2); + Stack ::push (m_L, p3); + Stack ::push (m_L, p4); + Stack ::push (m_L, p5); + Stack ::push (m_L, p6); + Stack ::push (m_L, p7); + LuaException::pcall (m_L, 7, 1); + return LuaRef (m_L, FromStack ()); + } + + template + LuaRef const operator() (P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7, P8 p8) const + { + push (m_L); + Stack ::push (m_L, p1); + Stack ::push (m_L, p2); + Stack ::push (m_L, p3); + Stack ::push (m_L, p4); + Stack ::push (m_L, p5); + Stack ::push (m_L, p6); + Stack ::push (m_L, p7); + Stack ::push (m_L, p8); + LuaException::pcall (m_L, 8, 1); + return LuaRef (m_L, FromStack ()); + } + /** @} */ + + //========================================================================== + }; + +private: + friend struct Stack ; + + //---------------------------------------------------------------------------- + /** + Type tag for stack construction. + */ + struct FromStack { }; + + //---------------------------------------------------------------------------- + /** + Create a reference to an object at the top of the Lua stack and pop it. + + This constructor is private and not invoked directly. + Instead, use the `fromStack` function. + + @note The object is popped. + */ + LuaRef (lua_State* L, FromStack) + : m_L (L) + { + m_ref = luaL_ref (m_L, LUA_REGISTRYINDEX); + } + + //---------------------------------------------------------------------------- + /** + Create a reference to an object on the Lua stack. + + This constructor is private and not invoked directly. + Instead, use the `fromStack` function. + + @note The object is not popped. + */ + LuaRef (lua_State* L, int index, FromStack) + : m_L (L) + { + lua_pushvalue (m_L, index); + m_ref = luaL_ref (m_L, LUA_REGISTRYINDEX); + } + + //---------------------------------------------------------------------------- + + // This type of construction is disallowed, since we don't have a `lua_State`. + // + template + LuaRef (T) + { + } + + //---------------------------------------------------------------------------- + /** + Create a reference to this ref. + + This is used internally. + */ + int createRef () const + { + if (m_ref != LUA_REFNIL) + { + push (m_L); + return luaL_ref (m_L, LUA_REGISTRYINDEX); + } + else + { + return LUA_REFNIL; + } + } + +public: + //---------------------------------------------------------------------------- + /** + Create a nil reference. + + The LuaRef may be assigned later. + */ + LuaRef (lua_State* L) + : m_L (L) + , m_ref (LUA_REFNIL) + { + } + + //---------------------------------------------------------------------------- + /** + Create a reference to a value. + */ + template + LuaRef (lua_State* L, T v) + : m_L (L) + { + Stack ::push (m_L, v); + m_ref = luaL_ref (m_L, LUA_REGISTRYINDEX); + } + + //---------------------------------------------------------------------------- + /** + Create a reference to a table value. + */ + LuaRef (Proxy const& v) + : m_L (v.state ()) + , m_ref (v.createRef ()) + { + } + + //---------------------------------------------------------------------------- + /** + Create a new reference to an existing reference. + */ + LuaRef (LuaRef const& other) + : m_L (other.m_L) + , m_ref (other.createRef ()) + { + } + + //---------------------------------------------------------------------------- + /** + Destroy a reference. + + The corresponding Lua registry reference will be released. + + @note If the state refers to a thread, it is the responsibility of the + caller to ensure that the thread still exists when the LuaRef + is destroyed. + */ + ~LuaRef () + { + luaL_unref (m_L, LUA_REGISTRYINDEX, m_ref); + } + + //---------------------------------------------------------------------------- + /** + Return a LuaRef from a stack item. + + The stack item is not popped. + */ + static LuaRef fromStack (lua_State* L, int index) + { + lua_pushvalue (L, index); + return LuaRef (L, FromStack ()); + } + + //---------------------------------------------------------------------------- + /** + Create a new empty table and return a reference to it. + + It is also possible to use the free function `newTable`. + + @see ::getGlobal + */ + static LuaRef newTable (lua_State* L) + { + lua_newtable (L); + return LuaRef (L, FromStack ()); + } + + //---------------------------------------------------------------------------- + /** + Return a reference to a named global. + + It is also possible to use the free function `getGlobal`. + + @see ::getGlobal + */ + static LuaRef getGlobal (lua_State *L, char const* name) + { + lua_getglobal (L, name); + return LuaRef (L, FromStack ()); + } + + //---------------------------------------------------------------------------- + /** + Assign a different value to this LuaRef. + */ + template + LuaRef& operator= (T rhs) + { + luaL_unref (m_L, LUA_REGISTRYINDEX, m_ref); + Stack ::push (m_L, rhs); + m_ref = luaL_ref (m_L, LUA_REGISTRYINDEX); + return *this; + } + + //---------------------------------------------------------------------------- + /** + Assign another LuaRef to this LuaRef. + */ + LuaRef& operator= (LuaRef const& rhs) + { + luaL_unref (m_L, LUA_REGISTRYINDEX, m_ref); + rhs.push (m_L); + m_L = rhs.state (); + m_ref = luaL_ref (m_L, LUA_REGISTRYINDEX); + return *this; + } + + //---------------------------------------------------------------------------- + /** + converts to a string using luas tostring function + */ + std::string tostring() const + { + lua_getglobal (m_L, "tostring"); + push (m_L); + lua_call (m_L, 1, 1); + const char* str = lua_tostring(m_L, 1); + lua_pop(m_L, 1); + return std::string(str); + } + + //---------------------------------------------------------------------------- + /** + Print a text description of the value to a stream. + + This is used for diagnostics. + */ + void print (std::ostream& os) const + { + switch (type ()) + { + case LUA_TNIL: + os << "nil"; + break; + + case LUA_TNUMBER: + os << cast (); + break; + + case LUA_TBOOLEAN: + os << (cast () ? "true" : "false"); + break; + + case LUA_TSTRING: + os << '"' << cast () << '"'; + break; + + case LUA_TTABLE: + os << "table: " << tostring(); + break; + + case LUA_TFUNCTION: + os << "function: " << tostring(); + break; + + case LUA_TUSERDATA: + os << "userdata: " << tostring(); + break; + + case LUA_TTHREAD: + os << "thread: " << tostring(); + break; + + case LUA_TLIGHTUSERDATA: + os << "lightuserdata: " << tostring(); + break; + + default: + os << "unknown"; + break; + } + } + + //============================================================================ + // + // This group of member functions is mirrored in Proxy + // + + /** Retrieve the lua_State associated with the reference. + */ + lua_State* state () const + { + return m_L; + } + + //---------------------------------------------------------------------------- + /** + Place the object onto the Lua stack. + */ + void push (lua_State* L) const + { + assert (equalstates (L, m_L)); + lua_rawgeti (L, LUA_REGISTRYINDEX, m_ref); + } + + //---------------------------------------------------------------------------- + /** + Pop the top of Lua stack and assign the ref to m_ref + */ + void pop (lua_State* L) + { + assert (equalstates (L, m_L)); + luaL_unref (m_L, LUA_REGISTRYINDEX, m_ref); + m_ref = luaL_ref (m_L, LUA_REGISTRYINDEX); + } + + //---------------------------------------------------------------------------- + /** + Determine the object type. + + The return values are the same as for `lua_type`. + */ + /** @{ */ + int type () const + { + int result; + if (m_ref != LUA_REFNIL) + { + push (m_L); + result = lua_type (m_L, -1); + lua_pop (m_L, 1); + } + else + { + result = LUA_TNIL; + } + + return result; + } + + // should never happen + //inline bool isNone () const { return m_ref == LUA_NOREF; } + + inline bool isNil () const { return type () == LUA_TNIL; } + inline bool isNumber () const { return type () == LUA_TNUMBER; } + inline bool isString () const { return type () == LUA_TSTRING; } + inline bool isTable () const { return type () == LUA_TTABLE; } + inline bool isFunction () const { return type () == LUA_TFUNCTION; } + inline bool isUserdata () const { return type () == LUA_TUSERDATA; } + inline bool isThread () const { return type () == LUA_TTHREAD; } + inline bool isLightUserdata () const { return type () == LUA_TLIGHTUSERDATA; } + /** @} */ + + //---------------------------------------------------------------------------- + /** + Perform an explicit conversion. + */ + template + T cast () const + { + StackPop p (m_L, 1); + push (m_L); + + // lua_gettop is used because Userdata::getClass() doesn't handle + // negative stack indexes. + // + return Stack ::get (m_L, lua_gettop (m_L)); + } + + //---------------------------------------------------------------------------- + /** + Universal implicit conversion operator. + + NOTE: Visual Studio 2010 and 2012 have a bug where this function + is not used. See: + + http://social.msdn.microsoft.com/Forums/en-US/vcgeneral/thread/e30b2664-a92d-445c-9db2-e8e0fbde2014 + https://connect.microsoft.com/VisualStudio/feedback/details/771509/correct-code-doesnt-compile + + // This code snippet fails to compile in vs2010,vs2012 + struct S { + template inline operator T () const { return T (); } + }; + int main () { + S () || false; + return 0; + } + */ + template + inline operator T () const + { + return cast (); + } + + //---------------------------------------------------------------------------- + /** + Universal comparison operators. + */ + /** @{ */ + template + bool operator== (T rhs) const + { + StackPop p (m_L, 2); + push (m_L); + Stack ::push (m_L, rhs); + return lua_compare (m_L, -2, -1, LUA_OPEQ) == 1; + } + + template + bool operator< (T rhs) const + { + StackPop p (m_L, 2); + push (m_L); + Stack ::push (m_L, rhs); + return lua_compare (m_L, -2, -1, LUA_OPLT) == 1; + } + + template + bool operator<= (T rhs) const + { + StackPop p (m_L, 2); + push (m_L); + Stack ::push (m_L, rhs); + return lua_compare (m_L, -2, -1, LUA_OPLE) == 1; + } + + template + bool operator> (T rhs) const + { + StackPop p (m_L, 2); + push (m_L); + Stack ::push (m_L, rhs); + return lua_compare (m_L, -1, -2, LUA_OPLT) == 1; + } + + template + bool operator>= (T rhs) const + { + StackPop p (m_L, 2); + push (m_L); + Stack ::push (m_L, rhs); + return lua_compare (m_L, -1, -2, LUA_OPLE) == 1; + } + + template + bool rawequal (T rhs) const + { + StackPop p (m_L, 2); + push (m_L); + Stack ::push (m_L, rhs); + return lua_rawequal (m_L, -1, -2) == 1; + } + /** @} */ + + //---------------------------------------------------------------------------- + /** + Append a value to the table. + + If the table is a sequence this will add another element to it. + */ + template + void append (T v) const + { + push (m_L); + Stack ::push (m_L, v); + luaL_ref (m_L, -2); + lua_pop (m_L, 1); + } + + //---------------------------------------------------------------------------- + /** + Call the length operator. + + This is identical to applying the Lua # operator. + */ + int length () const + { + StackPop p (m_L, 1); + push (m_L); + return get_length (m_L, -1); + } + + //---------------------------------------------------------------------------- + /** + Access a table value using a key. + + This invokes metamethods. + */ + template + Proxy operator[] (T key) const + { + Stack ::push (m_L, key); + return Proxy (m_L, m_ref); + } + + //---------------------------------------------------------------------------- + /** + Call Lua code. + + These overloads allow Lua code to be called with up to 8 parameters. + The return value is provided as a LuaRef (which may be LUA_REFNIL). + If an error occurs, a LuaException is thrown. + */ + /** @{ */ + LuaRef const operator() () const + { + push (m_L); + LuaException::pcall (m_L, 0, 1); + return LuaRef (m_L, FromStack ()); + } + + template + LuaRef const operator() (P1 p1) const + { + push (m_L); + Stack ::push (m_L, p1); + LuaException::pcall (m_L, 1, 1); + return LuaRef (m_L, FromStack ()); + } + + template + LuaRef const operator() (P1 p1, P2 p2) const + { + push (m_L); + Stack ::push (m_L, p1); + Stack ::push (m_L, p2); + LuaException::pcall (m_L, 2, 1); + return LuaRef (m_L, FromStack ()); + } + + template + LuaRef const operator() (P1 p1, P2 p2, P3 p3) const + { + push (m_L); + Stack ::push (m_L, p1); + Stack ::push (m_L, p2); + Stack ::push (m_L, p3); + LuaException::pcall (m_L, 3, 1); + return LuaRef (m_L, FromStack ()); + } + + template + LuaRef const operator() (P1 p1, P2 p2, P3 p3, P4 p4) const + { + push (m_L); + Stack ::push (m_L, p1); + Stack ::push (m_L, p2); + Stack ::push (m_L, p3); + Stack ::push (m_L, p4); + LuaException::pcall (m_L, 4, 1); + return LuaRef (m_L, FromStack ()); + } + + template + LuaRef const operator() (P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) const + { + push (m_L); + Stack ::push (m_L, p1); + Stack ::push (m_L, p2); + Stack ::push (m_L, p3); + Stack ::push (m_L, p4); + Stack ::push (m_L, p5); + LuaException::pcall (m_L, 5, 1); + return LuaRef (m_L, FromStack ()); + } + + template + LuaRef const operator() (P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6) const + { + push (m_L); + Stack ::push (m_L, p1); + Stack ::push (m_L, p2); + Stack ::push (m_L, p3); + Stack ::push (m_L, p4); + Stack ::push (m_L, p5); + Stack ::push (m_L, p6); + LuaException::pcall (m_L, 6, 1); + return LuaRef (m_L, FromStack ()); + } + + template + LuaRef const operator() (P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7) const + { + push (m_L); + Stack ::push (m_L, p1); + Stack ::push (m_L, p2); + Stack ::push (m_L, p3); + Stack ::push (m_L, p4); + Stack ::push (m_L, p5); + Stack ::push (m_L, p6); + Stack ::push (m_L, p7); + LuaException::pcall (m_L, 7, 1); + return LuaRef (m_L, FromStack ()); + } + + template + LuaRef const operator() (P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7, P8 p8) const + { + push (m_L); + Stack ::push (m_L, p1); + Stack ::push (m_L, p2); + Stack ::push (m_L, p3); + Stack ::push (m_L, p4); + Stack ::push (m_L, p5); + Stack ::push (m_L, p6); + Stack ::push (m_L, p7); + Stack ::push (m_L, p8); + LuaException::pcall (m_L, 8, 1); + return LuaRef (m_L, FromStack ()); + } + /** @} */ + + //============================================================================ + +private: + lua_State* m_L; + int m_ref; +}; + +//------------------------------------------------------------------------------ +/** + Stack specialization for Nil +*/ +template <> +struct Stack +{ +public: + static inline void push (lua_State* L, Nil) + { + lua_pushnil (L); + } +}; + +//------------------------------------------------------------------------------ +/** + Stack specialization for LuaRef. +*/ +template <> +struct Stack +{ +public: + // The value is const& to prevent a copy construction. + // + static inline void push (lua_State* L, LuaRef const& v) + { + v.push (L); + } + + static inline LuaRef get (lua_State* L, int index) + { + return LuaRef (L, index, LuaRef::FromStack ()); + } +}; + +//------------------------------------------------------------------------------ +/** + Stack specialization for Proxy. +*/ +template <> +struct Stack +{ +public: + // The value is const& to prevent a copy construction. + // + static inline void push (lua_State* L, LuaRef::Proxy const& v) + { + v.push (L); + } +}; + +//------------------------------------------------------------------------------ +/** + Create a reference to a new, empty table. + + This is a syntactic abbreviation for LuaRef::newTable(). +*/ +inline LuaRef newTable (lua_State* L) +{ + return LuaRef::newTable (L); +} + +//------------------------------------------------------------------------------ +/** + Create a reference to a value in the global table. + + This is a syntactic abbreviation for LuaRef::getGlobal(). +*/ +inline LuaRef getGlobal (lua_State *L, char const* name) +{ + return LuaRef::getGlobal (L, name); +} + +//------------------------------------------------------------------------------ +/** + Write a LuaRef to a stream. + + This allows LuaRef and table proxies to work with streams. +*/ +inline std::ostream& operator<< (std::ostream& os, LuaRef const& ref) +{ + ref.print (os); + return os; +} + +//------------------------------------------------------------------------------ + +// more C++-like cast syntax +// +template +inline T LuaRef_cast(LuaRef const& lr) +{ + return lr.cast(); +} diff --git a/src/lib/lua/bridge/detail/Namespace.h b/src/lib/lua/bridge/detail/Namespace.h new file mode 100644 index 00000000000..ff22e6b092c --- /dev/null +++ b/src/lib/lua/bridge/detail/Namespace.h @@ -0,0 +1,1136 @@ +//------------------------------------------------------------------------------ +/* + https://github.com/vinniefalco/LuaBridge + + 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. +*/ +//============================================================================== + +/** 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; + + 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) + { + 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 : public ClassBase + { + public: + //========================================================================== + /** + Register a new class or add to an existing class registration. + */ + Class (char const* name, Namespace const* parent) : ClassBase (parent->L) + { + 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 + { + 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) + { + 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) + { + 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; + } + + //-------------------------------------------------------------------------- + /** + 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; + } + + //-------------------------------------------------------------------------- + /** + Add or replace a static member function. + */ + template + Class & addStaticFunction (char const* name, FP const 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*)) + { + 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) + { + 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; + } + + //-------------------------------------------------------------------------- + /** + 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; + } + + //-------------------------------------------------------------------------- + /** + Add or replace a member function. + */ + template + Class & addFunction (char const* name, MemFn mf) + { + CFunc::CallMemberFunctionHelper ::isConstMemberFunction>::add (L, name, mf); + return *this; + } + + //-------------------------------------------------------------------------- + /** + Add or replace a member lua_CFunction. + */ + Class & addCFunction (char const* name, int (T::*mfp)(lua_State*)) + { + 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; + } + + //-------------------------------------------------------------------------- + /** + Add or replace a const member lua_CFunction. + */ + Class & addCFunction (char const* name, int (T::*mfp)(lua_State*) const) + { + 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 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 () + { + lua_pushcclosure (L, + &ctorContainerProxy ::Params, C>, 0); + rawsetfield(L, -2, "__call"); + + return *this; + } + + template + Class & addConstructor () + { + lua_pushcclosure (L, + &ctorPlacementProxy ::Params, T>, 0); + rawsetfield(L, -2, "__call"); + + return *this; + } + }; + +private: + //---------------------------------------------------------------------------- + /** + Open the global namespace for registrations. + */ + explicit Namespace (lua_State* L_) + : L (L_) + , m_stackSize (0) + { + lua_getglobal (L, "_G"); + ++m_stackSize; + } + + //---------------------------------------------------------------------------- + /** + 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) + { + 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) + { + 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) + { + 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; + } + + //---------------------------------------------------------------------------- + /** + 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; + } + + //---------------------------------------------------------------------------- + /** + 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) + { + 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; + } + + //---------------------------------------------------------------------------- + /** + Add or replace a lua_CFunction. + */ + Namespace& addCFunction (char const* name, int (*const fp)(lua_State*)) + { + 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); + } + + //---------------------------------------------------------------------------- + /** + 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) + { + return Class (name, this, ClassInfo ::getStaticKey ()); + } +}; + +//------------------------------------------------------------------------------ +/** + 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); +} diff --git a/src/lib/lua/bridge/detail/Stack.h b/src/lib/lua/bridge/detail/Stack.h new file mode 100644 index 00000000000..1a0aae7c29b --- /dev/null +++ b/src/lib/lua/bridge/detail/Stack.h @@ -0,0 +1,469 @@ +//------------------------------------------------------------------------------ +/* + https://github.com/vinniefalco/LuaBridge + + 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. +*/ +//============================================================================== + +//------------------------------------------------------------------------------ +/** + Receive the lua_State* as an argument. +*/ +template <> +struct Stack +{ + static lua_State* get (lua_State* L, int) + { + return L; + } +}; + +//------------------------------------------------------------------------------ +/** + Push a lua_CFunction. +*/ +template <> +struct Stack +{ + static void push (lua_State* L, lua_CFunction f) + { + lua_pushcfunction (L, f); + } + + static lua_CFunction get (lua_State* L, int index) + { + return lua_tocfunction (L, index); + } +}; + +//------------------------------------------------------------------------------ +/** + Stack specialization for `int`. +*/ +template <> +struct Stack +{ + static inline void push (lua_State* L, int value) + { + lua_pushinteger (L, static_cast (value)); + } + + static inline int get (lua_State* L, int index) + { + return static_cast (luaL_checkinteger (L, index)); + } +}; + +template <> +struct Stack +{ + static inline void push (lua_State* L, int value) + { + lua_pushnumber (L, static_cast (value)); + } + + static inline int get (lua_State* L, int index) + { + return static_cast (luaL_checknumber (L, index)); + } +}; +//------------------------------------------------------------------------------ +/** + Stack specialization for `unsigned int`. +*/ +template <> +struct Stack +{ + static inline void push (lua_State* L, unsigned int value) + { + lua_pushinteger (L, static_cast (value)); + } + + static inline unsigned int get (lua_State* L, int index) + { + return static_cast (luaL_checkinteger (L, index)); + } +}; + +template <> +struct Stack +{ + static inline void push (lua_State* L, unsigned int value) + { + lua_pushnumber (L, static_cast (value)); + } + + static inline unsigned int get (lua_State* L, int index) + { + return static_cast (luaL_checknumber (L, index)); + } +}; + +//------------------------------------------------------------------------------ +/** + Stack specialization for `unsigned char`. +*/ +template <> +struct Stack +{ + static inline void push (lua_State* L, unsigned char value) + { + lua_pushinteger (L, static_cast (value)); + } + + static inline unsigned char get (lua_State* L, int index) + { + return static_cast (luaL_checkinteger (L, index)); + } +}; + +template <> +struct Stack +{ + static inline void push (lua_State* L, unsigned char value) + { + lua_pushnumber (L, static_cast (value)); + } + + static inline unsigned char get (lua_State* L, int index) + { + return static_cast (luaL_checknumber (L, index)); + } +}; + +//------------------------------------------------------------------------------ +/** + Stack specialization for `short`. +*/ +template <> +struct Stack +{ + static inline void push (lua_State* L, short value) + { + lua_pushinteger (L, static_cast (value)); + } + + static inline short get (lua_State* L, int index) + { + return static_cast (luaL_checkinteger (L, index)); + } +}; + +template <> +struct Stack +{ + static inline void push (lua_State* L, short value) + { + lua_pushnumber (L, static_cast (value)); + } + + static inline short get (lua_State* L, int index) + { + return static_cast (luaL_checknumber (L, index)); + } +}; + +//------------------------------------------------------------------------------ +/** + Stack specialization for `unsigned short`. +*/ +template <> +struct Stack +{ + static inline void push (lua_State* L, unsigned short value) + { + lua_pushinteger (L, static_cast (value)); + } + + static inline unsigned short get (lua_State* L, int index) + { + return static_cast (luaL_checkinteger (L, index)); + } +}; + +template <> +struct Stack +{ + static inline void push (lua_State* L, unsigned short value) + { + lua_pushnumber (L, static_cast (value)); + } + + static inline unsigned short get (lua_State* L, int index) + { + return static_cast (luaL_checknumber (L, index)); + } +}; + +//------------------------------------------------------------------------------ +/** + Stack specialization for `long`. +*/ +template <> +struct Stack +{ + static inline void push (lua_State* L, long value) + { + lua_pushinteger (L, static_cast (value)); + } + + static inline long get (lua_State* L, int index) + { + return static_cast (luaL_checkinteger (L, index)); + } +}; + +template <> +struct Stack +{ + static inline void push (lua_State* L, long value) + { + lua_pushnumber (L, static_cast (value)); + } + + static inline long get (lua_State* L, int index) + { + return static_cast (luaL_checknumber (L, index)); + } +}; + +//------------------------------------------------------------------------------ +/** + Stack specialization for `unsigned long`. +*/ +template <> +struct Stack +{ + static inline void push (lua_State* L, unsigned long value) + { + lua_pushinteger (L, static_cast (value)); + } + + static inline unsigned long get (lua_State* L, int index) + { + return static_cast (luaL_checkinteger (L, index)); + } +}; + +template <> +struct Stack +{ + static inline void push (lua_State* L, unsigned long value) + { + lua_pushnumber (L, static_cast (value)); + } + + static inline unsigned long get (lua_State* L, int index) + { + return static_cast (luaL_checknumber (L, index)); + } +}; + +//------------------------------------------------------------------------------ +/** + Stack specialization for `float`. +*/ +template <> +struct Stack +{ + static inline void push (lua_State* L, float value) + { + lua_pushnumber (L, static_cast (value)); + } + + static inline float get (lua_State* L, int index) + { + return static_cast (luaL_checknumber (L, index)); + } +}; + +template <> +struct Stack +{ + static inline void push (lua_State* L, float value) + { + lua_pushnumber (L, static_cast (value)); + } + + static inline float get (lua_State* L, int index) + { + return static_cast (luaL_checknumber (L, index)); + } +}; + +//------------------------------------------------------------------------------ +/** + Stack specialization for `double`. +*/ +template <> struct Stack +{ + static inline void push (lua_State* L, double value) + { + lua_pushnumber (L, static_cast (value)); + } + + static inline double get (lua_State* L, int index) + { + return static_cast (luaL_checknumber (L, index)); + } +}; + +template <> struct Stack +{ + static inline void push (lua_State* L, double value) + { + lua_pushnumber (L, static_cast (value)); + } + + static inline double get (lua_State* L, int index) + { + return static_cast (luaL_checknumber (L, index)); + } +}; + +//------------------------------------------------------------------------------ +/** + Stack specialization for `bool`. +*/ +template <> +struct Stack { + static inline void push (lua_State* L, bool value) + { + lua_pushboolean (L, value ? 1 : 0); + } + + static inline bool get (lua_State* L, int index) + { + return lua_toboolean (L, index) ? true : false; + } +}; + +template <> +struct Stack { + static inline void push (lua_State* L, bool value) + { + lua_pushboolean (L, value ? 1 : 0); + } + + static inline bool get (lua_State* L, int index) + { + return lua_toboolean (L, index) ? true : false; + } +}; + +//------------------------------------------------------------------------------ +/** + Stack specialization for `char`. +*/ +template <> +struct Stack +{ + static inline void push (lua_State* L, char value) + { + char str [2] = { value, 0 }; + lua_pushstring (L, str); + } + + static inline char get (lua_State* L, int index) + { + return luaL_checkstring (L, index) [0]; + } +}; + +template <> +struct Stack +{ + static inline void push (lua_State* L, char value) + { + char str [2] = { value, 0 }; + lua_pushstring (L, str); + } + + static inline char get (lua_State* L, int index) + { + return luaL_checkstring (L, index) [0]; + } +}; + +//------------------------------------------------------------------------------ +/** + Stack specialization for `float`. +*/ +template <> +struct Stack +{ + static inline void push (lua_State* L, char const* str) + { + if (str != 0) + lua_pushstring (L, str); + else + lua_pushnil (L); + } + + static inline char const* get (lua_State* L, int index) + { + return lua_isnil (L, index) ? 0 : luaL_checkstring (L, index); + } +}; + +//------------------------------------------------------------------------------ +/** + Stack specialization for `std::string`. +*/ +template <> +struct Stack +{ + static inline void push (lua_State* L, std::string const& str) + { + lua_pushlstring (L, str.c_str (), str.size()); + } + + static inline std::string get (lua_State* L, int index) + { + size_t len; + const char *str = luaL_checklstring(L, index, &len); + return std::string (str, len); + } +}; + +//------------------------------------------------------------------------------ +/** + Stack specialization for `std::string const&`. +*/ +template <> +struct Stack +{ + static inline void push (lua_State* L, std::string const& str) + { + lua_pushstring (L, str.c_str()); + } + + static inline std::string get (lua_State* L, int index) + { + size_t len; + const char *str = luaL_checklstring(L, index, &len); + return std::string (str, len); + } +}; diff --git a/src/lib/lua/bridge/detail/TypeList.h b/src/lib/lua/bridge/detail/TypeList.h new file mode 100644 index 00000000000..21f850e89df --- /dev/null +++ b/src/lib/lua/bridge/detail/TypeList.h @@ -0,0 +1,174 @@ +//------------------------------------------------------------------------------ +/* + https://github.com/vinniefalco/LuaBridge + + 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. + + This file incorporates work covered by the following copyright and + permission notice: + + The Loki Library + Copyright (c) 2001 by Andrei Alexandrescu + This code accompanies the book: + Alexandrescu, Andrei. "Modern C++ Design: Generic Programming and Design + Patterns Applied". Copyright (c) 2001. Addison-Wesley. + Permission to use, copy, modify, distribute and sell this software for any + purpose is hereby granted without fee, provided that the above copyright + notice appear in all copies and that both that copyright notice and this + permission notice appear in supporting documentation. + The author or Addison-Welsey Longman make no representations about the + suitability of this software for any purpose. It is provided "as is" + without express or implied warranty. +*/ +//============================================================================== + +/** + None type means void parameters or return value. +*/ +typedef void None; + +template +struct TypeList +{ +}; + +/** + A TypeList with actual values. +*/ +template +struct TypeListValues +{ + static std::string const tostring (bool) + { + return ""; + } +}; + +/** + TypeListValues recursive template definition. +*/ +template +struct TypeListValues > +{ + Head hd; + TypeListValues tl; + + TypeListValues (Head hd_, TypeListValues const& tl_) + : hd (hd_), tl (tl_) + { + } + + static std::string const tostring (bool comma = false) + { + std::string s; + + if (comma) + s = ", "; + + s = s + typeid (Head).name (); + + return s + TypeListValues ::tostring (true); + } +}; + +// Specializations of type/value list for head types that are references and +// const-references. We need to handle these specially since we can't count +// on the referenced object hanging around for the lifetime of the list. + +template +struct TypeListValues > +{ + Head hd; + TypeListValues tl; + + TypeListValues (Head& hd_, TypeListValues const& tl_) + : hd (hd_), tl (tl_) + { + } + + static std::string const tostring (bool comma = false) + { + std::string s; + + if (comma) + s = ", "; + + s = s + typeid (Head).name () + "&"; + + return s + TypeListValues ::tostring (true); + } +}; + +template +struct TypeListValues > +{ + Head hd; + TypeListValues tl; + + TypeListValues (Head const& hd_, const TypeListValues & tl_) + : hd (hd_), tl (tl_) + { + } + + static std::string const tostring (bool comma = false) + { + std::string s; + + if (comma) + s = ", "; + + s = s + typeid (Head).name () + " const&"; + + return s + TypeListValues ::tostring (true); + } +}; + +//============================================================================== +/** + Subclass of a TypeListValues constructable from the Lua stack. +*/ + +template +struct ArgList +{ +}; + +template +struct ArgList : public TypeListValues +{ + ArgList (lua_State*) + { + } +}; + +template +struct ArgList , Start> + : public TypeListValues > +{ + ArgList (lua_State* L) + : TypeListValues > (Stack ::get (L, Start), + ArgList (L)) + { + } +}; diff --git a/src/lib/lua/bridge/detail/TypeTraits.h b/src/lib/lua/bridge/detail/TypeTraits.h new file mode 100644 index 00000000000..5dd077194f1 --- /dev/null +++ b/src/lib/lua/bridge/detail/TypeTraits.h @@ -0,0 +1,125 @@ +//------------------------------------------------------------------------------ +/* + https://github.com/vinniefalco/LuaBridge + + Copyright 2012, Vinnie Falco + + 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. +*/ +//============================================================================== + +#ifndef LUABRIDGE_TYPEINFO_HEADER +#define LUABRIDGE_TYPEINFO_HEADER + +//------------------------------------------------------------------------------ +/** + Container traits. + + Unspecialized ContainerTraits has the isNotContainer typedef for SFINAE. + All user defined containers must supply an appropriate specialization for + ContinerTraits (without the typedef isNotContainer). The containers that + come with LuaBridge also come with the appropriate ContainerTraits + specialization. See the corresponding declaration for details. + + A specialization of ContainerTraits for some generic type ContainerType + looks like this: + + template + struct ContainerTraits > + { + typedef typename T Type; + + static T* get (ContainerType const& c) + { + return c.get (); // Implementation-dependent on ContainerType + } + }; +*/ +template +struct ContainerTraits +{ + typedef bool isNotContainer; +}; + +//------------------------------------------------------------------------------ +/** + Type traits. + + Specializations return information about a type. +*/ +struct TypeTraits +{ + /** Determine if type T is a container. + + To be considered a container, there must be a specialization of + ContainerTraits with the required fields. + */ + template + class isContainer + { + private: + typedef char yes[1]; // sizeof (yes) == 1 + typedef char no [2]; // sizeof (no) == 2 + + template + static no& test (typename C::isNotContainer*); + + template + static yes& test (...); + + public: + static const bool value = sizeof (test >(0)) == sizeof (yes); + }; + + /** Determine if T is const qualified. + */ + /** @{ */ + template + struct isConst + { + static bool const value = false; + }; + + template + struct isConst + { + static bool const value = true; + }; + /** @} */ + + /** Remove the const qualifier from T. + */ + /** @{ */ + template + struct removeConst + { + typedef T Type; + }; + + template + struct removeConst + { + typedef T Type; + }; + /**@}*/ +}; + +#endif diff --git a/src/lib/lua/bridge/detail/Userdata.h b/src/lib/lua/bridge/detail/Userdata.h new file mode 100644 index 00000000000..19451bd7d3e --- /dev/null +++ b/src/lib/lua/bridge/detail/Userdata.h @@ -0,0 +1,817 @@ +//------------------------------------------------------------------------------ +/* + https://github.com/vinniefalco/LuaBridge + + Copyright 2012, Vinnie Falco + + 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. +*/ +//============================================================================== + +//============================================================================== +/** + Return the identity pointer for our lightuserdata tokens. + + LuaBridge metatables are tagged with a security "token." The token is a + lightuserdata created from the identity pointer, used as a key in the + metatable. The value is a boolean = true, although any value could have been + used. + + Because of Lua's dynamic typing and our improvised system of imposing C++ + class structure, there is the possibility that executing scripts may + knowingly or unknowingly cause invalid data to get passed to the C functions + created by LuaBridge. In particular, our security model addresses the + following: + + Notes: + 1. Scripts cannot create a userdata (ignoring the debug lib). + 2. Scripts cannot create a lightuserdata (ignoring the debug lib). + 3. Scripts cannot set the metatable on a userdata. + 4. Our identity key is a unique pointer in the process. + 5. Our metatables have a lightuserdata identity key / value pair. + 6. Our metatables have "__metatable" set to a boolean = false. + 7. Our lightuserdata is unique. +*/ +inline void* getIdentityKey () +{ + static char value; + return &value; +} + +/** + Interface to a class pointer retrievable from a userdata. +*/ +class Userdata +{ +protected: + void* m_p; // subclasses must set this + + //-------------------------------------------------------------------------- + /** + Get an untyped pointer to the contained class. + */ + inline void* const getPointer () + { + return m_p; + } + +private: + //-------------------------------------------------------------------------- + /** + Validate and retrieve a Userdata on the stack. + + The Userdata must exactly match the corresponding class table or + const table, or else a Lua error is raised. This is used for the + __gc metamethod. + */ + static Userdata* getExactClass (lua_State* L, + int narg, + void const* classKey) + { + Userdata* ud = 0; + int const index = lua_absindex (L, narg); + + bool mismatch = false; + char const* got = 0; + + lua_rawgetp (L, LUA_REGISTRYINDEX, classKey); + assert (lua_istable (L, -1)); + + // Make sure we have a userdata. + if (!lua_isuserdata (L, index)) + mismatch = true; + + // Make sure it's metatable is ours. + if (!mismatch) + { + lua_getmetatable (L, index); + lua_rawgetp (L, -1, getIdentityKey ()); + if (lua_isboolean (L, -1)) + { + lua_pop (L, 1); + } + else + { + lua_pop (L, 2); + mismatch = true; + } + } + + if (!mismatch) + { + if (lua_rawequal (L, -1, -2)) + { + // Matches class table. + lua_pop (L, 2); + ud = static_cast (lua_touserdata (L, index)); + } + else + { + rawgetfield (L, -2, "__const"); + if (lua_rawequal (L, -1, -2)) + { + // Matches const table + lua_pop (L, 3); + ud = static_cast (lua_touserdata (L, index)); + } + else + { + // Mismatch, but its one of ours so get a type name. + rawgetfield (L, -2, "__type"); + lua_insert (L, -4); + lua_pop (L, 2); + got = lua_tostring (L, -2); + mismatch = true; + } + } + } + + if (mismatch) + { + rawgetfield (L, -1, "__type"); + assert (lua_type (L, -1) == LUA_TSTRING); + char const* const expected = lua_tostring (L, -1); + + if (got == 0) + got = lua_typename (L, lua_type (L, index)); + + char const* const msg = lua_pushfstring ( + L, "%s expected, got %s", expected, got); + + if (narg > 0) + luaL_argerror (L, narg, msg); + else + lua_error (L); + } + + return ud; + } + + //-------------------------------------------------------------------------- + /** + Validate and retrieve a Userdata on the stack. + + The Userdata must be derived from or the same as the given base class, + identified by the key. If canBeConst is false, generates an error if + the resulting Userdata represents to a const object. We do the type check + first so that the error message is informative. + */ + static Userdata* getClass (lua_State* L, + int index, + void const* baseClassKey, + bool canBeConst) + { + assert (index > 0); + Userdata* ud = 0; + + bool mismatch = false; + char const* got = 0; + + lua_rawgetp (L, LUA_REGISTRYINDEX, baseClassKey); + assert (lua_istable (L, -1)); + + // Make sure we have a userdata. + if (lua_isuserdata (L, index)) + { + // Make sure it's metatable is ours. + lua_getmetatable (L, index); + lua_rawgetp (L, -1, getIdentityKey ()); + if (lua_isboolean (L, -1)) + { + lua_pop (L, 1); + + // If __const is present, object is NOT const. + rawgetfield (L, -1, "__const"); + assert (lua_istable (L, -1) || lua_isnil (L, -1)); + bool const isConst = lua_isnil (L, -1); + lua_pop (L, 1); + + // Replace the class table with the const table if needed. + if (isConst) + { + rawgetfield (L, -2, "__const"); + assert (lua_istable (L, -1)); + lua_replace (L, -3); + } + + for (;;) + { + if (lua_rawequal (L, -1, -2)) + { + lua_pop (L, 2); + + // Match, now check const-ness. + if (isConst && !canBeConst) + { + luaL_argerror (L, index, "cannot be const"); + } + else + { + ud = static_cast (lua_touserdata (L, index)); + break; + } + } + else + { + // Replace current metatable with it's base class. + rawgetfield (L, -1, "__parent"); +/* +ud +class metatable +ud metatable +ud __parent (nil) +*/ + + if (lua_isnil (L, -1)) + { + lua_remove (L, -1); + // Mismatch, but its one of ours so get a type name. + rawgetfield (L, -1, "__type"); + lua_insert (L, -3); + lua_pop (L, 1); + got = lua_tostring (L, -2); + mismatch = true; + break; + } + else + { + lua_remove (L, -2); + } + } + } + } + else + { + lua_pop (L, 2); + mismatch = true; + } + } + else + { + mismatch = true; + } + + if (mismatch) + { + assert (lua_type (L, -1) == LUA_TTABLE); + rawgetfield (L, -1, "__type"); + assert (lua_type (L, -1) == LUA_TSTRING); + char const* const expected = lua_tostring (L, -1); + + if (got == 0) + got = lua_typename (L, lua_type (L, index)); + + char const* const msg = lua_pushfstring ( + L, "%s expected, got %s", expected, got); + + luaL_argerror (L, index, msg); + } + + return ud; + } + +public: + virtual ~Userdata () { } + + //-------------------------------------------------------------------------- + /** + Returns the Userdata* if the class on the Lua stack matches. + + If the class does not match, a Lua error is raised. + */ + template + static inline Userdata* getExact (lua_State* L, int index) + { + return getExactClass (L, index, ClassInfo ::getClassKey ()); + } + + //-------------------------------------------------------------------------- + /** + Get a pointer to the class from the Lua stack. + + If the object is not the class or a subclass, or it violates the + const-ness, a Lua error is raised. + */ + template + static inline T* get (lua_State* L, int index, bool canBeConst) + { + if (lua_isnil (L, index)) + return 0; + else + return static_cast (getClass (L, index, + ClassInfo ::getClassKey (), canBeConst)->getPointer ()); + } +}; + +//---------------------------------------------------------------------------- +/** + Wraps a class object stored in a Lua userdata. + + The lifetime of the object is managed by Lua. The object is constructed + inside the userdata using placement new. +*/ +template +class UserdataValue : public Userdata +{ +private: + UserdataValue (UserdataValue const&); + UserdataValue operator= (UserdataValue const&); + + char m_storage [sizeof (T)]; + + inline T* getObject () + { + // If this fails to compile it means you forgot to provide + // a Container specialization for your container! + // + return reinterpret_cast (&m_storage [0]); + } + +private: + /** + Used for placement construction. + */ + UserdataValue () + { + m_p = getObject (); + } + + ~UserdataValue () + { + getObject ()->~T (); + } + +public: + /** + Push a T via placement new. + + The caller is responsible for calling placement new using the + returned uninitialized storage. + */ + static void* place (lua_State* const L) + { + UserdataValue * const ud = new ( + lua_newuserdata (L, sizeof (UserdataValue ))) UserdataValue (); + lua_rawgetp (L, LUA_REGISTRYINDEX, ClassInfo ::getClassKey ()); + // If this goes off it means you forgot to register the class! + assert (lua_istable (L, -1)); + lua_setmetatable (L, -2); + return ud->getPointer (); + } + + /** + Push T via copy construction from U. + */ + template + static inline void push (lua_State* const L, U const& u) + { + new (place (L)) U (u); + } +}; + +//---------------------------------------------------------------------------- +/** + Wraps a pointer to a class object inside a Lua userdata. + + The lifetime of the object is managed by C++. +*/ +class UserdataPtr : public Userdata +{ +private: + UserdataPtr (UserdataPtr const&); + UserdataPtr operator= (UserdataPtr const&); + +private: + /** Push non-const pointer to object using metatable key. + */ + static void push (lua_State* L, void* const p, void const* const key) + { + if (p) + { + new (lua_newuserdata (L, sizeof (UserdataPtr))) UserdataPtr (p); + lua_rawgetp (L, LUA_REGISTRYINDEX, key); + // If this goes off it means you forgot to register the class! + assert (lua_istable (L, -1)); + lua_setmetatable (L, -2); + } + else + { + lua_pushnil (L); + } + } + + /** Push const pointer to object using metatable key. + */ + static void push (lua_State* L, void const* const p, void const* const key) + { + if (p) + { + new (lua_newuserdata (L, sizeof (UserdataPtr))) + UserdataPtr (const_cast (p)); + lua_rawgetp (L, LUA_REGISTRYINDEX, key); + // If this goes off it means you forgot to register the class! + assert (lua_istable (L, -1)); + lua_setmetatable (L, -2); + } + else + { + lua_pushnil (L); + } + } + + explicit UserdataPtr (void* const p) + { + m_p = p; + + // Can't construct with a null pointer! + // + assert (m_p != 0); + } + +public: + /** Push non-const pointer to object. + */ + template + static inline void push (lua_State* const L, T* const p) + { + if (p) + push (L, p, ClassInfo ::getClassKey ()); + else + lua_pushnil (L); + } + + /** Push const pointer to object. + */ + template + static inline void push (lua_State* const L, T const* const p) + { + if (p) + push (L, p, ClassInfo ::getConstKey ()); + else + lua_pushnil (L); + } +}; + +//============================================================================ +/** + Wraps a container thet references a class object. + + The template argument C is the container type, ContainerTraits must be + specialized on C or else a compile error will result. +*/ +template +class UserdataShared : public Userdata +{ +private: + UserdataShared (UserdataShared const&); + UserdataShared & operator= (UserdataShared const&); + + typedef typename TypeTraits::removeConst < + typename ContainerTraits ::Type>::Type T; + + C m_c; + +private: + ~UserdataShared () + { + } + +public: + /** + Construct from a container to the class or a derived class. + */ + template + explicit UserdataShared (U const& u) : m_c (u) + { + m_p = const_cast (reinterpret_cast ( + (ContainerTraits ::get (m_c)))); + } + + /** + Construct from a pointer to the class or a derived class. + */ + template + explicit UserdataShared (U* u) : m_c (u) + { + m_p = const_cast (reinterpret_cast ( + (ContainerTraits ::get (m_c)))); + } +}; + +//---------------------------------------------------------------------------- +// +// SFINAE helpers. +// + +// non-const objects +template +struct UserdataSharedHelper +{ + typedef typename TypeTraits::removeConst < + typename ContainerTraits ::Type>::Type T; + + static void push (lua_State* L, C const& c) + { + if (ContainerTraits ::get (c) != 0) + { + new (lua_newuserdata (L, sizeof (UserdataShared ))) UserdataShared (c); + lua_rawgetp (L, LUA_REGISTRYINDEX, ClassInfo ::getClassKey ()); + // If this goes off it means the class T is unregistered! + assert (lua_istable (L, -1)); + lua_setmetatable (L, -2); + } + else + { + lua_pushnil (L); + } + } + + static void push (lua_State* L, T* const t) + { + if (t) + { + new (lua_newuserdata (L, sizeof (UserdataShared ))) UserdataShared (t); + lua_rawgetp (L, LUA_REGISTRYINDEX, ClassInfo ::getClassKey ()); + // If this goes off it means the class T is unregistered! + assert (lua_istable (L, -1)); + lua_setmetatable (L, -2); + } + else + { + lua_pushnil (L); + } + } +}; + +// const objects +template +struct UserdataSharedHelper +{ + typedef typename TypeTraits::removeConst < + typename ContainerTraits ::Type>::Type T; + + static void push (lua_State* L, C const& c) + { + if (ContainerTraits ::get (c) != 0) + { + new (lua_newuserdata (L, sizeof (UserdataShared ))) UserdataShared (c); + lua_rawgetp (L, LUA_REGISTRYINDEX, ClassInfo ::getConstKey ()); + // If this goes off it means the class T is unregistered! + assert (lua_istable (L, -1)); + lua_setmetatable (L, -2); + } + else + { + lua_pushnil (L); + } + } + + static void push (lua_State* L, T* const t) + { + if (t) + { + new (lua_newuserdata (L, sizeof (UserdataShared ))) UserdataShared (t); + lua_rawgetp (L, LUA_REGISTRYINDEX, ClassInfo ::getConstKey ()); + // If this goes off it means the class T is unregistered! + assert (lua_istable (L, -1)); + lua_setmetatable (L, -2); + } + else + { + lua_pushnil (L); + } + } +}; + +/** + Pass by container. + + The container controls the object lifetime. Typically this will be a + lifetime shared by C++ and Lua using a reference count. Because of type + erasure, containers like std::shared_ptr will not work. Containers must + either be of the intrusive variety, or in the style of the RefCountedPtr + type provided by LuaBridge (that uses a global hash table). +*/ +template +struct StackHelper +{ + static inline void push (lua_State* L, C const& c) + { + UserdataSharedHelper ::Type>::value>::push (L, c); + } + + typedef typename TypeTraits::removeConst < + typename ContainerTraits ::Type>::Type T; + + static inline C get (lua_State* L, int index) + { + return Userdata::get (L, index, true); + } +}; + +/** + Pass by value. + + Lifetime is managed by Lua. A C++ function which accesses a pointer or + reference to an object outside the activation record in which it was + retrieved may result in undefined behavior if Lua garbage collected it. +*/ +template +struct StackHelper +{ + static inline void push (lua_State* L, T const& t) + { + UserdataValue ::push (L, t); + } + + static inline T const& get (lua_State* L, int index) + { + return *Userdata::get (L, index, true); + } +}; + +//============================================================================== + +/** + Lua stack conversions for class objects passed by value. +*/ +template +struct Stack +{ +public: + static inline void push (lua_State* L, T const& t) + { + StackHelper ::value>::push (L, t); + } + + static inline T get (lua_State* L, int index) + { + return StackHelper ::value>::get (L, index); + } +}; + +//------------------------------------------------------------------------------ +/** + Lua stack conversions for pointers and references to class objects. + + Lifetime is managed by C++. Lua code which remembers a reference to the + value may result in undefined behavior if C++ destroys the object. The + handling of the const and volatile qualifiers happens in UserdataPtr. +*/ + +// pointer +template +struct Stack +{ + static inline void push (lua_State* L, T* const p) + { + UserdataPtr::push (L, p); + } + + static inline T* const get (lua_State* L, int index) + { + return Userdata::get (L, index, false); + } +}; + +// Strips the const off the right side of * +template +struct Stack +{ + static inline void push (lua_State* L, T* const p) + { + UserdataPtr::push (L, p); + } + + static inline T* const get (lua_State* L, int index) + { + return Userdata::get (L, index, false); + } +}; + +// pointer to const +template +struct Stack +{ + static inline void push (lua_State* L, T const* const p) + { + UserdataPtr::push (L, p); + } + + static inline T const* const get (lua_State* L, int index) + { + return Userdata::get (L, index, true); + } +}; + +// Strips the const off the right side of * +template +struct Stack +{ + static inline void push (lua_State* L, T const* const p) + { + UserdataPtr::push (L, p); + } + + static inline T const* const get (lua_State* L, int index) + { + return Userdata::get (L, index, true); + } +}; + +// reference +template +struct Stack +{ + static inline void push (lua_State* L, T& t) + { + UserdataPtr::push (L, &t); + } + + static T& get (lua_State* L, int index) + { + T* const t = Userdata::get (L, index, false); + if (!t) + luaL_error (L, "nil passed to reference"); + return *t; + } +}; + +template +struct RefStackHelper +{ + typedef C return_type; + + static inline void push (lua_State* L, C const& t) + { + UserdataSharedHelper ::Type>::value>::push (L, t); + } + + typedef typename TypeTraits::removeConst < + typename ContainerTraits ::Type>::Type T; + + static return_type get (lua_State* L, int index) + { + return Userdata::get (L, index, true); + } +}; + +template +struct RefStackHelper +{ + typedef T const& return_type; + + static inline void push (lua_State* L, T const& t) + { + UserdataPtr::push (L, &t); + } + + static return_type get (lua_State* L, int index) + { + T const* const t = Userdata::get (L, index, true); + + if (!t) + luaL_error (L, "nil passed to reference"); + return *t; + } + +}; + +// reference to const +template +struct Stack +{ + typedef RefStackHelper ::value> helper_t; + + static inline void push (lua_State* L, T const& t) + { + helper_t::push (L, t); + } + + static typename helper_t::return_type get (lua_State* L, int index) + { + return helper_t::get (L, index); + } +}; diff --git a/src/lib/lua/bridge/detail/dump.h b/src/lib/lua/bridge/detail/dump.h new file mode 100644 index 00000000000..c0668035737 --- /dev/null +++ b/src/lib/lua/bridge/detail/dump.h @@ -0,0 +1,28 @@ +#include +#include + +std::string dumpLuaState(lua_State *L) { + std::stringstream ostr; + int i; + int top = lua_gettop(L); + ostr << "top=" << top << ":\n"; + for (i = 1; i <= top; ++i) { + int t = lua_type(L, i); + switch(t) { + case LUA_TSTRING: + ostr << " " << i << ": '" << lua_tostring(L, i) << "'\n"; + break; + case LUA_TBOOLEAN: + ostr << " " << i << ": " << + (lua_toboolean(L, i) ? "true" : "false") << "\n"; + break; + case LUA_TNUMBER: + ostr << " " << i << ": " << lua_tonumber(L, i) << "\n"; + break; + default: + ostr << " " << i << ": TYPE=" << lua_typename(L, t) << "\n"; + break; + } + } + return ostr.str(); +}