diff --git a/sol/container.hpp b/sol/container.hpp index 576f5946..946ba3ea 100644 --- a/sol/container.hpp +++ b/sol/container.hpp @@ -5,10 +5,10 @@ namespace sol { -template +template struct container { typedef typename std::conditional::value, Tc, Decay>::type T; - typedef Decay().begin()))> value_type; + typedef Unqualified().begin()))> value_type; T cont; template @@ -20,26 +20,54 @@ struct container { return cont; } - void set( std::ptrdiff_t i, const value_type& value ) { + void set(std::ptrdiff_t i, const value_type& value) { cont[ i ] = value; } - value_type& get( std::ptrdiff_t i ) { + value_type& get(std::ptrdiff_t i) { return cont[ i ]; } - std::size_t size( ) const { + std::size_t size() const { return cont.size(); } }; +template +struct container>::value>::type> { + typedef typename std::conditional::value, Tc, Decay>::type T; + typedef Unqualified().begin())).first)> key_type; + typedef Unqualified().begin())).second)> value_type; + T cont; + + template + container (Args&&... args) : cont(std::forward(args)...){ + + } + + operator T& () const { + return cont; + } + + void set(key_type i, const value_type& value) { + cont[ i ] = value; + } + + value_type& get(key_type i) { + return cont.at(i); + } + + std::size_t size() const { + return cont.size(); + } +}; + namespace stack { template struct pusher::value>::type> { - template>> = 0> static void push(lua_State* L, const T& cont) { - typedef container container_t; + typedef container container_t; // todo: NEED to find a different way of handling this... static std::vector> classes{}; userdata classdata(default_constructor, @@ -55,19 +83,22 @@ struct pusher::value>::type> { std::allocator alloc{}; alloc.construct(c, cont); - auto&& meta = userdata_traits::metatable; - luaL_getmetatable(L, std::addressof(meta[0])); + auto&& meta = userdata_traits::metatable; + if (luaL_newmetatable(L, std::addressof(meta[0])) == 1) { + std::string err("metatable not defined for "); + err += meta; + throw error(err); + } lua_setmetatable(L, -2); } +}; - template> = 0> - static void push(lua_State* L, const T& cont) { - lua_createtable(L, cont.size(), 0); - for(auto&& pair : cont) { - pusher>::push(L, pair.first); - pusher>::push(L, pair.second); - lua_settable(L, -3); - } +template +struct getter::value>::type> { + static Unqualified& get(lua_State* L, int index = -1) { + typedef container> container_t; + container_t& data = stack::get(L, index); + return data.cont; } }; } // stack diff --git a/sol/function.hpp b/sol/function.hpp index dc3b842e..d81578c7 100644 --- a/sol/function.hpp +++ b/sol/function.hpp @@ -79,7 +79,7 @@ public: template typename return_type::type call(Args&&... args) const { push(); - stack::push(state(), std::forward(args)...); + stack::push_args(state(), std::forward(args)...); return invoke(types(), sizeof...(Args)); } }; @@ -166,7 +166,7 @@ struct pusher { lua_settable(L, -3); } - stack::detail::push_userdata(L, userdata, metatablename); + stack::detail::push_userdata(L, metatablename, userdata); stack::pusher{}.push(L, freefunc, 1); } diff --git a/sol/function_types.hpp b/sol/function_types.hpp index 195c06f1..2f6ae30e 100644 --- a/sol/function_types.hpp +++ b/sol/function_types.hpp @@ -288,7 +288,7 @@ struct userdata_function : public base_function { template> typename std::enable_if::value, void>::type push(lua_State* L, Return&& r) { - if ( detail::get_ptr(r) == fx.item ) { + if (detail::get_ptr(r) == fx.item) { // push nothing // note that pushing nothing with the ':' // syntax means we leave the instance of what @@ -331,6 +331,8 @@ struct userdata_function : public base_function { virtual int operator()(lua_State* L) override { fx.item = detail::get_ptr(stack::get(L, 1)); + if (fx.item == nullptr) + throw error("userdata for function call is null: are you using wrong call syntax? (use item:function(...) synax)"); return (*this)(tuple_types(), typename traits_type::args_type(), L); } }; diff --git a/sol/stack.hpp b/sol/stack.hpp index dbb8a251..900617b4 100644 --- a/sol/stack.hpp +++ b/sol/stack.hpp @@ -47,10 +47,11 @@ T* get_ptr(T* val) { namespace stack { namespace detail { -template> -inline void push_userdata(lua_State* L, T&& userdata, Key&& metatablekey) { - U* pdatum = static_cast(lua_newuserdata(L, sizeof(U))); - new(pdatum)U(std::forward(userdata)); +template +inline void push_userdata(lua_State* L, Key&& metatablekey, Args&&... args) { + T* pdatum = static_cast(lua_newuserdata(L, sizeof(T))); + std::allocator alloc{}; + alloc.construct(pdatum, std::forward(args)...); luaL_getmetatable(L, std::addressof(metatablekey[0])); lua_setmetatable(L, -2); } @@ -193,16 +194,53 @@ struct pusher { pusher{}.push(L, std::addressof(t)); } - template>, Not>, Not>> = 0> + template, EnableIf>, Not>, Not>> = 0> static void push(lua_State* L, T&& t) { - detail::push_userdata(L, std::move(t), userdata_traits::metatable); + detail::push_userdata(L, userdata_traits::metatable, std::move(t)); } }; template struct pusher { static void push(lua_State* L, T* obj) { - detail::push_userdata(L, obj, userdata_traits::metatable); + detail::push_userdata(L, userdata_traits::metatable, obj); + } +}; + +template <> +struct pusher { + template, EnableIf> = 0> + static void push_as(lua_State *L, T&& ref) { + ref.push(); + } + + template, EnableIf, Not>> = 0> + static void push_as(lua_State* L, const T& cont) { + lua_createtable(L, cont.size(), 0); + unsigned index = 1; + for(auto&& i : cont) { + // push the index + pusher{}.push(L, index++); + // push the value + pusher>{}.push(L, i); + // set the table + lua_settable(L, -3); + } + } + + template, EnableIf, has_key_value_pair> = 0> + static void push_as(lua_State* L, const T& cont) { + lua_createtable(L, cont.size(), 0); + for(auto&& pair : cont) { + pusher>{}.push(L, pair.first); + pusher>{}.push(L, pair.second); + lua_settable(L, -3); + } + } + + template + static void push(lua_State *L, T&& table) { + push_as(L, std::forward(table)); } }; @@ -284,8 +322,17 @@ inline void push(lua_State*) { template inline void push(lua_State* L, T&& t, Args&&... args) { - using swallow = char[]; + pusher>{}.push(L, std::forward(t), std::forward(args)...); +} + +inline void push_args(lua_State*) { + // do nothing +} + +template +inline void push_args(lua_State* L, T&& t, Args&&... args) { pusher>{}.push(L, std::forward(t)); + using swallow = char[]; void(swallow{'\0', (pusher>{}.push(L, std::forward(args)), '\0')... }); } diff --git a/sol/table.hpp b/sol/table.hpp index ddd7026a..b739ed26 100644 --- a/sol/table.hpp +++ b/sol/table.hpp @@ -232,7 +232,7 @@ private: } push(); - stack::detail::push_userdata(state(), userdata, metatablename); + stack::detail::push_userdata(state(), metatablename, userdata); luaL_setfuncs(state(), funcreg, 1); pop(); return *this; diff --git a/sol/userdata.hpp b/sol/userdata.hpp index d0fb300c..5ecfff15 100644 --- a/sol/userdata.hpp +++ b/sol/userdata.hpp @@ -26,6 +26,8 @@ #include "function_types.hpp" #include "userdata_traits.hpp" #include +#include +#include namespace sol { namespace detail { @@ -38,12 +40,17 @@ inline std::unique_ptr make_unique(Args&&... args) { template class userdata { private: + const static std::array metafunctionnames; std::string luaname; std::vector functionnames; std::vector> funcs; std::vector> ptrfuncs; + std::vector> metafuncs; + std::vector> ptrmetafuncs; std::vector functiontable; std::vector ptrfunctiontable; + std::vector metafunctiontable; + std::vector ptrmetafunctiontable; template struct constructor { @@ -79,7 +86,7 @@ private: match_constructor(L, obj, syntax, argcount - static_cast(syntax), typename std::common_type::type()...); luaL_getmetatable(L, std::addressof(meta[0])); - lua_setmetatable(L, -2); + lua_setmetatable(L, -1); return 1; } @@ -100,14 +107,24 @@ private: void build_function_tables() {} template - void build_function_tables(std::string name, Ret TBase::* func, Args&&... args) { + void build_function_tables(std::string funcname, Ret TBase::* func, Args&&... args) { static_assert(std::is_base_of::value, "Any registered function must be part of the class"); typedef typename std::decay::type function_type; - functionnames.push_back(std::move(name)); - funcs.emplace_back(detail::make_unique>(std::move(func))); - ptrfuncs.emplace_back(detail::make_unique::type>>(std::move(func))); - functiontable.push_back({ functionnames.back().c_str(), &base_function::userdata::call }); - ptrfunctiontable.push_back({ functionnames.back().c_str(), &base_function::userdata::call }); + functionnames.push_back(std::move(funcname)); + std::string& name = functionnames.back(); + auto metamethod = std::find(metafunctionnames.begin(), metafunctionnames.end(), name); + if (metamethod != metafunctionnames.end()) { + metafuncs.emplace_back(detail::make_unique>(std::move(func))); + ptrmetafuncs.emplace_back(detail::make_unique::type>>(std::move(func))); + metafunctiontable.push_back({ name.c_str(), &base_function::userdata::call }); + ptrmetafunctiontable.push_back({ name.c_str(), &base_function::userdata::call }); + } + else { + funcs.emplace_back(detail::make_unique>(std::move(func))); + ptrfuncs.emplace_back(detail::make_unique::type>>(std::move(func))); + functiontable.push_back({ name.c_str(), &base_function::userdata::call }); + ptrfunctiontable.push_back({ name.c_str(), &base_function::userdata::call }); + } build_function_tables(std::forward(args)...); } @@ -123,21 +140,27 @@ public: functionnames.reserve(sizeof...(args) + 2); functiontable.reserve(sizeof...(args) + 2); ptrfunctiontable.reserve(sizeof...(args) + 2); + metafunctiontable.reserve(sizeof...(args) + 2); + ptrmetafunctiontable.reserve(sizeof...(args) + 2); funcs.reserve(sizeof...(args) + 2); ptrfuncs.reserve(sizeof...(args) + 2); + metafuncs.reserve(sizeof...(args) + 2); + ptrmetafuncs.reserve(sizeof...(args) + 2); build_function_tables<0>(std::forward(args)...); functionnames.push_back("new"); functiontable.push_back({ functionnames.back().c_str(), &constructor::construct }); functionnames.push_back("__gc"); - functiontable.push_back({ functionnames.back().c_str(), &destructor::destruct }); + metafunctiontable.push_back({ functionnames.back().c_str(), &destructor::destruct }); // ptr_functions does not participate in garbage collection/new, // as all pointered types are considered // to be references. This makes returns of // `std::vector&` and `std::vector*` work functiontable.push_back({ nullptr, nullptr }); + metafunctiontable.push_back({ nullptr, nullptr }); ptrfunctiontable.push_back({ nullptr, nullptr }); + ptrmetafunctiontable.push_back({ nullptr, nullptr }); } template @@ -156,6 +179,14 @@ public: return ptrfuncs; } + const std::vector>& meta_functions () const { + return metafuncs; + } + + const std::vector>& meta_reference_functions () const { + return ptrmetafuncs; + } + const std::vector& function_table () const { return functiontable; } @@ -164,34 +195,65 @@ public: return ptrfunctiontable; } + const std::vector& meta_function_table () const { + return metafunctiontable; + } + + const std::vector& meta_reference_function_table () const { + return ptrmetafunctiontable; + } + const std::string& name () const { return luaname; } }; +template +const std::array userdata::metafunctionnames = { + "__index", + "__newindex", + "__mode", + "__call", + "__metatable", + "__tostring", + "__len", + "__gc", + "__unm", + "__add", + "__sub", + "__mul", + "__div", + "__mod", + "__pow", + "__concat", + "__eq", + "__lt", + "__le", +}; + namespace stack { template struct pusher> { - static void push ( lua_State* L, userdata& user ) { - auto&& ptrmeta = userdata_traits::type>::metatable; - luaL_newmetatable(L, ptrmeta.c_str()); - for (std::size_t upvalues = 0; upvalues < user.reference_functions().size(); ++upvalues) { - stack::push(L, static_cast(user.reference_functions()[ upvalues ].get())); - } - luaL_setfuncs(L, user.reference_function_table().data(), static_cast(user.reference_functions().size())); - + template + static void push_metatable(lua_State* L, Meta&& meta, Funcs&& funcs, FuncTable&& functable, MetaFuncs&& metafuncs, MetaFuncTable&& metafunctable) { + luaL_newmetatable(L, std::addressof(meta[0])); + // regular functions accessed through __index semantics lua_pushvalue(L, -1); lua_setfield(L, -1, "__index"); - - auto&& meta = userdata_traits::metatable; - luaL_newmetatable(L, meta.c_str()); - for (std::size_t upvalues = 0; upvalues < user.functions().size(); ++upvalues) { - stack::push(L, static_cast(user.functions()[ upvalues ].get())); + for (std::size_t u = 0; u < funcs.size(); ++u) { + stack::push(L, funcs[ u ].get()); } - luaL_setfuncs(L, user.function_table().data(), static_cast(user.functions().size())); + luaL_setfuncs(L, functable.data(), static_cast(funcs.size())); + // meta functions + for (std::size_t u = 0; u < metafuncs.size(); ++u) { + stack::push(L, metafuncs[ u ].get()); + } + luaL_setfuncs(L, metafunctable.data(), static_cast(metafuncs.size())); + } - lua_pushvalue(L, -1); - lua_setfield(L, -1, "__index"); + static void push (lua_State* L, userdata& user) { + push_metatable(L, userdata_traits::metatable, user.functions(), user.function_table(), user.meta_functions(), user.meta_function_table()); + push_metatable(L, userdata_traits::metatable, user.reference_functions(), user.reference_function_table(), user.meta_reference_functions(), user.meta_reference_function_table()); } }; } // stack diff --git a/tests.cpp b/tests.cpp index b073a4dc..63fdbce8 100644 --- a/tests.cpp +++ b/tests.cpp @@ -14,14 +14,14 @@ void test_free_func2(std::function f, int arg1) { throw sol::error("failed function call!"); } -std::function makefn () { +std::function makefn() { auto fx = []() -> int { return 0x1456789; }; return fx; } -void takefn ( std::function purr ) { +void takefn(std::function purr) { if (purr() != 0x1456789) throw 0; } @@ -332,7 +332,7 @@ TEST_CASE("tables/functions_variables", "Check if tables and function calls work std::cout << "stateless lambda()" << std::endl; return "test"; } - ); + ); REQUIRE_NOTHROW(run_script(lua)); lua.get("os").set_function("fun", &free_function); @@ -389,7 +389,7 @@ TEST_CASE("functions/sol::function to std::function", "check if conversion to st lua.set_function("testFunc2", test_free_func2); lua.script( "testFunc(function() print(\"hello std::function\") end)" - ); + ); lua.script( "function m(a)\n" " print(\"hello std::function with arg \", a)\n" @@ -397,18 +397,18 @@ TEST_CASE("functions/sol::function to std::function", "check if conversion to st "end\n" "\n" "testFunc2(m, 1)" - ); + ); } TEST_CASE("functions/returning functions from C++ and getting in lua", "check to see if returning a functor and getting a functor from lua is possible") { sol::state lua; lua.open_libraries(sol::lib::base); - lua.set_function( "makefn", makefn ); - lua.set_function( "takefn", takefn ); - lua.script( "afx = makefn()\n" + lua.set_function("makefn", makefn); + lua.set_function("takefn", takefn); + lua.script("afx = makefn()\n" "print(afx())\n" - "takefn(afx)\n" ); + "takefn(afx)\n"); } TEST_CASE("tables/operator[]", "Check if operator[] retrieval and setting works properly") { @@ -591,7 +591,7 @@ TEST_CASE("tables/self-referential userdata", "userdata classes must play nice w "local a = test.new()\n" "a:g(\"woof\")\n" "a:f(a)\n" - ); + ); } TEST_CASE("tables/arbitrary-creation", "tables should be created from standard containers") {