diff --git a/sol/demangle.hpp b/sol/demangle.hpp index af271ef2..129eb01a 100644 --- a/sol/demangle.hpp +++ b/sol/demangle.hpp @@ -35,19 +35,19 @@ namespace detail { std::string demangle(const std::type_info& id) { const static std::array removals = { "struct ", "class " }; const static std::array replacements = { "::", "_" }; - std::string realname = id.name( ); + std::string realname = id.name(); for(std::size_t r = 0; r < removals.size(); ++r) { auto found = realname.find(removals[r]); - while (found != std::string::npos) { + while (found != std::string::npos) { realname.erase(found, removals[r].size()); - found = realname.find( removals[r] ); - } + found = realname.find(removals[r]); + } } for(std::size_t r = 0; r < replacements.size(); r+=2) { auto found = realname.find(replacements[r]); - while (found != std::string::npos) { + while (found != std::string::npos) { realname.replace(found, replacements[r].size(), replacements[r+1]); - found = realname.find(replacements[r], found); + found = realname.find(replacements[r], found); } } return realname; diff --git a/sol/function_types.hpp b/sol/function_types.hpp index 4f855d6b..16f3f957 100644 --- a/sol/function_types.hpp +++ b/sol/function_types.hpp @@ -104,26 +104,41 @@ struct static_member_function { }; struct base_function { - static int call(lua_State* L) { - void** pinheritancedata = static_cast(stack::get(L, 1).value); - void* inheritancedata = *pinheritancedata; - if (inheritancedata == nullptr) { + static int base_call(lua_State* L, void* inheritancedata) { + if (inheritancedata == nullptr) throw sol_error("call from Lua to C++ function has null data"); - } base_function* pfx = static_cast(inheritancedata); base_function& fx = *pfx; int r = fx(L); return r; } - static int gc(lua_State* L) { - void** puserdata = static_cast(stack::get(L, 1).value); - void* userdata = *puserdata; - base_function* ptr = static_cast(userdata); + static int base_gc(lua_State* L, void* udata) { + if (udata == nullptr) + throw sol_error("call from lua to C++ gc function with null data"); + base_function* ptr = static_cast(udata); std::default_delete dx{}; dx(ptr); return 0; } + + static int call(lua_State* L) { + void** pinheritancedata = static_cast(stack::get(L, 1).value); + return base_call(L, *pinheritancedata); + } + + static int gc(lua_State* L) { + void** pudata = static_cast(stack::get(L, 1).value); + return base_gc(L, *pudata); + } + + template + struct userdata { + static int call(lua_State* L) { + // Zero-based template parameter, but upvalues start at 1 + return base_call(L, stack::get(L, i + 1)); + } + }; virtual int operator()(lua_State*) { throw sol_error("failure to call specialized wrapped C++ function from Lua"); @@ -220,9 +235,11 @@ struct userdata_function : public base_function { template functor(FxArgs&&... fxargs): item(nullptr), invocation(std::forward(fxargs)...) {} - void pre_call(lua_State* L) { - void* userdata = lua_touserdata(L, 0); - item = static_cast(userdata); + void prepare(lua_State* L) { + void* udata = stack::get(L, 1); + if (udata == nullptr) + throw sol_error("must use the syntax [object]:[function] to call member functions bound to C++"); + item = static_cast(udata); } template @@ -255,7 +272,7 @@ struct userdata_function : public base_function { } virtual int operator()(lua_State* L) override { - fx.pre_call(L); + fx.prepare(L); return (*this)(tuple_types(), typename traits_type::args_type(), L); } }; diff --git a/sol/proxy.hpp b/sol/proxy.hpp index 09097660..0dfc052d 100644 --- a/sol/proxy.hpp +++ b/sol/proxy.hpp @@ -24,7 +24,7 @@ #include "function.hpp" namespace sol { -template +template struct proxy { private: Table& tbl; @@ -32,7 +32,7 @@ private: public: - template + template proxy(Table& table, T&& key) : tbl(table), key(std::forward(key)) { } @@ -80,22 +80,22 @@ public: return get(); } - template + template operator bool() const { return get(); } - template + template operator double() const { return get(); } - template + template operator float() const { return get(); } - template + template operator int() const { return get(); } @@ -106,22 +106,22 @@ public: } }; -template +template inline bool operator== (T&& left, const proxy& right) { return left == right.template get>(); } -template +template inline bool operator== (const proxy& right, T&& left) { return right.template get>() == left; } -template +template inline bool operator!= (T&& left, const proxy& right) { return right.template get>() != left; } -template +template inline bool operator!= (const proxy& right, T&& left) { return right.template get>() != left; } diff --git a/sol/stack.hpp b/sol/stack.hpp index c948dbbd..94f40194 100644 --- a/sol/stack.hpp +++ b/sol/stack.hpp @@ -134,7 +134,12 @@ inline const char* get(lua_State* L, int index) { return lua_tostring(L, index); } -template +template<> +inline type get(lua_State* L, int index) { + return static_cast(lua_type(L, index)); +} + +template inline std::pair get_user(lua_State* L, int index = 1) { const static std::size_t data_t_count = (sizeof(T)+(sizeof(void*)-1)) / sizeof(void*); typedef std::array data_t; @@ -234,6 +239,15 @@ inline void push_tuple(lua_State* L, indices, T&& tuplen) { swallow {'\0', (sol::stack::push(L, std::get(tuplen)), '\0')... }; } +template +inline auto ltr_get(lua_State*, int index, F&& f, types, types<>, Vs&&... vs) -> decltype(f(std::forward(vs)...)) { + return f(std::forward(vs)...); +} +template +inline auto ltr_get(lua_State* L, int index, F&& f, types t, types, Vs&&... vs) -> decltype(f(std::declval()...)) { + return ltr_get(L, index + 1, std::forward(f), t, types(), std::forward(vs)..., get(L, index)); +} + template inline auto ltr_pop(lua_State*, F&& f, types, types<>, Vs&&... vs) -> decltype(f(std::forward(vs)...)) { return f(std::forward(vs)...); @@ -278,6 +292,11 @@ inline void push_reverse(lua_State* L, std::tuple&& tuplen) { detail::push_tuple(L, build_reverse_indices(), std::move(tuplen)); } +template +inline auto get_call(lua_State* L, int index, TFx&& fx, types t) -> decltype(detail::ltr_get(L, index, std::forward(fx), t, t)) { + return detail::ltr_get(L, index, std::forward(fx), t, t); +} + template inline auto pop_call(lua_State* L, TFx&& fx, types t) -> decltype(detail::ltr_pop(L, std::forward(fx), t, t)) { return detail::ltr_pop(L, std::forward(fx), t, t); @@ -298,6 +317,27 @@ inline void push_args(lua_State* L, Arg&& arg, Args&&... args) { stack::push(L, std::forward(arg)); void(swallow{'\0', (stack::push(L, std::forward(args)), '\0')... }); } + +inline call_syntax get_call_syntax(lua_State* L, const std::string& meta) { + if (get(L, 1) == type::table) { + if (luaL_newmetatable(L, meta.c_str()) == 0) { + lua_settop(L, -2); + return call_syntax::colon; + } + } + return call_syntax::dot; +} + +inline std::string dump_types(lua_State* L) { + std::string visual; + std::size_t size = lua_gettop(L) + 1; + for (std::size_t i = 1; i < size; ++i) { + if (i != 1) + visual += " | "; + visual += type_name(L, stack::get(L, i)); + } + return visual; +} } // stack } // sol diff --git a/sol/state.hpp b/sol/state.hpp index 80a27a79..3a0187cd 100644 --- a/sol/state.hpp +++ b/sol/state.hpp @@ -180,12 +180,12 @@ public: return reg; } - template + template proxy operator[](T&& key) { return global[std::forward(key)]; } - template + template proxy operator[](T&& key) const { return global[std::forward(key)]; } diff --git a/sol/table.hpp b/sol/table.hpp index 73611050..3cd67ad8 100644 --- a/sol/table.hpp +++ b/sol/table.hpp @@ -113,43 +113,19 @@ public: std::forward(key), std::forward(fx), std::forward(obj)); } - template + template table& set_class(userdata& user) { - push(); - - lua_createtable(state(), 0, 0); - int classid = lua_gettop(state()); - - // Register metatable for user data in registry - // using the metaname key generated from the demangled name luaL_newmetatable(state(), user.meta.c_str()); - int metaid = lua_gettop(state()); - // Meta functions: have no light up values - luaL_setfuncs(state(), user.metatable.data(), 0); - - // Regular functions: each one references an upvalue at its own index, - // resulting in [function count] upvalues - //luaL_newlib(L, functiontable.data()); - // the newlib macro doesn't have a form for when you need upvalues: - // we duplicate the work below - lua_createtable(state(), 0, user.functiontable.size() - 1); for (std::size_t upvalues = 0; upvalues < user.functions.size(); ++upvalues) { stack::push(state(), static_cast(user.functions[ upvalues ].get())); } luaL_setfuncs(state(), user.functiontable.data(), static_cast(user.functions.size())); - lua_setfield(state(), metaid, "__index"); - - // Meta functions: no upvalues - lua_createtable(state(), 0, user.metatable.size() - 1); - luaL_setfuncs(state(), user.metatable.data(), 0); // 0, for no upvalues - lua_setfield(state(), metaid, "__metatable"); - - lua_setmetatable(state(), classid); - + + lua_pushvalue(state(), -1); + lua_setfield(state(), -1, "__index"); + lua_setglobal(state(), user.luaname.c_str()); - - pop(); - + return *this; } @@ -158,12 +134,12 @@ public: return lua_rawlen(state(), -1); } - template + template proxy operator[](T&& key) { return proxy(*this, std::forward(key)); } - template + template proxy operator[](T&& key) const { return proxy(*this, std::forward(key)); } diff --git a/sol/tuple.hpp b/sol/tuple.hpp index 8b974b3f..81240bb3 100644 --- a/sol/tuple.hpp +++ b/sol/tuple.hpp @@ -74,9 +74,11 @@ struct tuple_types : types, std::false_type {}; template struct tuple_types> : types, std::true_type {}; -template +template struct constructors {}; +const auto default_constructor = constructors>{}; + } // sol #endif // SOL_TUPLE_HPP \ No newline at end of file diff --git a/sol/types.hpp b/sol/types.hpp index c4284d3e..476a8708 100644 --- a/sol/types.hpp +++ b/sol/types.hpp @@ -43,6 +43,11 @@ struct userdata_t { operator void* () const { return value; } }; +enum class call_syntax { + dot = 0, + colon = 1 +}; + enum class type : int { none = LUA_TNONE, nil = LUA_TNIL, @@ -69,7 +74,11 @@ inline void type_assert(lua_State* L, int index, type expected) { } } -template +inline std::string type_name(lua_State*L, type t) { + return lua_typename(L, static_cast(t)); +} + +template class userdata; class table; class function; diff --git a/sol/userdata.hpp b/sol/userdata.hpp index e898a51d..78388e42 100644 --- a/sol/userdata.hpp +++ b/sol/userdata.hpp @@ -35,7 +35,7 @@ inline std::unique_ptr make_unique(Args&&... args) { } } // detail -template +template class userdata { private: friend table; @@ -46,142 +46,105 @@ private: std::vector functionnames; std::vector> functions; std::vector functiontable; - std::vector metatable; - template + template struct constructor { - template - static void do_constructor(lua_State* L, T* obj, int argcount, types t) { - auto fx = [&obj] ( Args&&... args ) -> void { + template + static void do_constructor(lua_State* L, T* obj, call_syntax syntax, int argcount, types) { + auto fx = [&obj] (Args&&... args) -> void { std::allocator alloc{}; alloc.construct(obj, std::forward(args)...); }; - stack::pop_call(L, fx, t); - } + stack::get_call(L, 1 + static_cast(syntax), fx, types()); + } - static void match_constructor(lua_State* L, T* obj, int argcount) { - if (argcount != 0) - throw sol_error("No matching constructor for the arguments provided"); + static void match_constructor(lua_State* L, T* obj, call_syntax, int argcount) { + throw sol_error("No matching constructor for the arguments provided"); } - - template - static void match_constructor(lua_State* L, T* obj, int argcount, types t, Args&&... args) { + + template + static void match_constructor(lua_State* L, T* obj, call_syntax syntax, int argcount, types t, Args&&... args) { if (argcount == sizeof...(CArgs)) { - do_constructor( L, obj, argcount, t ); + do_constructor(L, obj, syntax, argcount, t); return; } - match_constructor( L, obj, argcount, std::forward(args)...); + match_constructor(L, obj, syntax, argcount, std::forward(args)...); } static int construct(lua_State* L) { + call_syntax syntax = stack::get_call_syntax(L, meta); int argcount = lua_gettop(L); - // First argument is now a table that represent the class to instantiate - luaL_checktype( L, 1, LUA_TTABLE ); - - // Table represents the instance - lua_createtable(L, 0, 0); - // Set first argument of new to metatable of instance - lua_pushvalue(L, 1); + + void* udata = lua_newuserdata(L, sizeof(T)); + T* obj = static_cast(udata); + match_constructor(L, obj, syntax, + argcount - static_cast(syntax), + std::common_type::type()...); + + luaL_getmetatable(L, meta.c_str()); lua_setmetatable(L, -2); - - // Do function lookups in metatable - lua_pushvalue(L, 1); - lua_setfield(L, 1, "__index"); - - - void* udata = lua_newuserdata( L, sizeof( T ) ); - T* obj = static_cast( udata ); - match_constructor( L, obj, argcount - 1, std::common_type::type( )... ); - //match_constructor( L, obj, argcount - 1, types( ) ); - - luaL_getmetatable(L, meta.c_str()); - lua_setmetatable(L, -2); - lua_setfield(L, -2, "__self"); - - return 1; + + return 1; } }; template struct destructor { static int destruct(lua_State* L) { - for(std::size_t i = 0; i < n; ++i) { - lightuserdata_t ludata = stack::get(L, i); - lua_func* func = static_cast(ludata.value); - std::default_delete dx{}; - dx(func); - } - - userdata_t udata = stack::get(L, 0); + /*for(std::size_t i = 0; i < n; ++i) { + base_function::base_gc(L, stack::get(L, i + 1)); + }*/ + userdata_t udata = stack::get(L, 1); T* obj = static_cast(udata.value); std::allocator alloc{}; alloc.destroy(obj); - return 0; } }; - template - struct class_func { - static int call(lua_State* L) { - // Zero-based template parameter, but upvalues start at 1 - void* inheritancedata = stack::get(L, i + 1); - if (inheritancedata == nullptr) - throw sol_error("call from Lua to C++ function has null data"); - base_function* pfx = static_cast(inheritancedata); - base_function& fx = *pfx; - int r = fx(L); - return r; - } - }; - - template + template void build_function_tables() { } - template + template void build_function_tables(Ret(T::* func)(MArgs...), std::string name, Args&&... args) { typedef typename std::decay::type fx_t; functionnames.push_back(std::move(name)); functions.emplace_back(detail::make_unique>(std::move(func))); - functiontable.push_back({ functionnames.back().c_str(), &class_func::call }); + functiontable.push_back({ functionnames.back().c_str(), &base_function::userdata::call }); build_function_tables(std::forward(args)...); } public: - template - userdata(Args&&... args) : userdata(classname, std::forward(args)...) {} + template + userdata(Args&&... args) : userdata(classname, default_constructor, std::forward(args)...) {} - template - userdata(std::string name, Args&&... args) : userdata(name, constructors>(), std::forward(args)...) {} - - template + template userdata(constructors c, Args&&... args) : userdata(classname, std::move(c), std::forward(args)...) {} - template + template userdata(std::string name, constructors, Args&&... args) : luaname(std::move(name)) { - functionnames.reserve(sizeof...(args)); - functiontable.reserve(sizeof...(args)); - functions.reserve(sizeof...(args)); - metatable.reserve(sizeof...(args)); + functionnames.reserve(sizeof...(args) + 2); + functiontable.reserve(sizeof...(args) + 3); + functions.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 }); + functiontable.push_back({ nullptr, nullptr }); - - metatable.push_back({ "__gc", &destructor::destruct }); - metatable.push_back({ nullptr, nullptr }); } void register_into(const table& s) { } }; -template +template const std::string userdata::classname = detail::demangle(typeid(T)); -template +template const std::string userdata::meta = std::string("sol.stateful.").append(classname); } diff --git a/tests.cpp b/tests.cpp index 298bd236..389f993e 100644 --- a/tests.cpp +++ b/tests.cpp @@ -36,6 +36,24 @@ struct fuser { } }; +namespace crapola { + struct fuser { + int x; + fuser( ) : x( 0 ) { + } + fuser( int x ) : x( x ) { + } + fuser( int x, int x2 ) : x( x * x2 ) { + } + int add( int y ) { + return x + y; + } + int add2( int y ) { + return x + y + 2; + } + }; +} + int plop_xyz(int x, int y, std::string z) { std::cout << x << " " << y << " " << z << std::endl; return 11; @@ -359,15 +377,59 @@ TEST_CASE("tables/userdata", "Show that we can create classes from userdata and sol::object a = lua.get("a"); sol::object b = lua.get("b"); sol::object c = lua.get("c"); - REQUIRE((a.is())); + REQUIRE((a.is())); auto atype = a.get_type(); auto btype = b.get_type(); auto ctype = c.get_type(); - REQUIRE((atype == sol::type::table)); + REQUIRE((atype == sol::type::userdata)); REQUIRE((btype == sol::type::number)); REQUIRE((ctype == sol::type::number)); int bresult = b.as(); int cresult = c.as(); REQUIRE(bresult == 1); REQUIRE(cresult == 3); +} + +TEST_CASE("tables/userdata constructors", "Show that we can create classes from userdata and use them with multiple destructors") { + + sol::state lua; + + sol::constructors, sol::types, sol::types> con; + sol::userdata lc(con, &crapola::fuser::add, "add", &crapola::fuser::add2, "add2"); + lua.set_class(lc); + + lua.script( + "a = crapola_fuser.new(2)\n" + "u = a:add(1)\n" + "v = a:add2(1)\n" + "b = crapola_fuser:new()\n" + "w = b:add(1)\n" + "x = b:add2(1)\n" + "c = crapola_fuser.new(2, 3)\n" + "y = c:add(1)\n" + "z = c:add2(1)\n" + ); + sol::object a = lua.get("a"); + auto atype = a.get_type(); + REQUIRE((atype == sol::type::userdata)); + sol::object u = lua.get("u"); + sol::object v = lua.get("v"); + REQUIRE((u.as() == 3)); + REQUIRE((v.as() == 5)); + + sol::object b = lua.get("b"); + auto btype = b.get_type(); + REQUIRE((btype == sol::type::userdata)); + sol::object w = lua.get("w"); + sol::object x = lua.get("x"); + REQUIRE((w.as() == 1)); + REQUIRE((x.as() == 3)); + + sol::object c = lua.get("c"); + auto ctype = c.get_type(); + REQUIRE((ctype == sol::type::userdata)); + sol::object y = lua.get("y"); + sol::object z = lua.get("z"); + REQUIRE((y.as() == 7)); + REQUIRE((z.as() == 9)); } \ No newline at end of file