From 019c7b037b90db798faaeee4385699a72aaacc85 Mon Sep 17 00:00:00 2001 From: ThePhD Date: Sun, 21 Feb 2016 19:26:58 -0500 Subject: [PATCH] Huge improvements to the library and fixes to compile in g++. usertype now respects factory functions and does not make default constructors/destructors unless the compiler says its okay new and __gc functions can be overridden for usertypes to provide handle-like creation and deletion functions Overloading match fixes RAII improvements for all usertypes Added tests to make sure these features stay --- sol/function.hpp | 12 +- sol/function_result.hpp | 4 +- sol/function_types.hpp | 1 + sol/function_types_allocator.hpp | 119 +++++- sol/function_types_core.hpp | 125 +++--- sol/function_types_overload.hpp | 67 ++-- sol/function_types_usertype.hpp | 33 +- sol/object.hpp | 2 +- sol/overload.hpp | 16 +- sol/raii.hpp | 84 ++++ sol/stack.hpp | 119 +++--- sol/state_view.hpp | 11 +- sol/table_core.hpp | 16 +- sol/traits.hpp | 23 +- sol/tuple.hpp | 20 +- sol/types.hpp | 6 +- sol/usertype.hpp | 645 +++++++++++++++++-------------- tests.cpp | 149 +++++-- 18 files changed, 884 insertions(+), 568 deletions(-) create mode 100644 sol/raii.hpp diff --git a/sol/function.hpp b/sol/function.hpp index 9ad46e8f..8bf68bcb 100644 --- a/sol/function.hpp +++ b/sol/function.hpp @@ -44,9 +44,9 @@ private: auto invoke( types, std::index_sequence, std::ptrdiff_t n ) const { luacall( n, sizeof...( Ret ) ); int stacksize = lua_gettop( lua_state( ) ); - int firstreturn = std::max(1, stacksize - static_cast(sizeof...(Ret)) + 1); + int firstreturn = std::max(1, stacksize - static_cast(sizeof...(Ret)) + 1); auto r = stack::get>( lua_state( ), firstreturn ); - lua_pop(lua_state(), static_cast(sizeof...(Ret))); + lua_pop(lua_state(), static_cast(sizeof...(Ret))); return r; } @@ -140,8 +140,8 @@ private: luacall(n, sizeof...(Ret), h); int stacksize = lua_gettop(lua_state()); int firstreturn = std::max(0, stacksize - static_cast(sizeof...(Ret)) + 1); - auto r = stack::get>(lua_state(), firstreturn); - lua_pop(lua_state(), static_cast(sizeof...(Ret))); + auto r = stack::get>(lua_state(), firstreturn); + lua_pop(lua_state(), static_cast(sizeof...(Ret))); return r; } @@ -323,7 +323,7 @@ struct pusher> { int metapushed = luaL_newmetatable(L, metatablename); if(metapushed == 1) { lua_pushstring(L, "__gc"); - stack::push(L, detail::gc); + stack::push(L, detail::gc); lua_settable(L, -3); lua_pop(L, 1); } @@ -365,7 +365,7 @@ struct pusher> { template static int push(std::index_sequence, lua_State* L, Set&& set) { pusher>{}.set_fx(L, std::make_unique>(std::get(set)...)); - return 1; + return 1; } template diff --git a/sol/function_result.hpp b/sol/function_result.hpp index 7f2896dc..9a064902 100644 --- a/sol/function_result.hpp +++ b/sol/function_result.hpp @@ -68,7 +68,7 @@ public: } ~function_result() { - lua_pop(L, returncount); + lua_pop(L, returncount); } }; @@ -101,7 +101,7 @@ public: L = o.L; index = o.index; returncount = o.returncount; - popcount = o.popcount; + popcount = o.popcount; error = o.error; // Must be manual, otherwise destructor will screw us // return count being 0 is enough to keep things clean diff --git a/sol/function_types.hpp b/sol/function_types.hpp index 6c4653a8..c7f45311 100644 --- a/sol/function_types.hpp +++ b/sol/function_types.hpp @@ -28,5 +28,6 @@ #include "function_types_member.hpp" #include "function_types_usertype.hpp" #include "function_types_overload.hpp" +#include "function_types_allocator.hpp" #endif // SOL_FUNCTION_TYPES_HPP diff --git a/sol/function_types_allocator.hpp b/sol/function_types_allocator.hpp index eb467826..724d62fe 100644 --- a/sol/function_types_allocator.hpp +++ b/sol/function_types_allocator.hpp @@ -22,9 +22,126 @@ #ifndef SOL_FUNCTION_TYPES_ALLOCATOR_HPP #define SOL_FUNCTION_TYPES_ALLOCATOR_HPP -#include "stack.hpp" +#include "raii.hpp" +#include "function_types_overload.hpp" namespace sol { +namespace detail { +template +struct void_call; + +template +struct void_call> { + static void call(Args... args) {} +}; + +template +struct constructor_match { + T* obj; + + constructor_match(T* obj) : obj(obj) {} + + template + int operator()(Bool, types, Index, types r, types a, lua_State* L, int, int start) const { + default_construct func{}; + return stack::typed_call(r, a, func, L, start, obj); + } +}; +} // detail + +template +inline int construct(Match&& matchfx, lua_State* L, int fxarity, int start) { + // use same overload resolution matching as all other parts of the framework + return overload_match_arity::call)...>(std::forward(matchfx), L, fxarity, start); +} + +template +inline int construct(lua_State* L) { + static const auto& meta = usertype_traits::metatable; + call_syntax syntax = stack::get_call_syntax(L, meta); + int argcount = lua_gettop(L) - static_cast(syntax); + + T** pointerpointer = reinterpret_cast(lua_newuserdata(L, sizeof(T*) + sizeof(T))); + T*& referencepointer = *pointerpointer; + T* obj = reinterpret_cast(pointerpointer + 1); + referencepointer = obj; + reference userdataref(L, -1); + userdataref.pop(); + + construct(detail::constructor_match(obj), L, argcount, 1 + static_cast(syntax)); + + userdataref.push(); + luaL_getmetatable(L, &meta[0]); + if (stack::get(L) == type::nil) { + lua_pop(L, 1); + std::string err = "unable to get usertype metatable for "; + err += meta; + throw error(err); + } + + lua_setmetatable(L, -2); + return 1; +} + +template +inline int destruct(lua_State* L) { + userdata udata = stack::get(L, 1); + // The first sizeof(T*) bytes are the reference: the rest is + // the actual data itself (if there is a reference at all) + T** pobj = reinterpret_cast(udata.value); + T*& obj = *pobj; + std::allocator alloc{}; + alloc.destroy(obj); + return 0; +} + +template +struct usertype_constructor_function : base_function { + typedef std::tuple overload_list; + typedef std::index_sequence_for indices; + overload_list overloads; + + usertype_constructor_function(constructor_wrapper set) : usertype_constructor_function(indices(), set) {} + + template + usertype_constructor_function(std::index_sequence, constructor_wrapper set) : usertype_constructor_function(std::get(set)...) {} + + usertype_constructor_function(Functions... fxs) : overloads(fxs...) {} + + template + int call(Bool, types, Index, types r, types a, lua_State* L, int, int start) { + static const auto& meta = usertype_traits::metatable; + T** pointerpointer = reinterpret_cast(lua_newuserdata(L, sizeof(T*) + sizeof(T))); + T*& referencepointer = *pointerpointer; + T* obj = reinterpret_cast(pointerpointer + 1); + referencepointer = obj; + reference userdataref(L, -1); + userdataref.pop(); + + auto& func = std::get(overloads); + stack::typed_call(r, a, func, L, start, detail::implicit_wrapper(obj)); + + userdataref.push(); + luaL_getmetatable(L, &meta[0]); + if (stack::get(L) == type::nil) { + lua_pop(L, 1); + std::string err = "unable to get usertype metatable for "; + err += meta; + throw error(err); + } + lua_setmetatable(L, -2); + + return 1; + } + + virtual int operator()(lua_State* L) override { + static const auto& meta = usertype_traits::metatable; + call_syntax syntax = stack::get_call_syntax(L, meta); + int argcount = lua_gettop(L) - static_cast(syntax); + auto mfx = [&](auto&&... args) { return this->call(std::forward(args)...); }; + return construct>...>(mfx, L, argcount, 1 + static_cast(syntax)); + } +}; } // sol #endif // SOL_FUNCTION_TYPES_ALLOCATOR_HPP diff --git a/sol/function_types_core.hpp b/sol/function_types_core.hpp index bcaa06f9..50bffe8f 100644 --- a/sol/function_types_core.hpp +++ b/sol/function_types_core.hpp @@ -31,6 +31,7 @@ struct ref_call_t {} const ref_call = ref_call_t{}; template struct implicit_wrapper { T& item; + implicit_wrapper(T* item) : item(*item) {} implicit_wrapper(T& item) : item(item) {} operator T& () { return item; @@ -49,13 +50,13 @@ function_packer function_pack( Args&&... args ) { } inline bool check_types(types<>, std::index_sequence<>, lua_State*, int) { - return true; + return true; } template inline bool check_types(types, std::index_sequence, lua_State* L, int start = 1) { if (!stack::check(L, start + I, no_panic)) - return false; + return false; return check_types(types(), std::index_sequence(), L, start); } @@ -129,7 +130,7 @@ struct functor::va template struct functor::value || std::is_class::value>> { typedef callable_traits traits_type; - typedef remove_one_type args_type; + typedef pop_front_type_t args_type; typedef typename traits_type::return_type return_type; typedef std::tuple_element_t<0, typename traits_type::args_tuple_type> Arg0; typedef std::conditional_t::value || std::is_class::value, Func, std::add_pointer_t> function_type; @@ -183,68 +184,68 @@ struct base_function { }; namespace detail { - static int base_call(lua_State* L, void* inheritancedata) { - if (inheritancedata == nullptr) { - throw error("call from Lua to C++ function has null data"); - } +static int base_call(lua_State* L, void* inheritancedata) { + if (inheritancedata == nullptr) { + throw 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 base_gc(lua_State*, void* udata) { - if (udata == nullptr) { - throw 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; - } - - template - static void func_gc(std::true_type, lua_State*) { - - } - - template - static void func_gc(std::false_type, lua_State* L) { - // Shut up clang tautological error without throwing out std::size_t - for (std::size_t i = 0; i < limit; ++i) { - upvalue up = stack::get(L, static_cast(i + 1)); - base_function* obj = static_cast(up.value); - std::allocator alloc{}; - alloc.destroy(obj); - alloc.deallocate(obj, 1); - } - } - - inline int call(lua_State* L) { - void** pinheritancedata = static_cast(stack::get(L, 1).value); - return base_call(L, *pinheritancedata); - } - - inline int gc(lua_State* L) { - void** pudata = static_cast(stack::get(L, 1).value); - return base_gc(L, *pudata); - } - - template - inline int usertype_call(lua_State* L) { - // Zero-based template parameter, but upvalues start at 1 - return base_call(L, stack::get(L, I + 1)); - } - - template - inline int usertype_gc(lua_State* L) { - func_gc(Bool<(I < 1)>(), L); - return 0; - } + base_function* pfx = static_cast(inheritancedata); + base_function& fx = *pfx; + int r = fx(L); + return r; } +static int base_gc(lua_State*, void* udata) { + if (udata == nullptr) { + throw 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; +} + +template +static void func_gc(std::true_type, lua_State*) { + +} + +template +static void func_gc(std::false_type, lua_State* L) { + for (std::size_t i = 0; i < limit; ++i) { + upvalue up = stack::get(L, static_cast(i + 1)); + if (up.value == nullptr) + continue; + base_function* obj = static_cast(up.value); + std::allocator alloc{}; + alloc.destroy(obj); + alloc.deallocate(obj, 1); + } +} + +inline int call(lua_State* L) { + void** pinheritancedata = static_cast(stack::get(L, 1).value); + return base_call(L, *pinheritancedata); +} + +inline int gc(lua_State* L) { + void** pudata = static_cast(stack::get(L, 1).value); + return base_gc(L, *pudata); +} + +template +inline int usertype_call(lua_State* L) { + // Zero-based template parameter, but upvalues start at 1 + return base_call(L, stack::get(L, I + 1)); +} + +template +inline int usertype_gc(lua_State* L) { + func_gc(Bool<(I < 1)>(), L); + return 0; +} +} // detail } // sol #endif // SOL_FUNCTION_TYPES_CORE_HPP diff --git a/sol/function_types_overload.hpp b/sol/function_types_overload.hpp index a79bebbb..24e0de70 100644 --- a/sol/function_types_overload.hpp +++ b/sol/function_types_overload.hpp @@ -28,45 +28,46 @@ namespace sol { namespace detail { -template -inline int match_arity(types<>, std::index_sequence<>, std::index_sequence, std::ptrdiff_t, lua_State*, Match&&, int) { +template +inline int overload_match_arity(types<>, std::index_sequence<>, std::index_sequence, Match&&, lua_State*, int, int, Args&&...) { throw error("no matching function call takes this number of arguments and the specified types"); } -template -inline int match_arity(types, std::index_sequence, std::index_sequence, std::ptrdiff_t fxarity, lua_State* L, Match&& matchfx, int start) { +template +inline int overload_match_arity(types, std::index_sequence, std::index_sequence, Match&& matchfx, lua_State* L, int fxarity, int start, Args&&... args) { typedef function_traits traits; typedef tuple_types::return_type> return_types; typedef typename function_traits::args_type args_type; typedef typename args_type::indices args_indices; // compile-time eliminate any functions that we know ahead of time are of improper arity - if (find_in_pack_v, Index...>::value || traits::arity != fxarity) { - return match_arity(types(), std::index_sequence(), std::index_sequence(), fxarity, L, std::forward(matchfx), start); + if (find_in_pack_v, Index...>::value) { + return overload_match_arity(types(), std::index_sequence(), std::index_sequence(), std::forward(matchfx), L, fxarity, start, std::forward(args)...); } if (traits::arity != fxarity) { - return match_arity(types(), std::index_sequence(), std::index_sequence(), fxarity, L, std::forward(matchfx), start); + return overload_match_arity(types(), std::index_sequence(), std::index_sequence(), std::forward(matchfx), L, fxarity, start, std::forward(args)...); } - if (!detail::check_types(args_type(), args_indices(), L, start)) { - return match_arity(types(), std::index_sequence(), std::index_sequence(), fxarity, L, std::forward(matchfx), start); + if (sizeof...(Fxs) != 0 && !detail::check_types(args_type(), args_indices(), L, start)) { + return overload_match_arity(types(), std::index_sequence(), std::index_sequence(), std::forward(matchfx), L, fxarity, start, std::forward(args)...); } - return matchfx(Index(), fxarity, L, start); + return matchfx(Bool(), types(), Index(), return_types(), args_type(), L, fxarity, start, std::forward(args)...); } } // detail -template -inline int match(std::ptrdiff_t fxarity, lua_State* L, Match&& matchfx, int start = 1) { - return detail::match_arity(types(), std::index_sequence_for(), std::index_sequence<>(), fxarity, L, std::forward(matchfx), start); +template +inline int overload_match_arity(Match&& matchfx, lua_State* L, int fxarity, int start = 1, Args&&... args) { + return detail::overload_match_arity(types(), std::index_sequence_for(), std::index_sequence<>(), std::forward(matchfx), L, fxarity, start, std::forward(args)...); } -template -inline int match(lua_State* L, Match&& matchfx, int start = 1) { - std::ptrdiff_t fxarity = lua_gettop(L) - (start - 1); - return match(fxarity, L, std::forward(matchfx), start); +template +inline int overload_match(Match&& matchfx, lua_State* L, int start = 1, Args&&... args) { + int fxarity = lua_gettop(L) - (start - 1); + return overload_match_arity(std::forward(matchfx), L, fxarity, start, std::forward(args)...); } template struct overloaded_function : base_function { typedef std::tuple overload_list; + typedef std::index_sequence_for indices; overload_list overloads; overloaded_function(overload_set set) @@ -74,26 +75,22 @@ struct overloaded_function : base_function { template overloaded_function(std::index_sequence, overload_set set) - : overloaded_function(std::get(set)...) {} + : overloaded_function(std::get(set)...) {} overloaded_function(Functions... fxs) : overloads(fxs...) { } - template - int call(Index, int, lua_State* L, int) { - auto& func = std::get(overloads); - typedef Unqualified Fx; - typedef function_traits traits; - typedef tuple_types::return_type> return_types; - typedef typename function_traits::args_type args_type; - return stack::typed_call(return_types(), args_type(), func, L); + template + int call(Bool, types, Index, types r, types a, lua_State* L, int, int start) { + auto& func = std::get(overloads); + return stack::typed_call(r, a, func, L, start); } virtual int operator()(lua_State* L) override { - auto mfx = [&](auto&&... args){ return call(std::forward(args)...); }; - return match(L, mfx); + auto mfx = [&](auto&&... args){ return this->call(std::forward(args)...); }; + return overload_match(mfx, L); } }; @@ -110,20 +107,16 @@ struct usertype_overloaded_function : base_function { usertype_overloaded_function(Functions... fxs) : overloads(fxs...) {} - template - int call(Index, int, lua_State* L, int) { + template + int call(Bool, types, Index, types r, types a, lua_State* L, int, int start) { auto& func = std::get(overloads); - typedef Unqualified Fx; - typedef typename Fx::traits_type traits; - typedef tuple_types return_types; - typedef typename Fx::args_type args_type; func.item = ptr(stack::get(L, 1)); - return stack::typed_call(return_types(), args_type(), func, L); + return stack::typed_call(r, a, func, L, start); } virtual int operator()(lua_State* L) override { - auto mfx = [&](auto&&... args){ return call(std::forward(args)...); }; - return match(L, mfx, 2); + auto mfx = [&](auto&&... args){ return this->call(std::forward(args)...); }; + return overload_match(mfx, L, 2); } }; diff --git a/sol/function_types_usertype.hpp b/sol/function_types_usertype.hpp index e0dec933..87c9235a 100644 --- a/sol/function_types_usertype.hpp +++ b/sol/function_types_usertype.hpp @@ -62,18 +62,17 @@ struct usertype_function_core : public base_function { return stack::push(L, std::forward(r)); } - template - int operator()(types tr, types ta, lua_State* L) { - //static const std::size_t skew = static_cast(std::is_member_object_pointer::value); - stack::call(tr, ta, L, 0, fx); + template + int operator()(types tr, types ta, Index, lua_State* L) { + stack::call(tr, ta, L, static_cast(Start), fx); int nargs = static_cast(sizeof...(Args)); lua_pop(L, nargs); return 0; } - template - int operator()(types tr, types ta, lua_State* L) { - decltype(auto) r = stack::call(tr, ta, L, 0, fx); + template + int operator()(types tr, types ta, Index, lua_State* L) { + decltype(auto) r = stack::call(tr, ta, L, static_cast(Start), fx); int nargs = static_cast(sizeof...(Args)); lua_pop(L, nargs); int pushcount = push(L, std::forward(r)); @@ -97,7 +96,7 @@ struct usertype_function : public usertype_function_core { if(this->fx.item == nullptr) { throw error("userdata for function call is null: are you using the wrong syntax? (use item:function/variable(...) syntax)"); } - return static_cast(*this)(tuple_types(), args_type(), L); + return static_cast(*this)(tuple_types(), args_type(), Index<2>(), L); } virtual int operator()(lua_State* L) override { @@ -118,15 +117,15 @@ struct usertype_variable_function : public usertype_function_core int prelude(lua_State* L) { int argcount = lua_gettop(L); - this->fx.item = stack::get(L, 1); + this->fx.item = stack::get(L, 1); if(this->fx.item == nullptr) { throw error("userdata for member variable is null"); } switch(argcount) { case 2: - return static_cast(*this)(tuple_types(), types<>(), L); + return static_cast(*this)(tuple_types(), types<>(), Index<2>(), L); case 3: - return static_cast(*this)(tuple_types(), args_type(), L); + return static_cast(*this)(tuple_types(), args_type(), Index<3>(), L); default: throw error("cannot get/set userdata member variable with inappropriate number of arguments"); } @@ -150,15 +149,15 @@ struct usertype_indexing_function : base_function { auto functionpair = functions.find(accessor); if (functionpair != functions.end()) { std::pair& target = functionpair->second; - if (target.first) { + if (target.first) { stack::push(L, target.second); stack::push(L, c_closure(detail::usertype_call<0>, 1)); - return 1; - } + return 1; + } return (*target.second)(L); - } - base_function& core = *original; - return core(L); + } + base_function& core = *original; + return core(L); } virtual int operator()(lua_State* L) override { diff --git a/sol/object.hpp b/sol/object.hpp index 7fd15db1..4b913662 100644 --- a/sol/object.hpp +++ b/sol/object.hpp @@ -43,7 +43,7 @@ public: template bool is() const { if (!reference::valid()) - return false; + return false; auto expected = type_of(); auto actual = get_type(); return (expected == actual) || (expected == type::poly); diff --git a/sol/overload.hpp b/sol/overload.hpp index ae2da711..e5cf7b2f 100644 --- a/sol/overload.hpp +++ b/sol/overload.hpp @@ -25,15 +25,15 @@ #include namespace sol { - template - struct overload_set : std::tuple { - using std::tuple::tuple; - }; + template + struct overload_set : std::tuple { + using std::tuple::tuple; + }; - template - decltype(auto) overload(Args&&... args) { - return overload_set(std::forward(args)...); - } + template + decltype(auto) overload(Args&&... args) { + return overload_set(std::forward(args)...); + } } #endif // SOL_OVERLOAD_HPP \ No newline at end of file diff --git a/sol/raii.hpp b/sol/raii.hpp new file mode 100644 index 00000000..61521d75 --- /dev/null +++ b/sol/raii.hpp @@ -0,0 +1,84 @@ +// The MIT License (MIT) + +// Copyright (c) 2013-2016 Rapptz and contributors + +// 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 SOL_RAII_HPP +#define SOL_RAII_HPP + +#include +#include "traits.hpp" + +namespace sol { +struct default_construct { + template + void operator()(T&& obj, Args&&... args) const { + std::allocator> alloc{}; + alloc.construct(obj, std::forward(args)...); + } +}; + +template +struct placement_construct { + T obj; + + template + placement_construct( Args&&... args ) : obj(std::forward(args)...) {} + + template + void operator()(Args&&... args) const { + default_construct{}(obj, std::forward(args)...); + } +}; + +template +using constructors = sol::types; + +const auto default_constructor = constructors>{}; + +template +struct constructor_wrapper : std::tuple { + using std::tuple::tuple; +}; + +template +constructor_wrapper constructor(Functions&&... functions) { + return constructor_wrapper(std::forward(functions)...); +} + +template +struct destructor_wrapper { + Function fx; + template + destructor_wrapper(Args&&... args) : fx(std::forward(args)...) {} +}; + +template <> +struct destructor_wrapper {}; + +const destructor_wrapper default_destructor{}; + +template +inline destructor_wrapper destructor(Fx&& fx) { + return destructor_wrapper(std::forward(fx)); +} + +} // sol + +#endif // SOL_RAII_HPP diff --git a/sol/stack.hpp b/sol/stack.hpp index 96433739..fc7bde78 100644 --- a/sol/stack.hpp +++ b/sol/stack.hpp @@ -241,7 +241,7 @@ struct checker { if (indextype == type::nil) { return true; } - return checker{}.check(L, indextype, index, handler); + return checker{}.check(L, indextype, index, handler); } }; @@ -251,36 +251,36 @@ struct checker { static bool check (lua_State* L, type indextype, int index, const Handler& handler) { if (indextype != type::userdata) { handler(L, index, type::userdata, indextype); - return false; + return false; } - if (lua_getmetatable(L, index) == 0) { - handler(L, index, type::userdata, indextype); - return false; - } - luaL_getmetatable(L, &usertype_traits::metatable[0]); + if (lua_getmetatable(L, index) == 0) { + handler(L, index, type::userdata, indextype); + return false; + } + luaL_getmetatable(L, &usertype_traits::metatable[0]); const type expectedmetatabletype = get(L); - if (expectedmetatabletype == type::nil) { - lua_pop(L, 2); - handler(L, index, type::userdata, indextype); - return false; - } - bool success = lua_rawequal(L, -1, -2) == 1; - lua_pop(L, 2); - return success; + if (expectedmetatabletype == type::nil) { + lua_pop(L, 2); + handler(L, index, type::userdata, indextype); + return false; + } + bool success = lua_rawequal(L, -1, -2) == 1; + lua_pop(L, 2); + return success; } template static bool check (lua_State* L, int index, const Handler& handler) { const type indextype = type_of(L, index); - return check(L, indextype, index, handler); + return check(L, indextype, index, handler); } }; template struct getter { - static T& get(lua_State* L, int index = -1) { - return getter{}.get(L, index); - } + static T& get(lua_State* L, int index = -1) { + return getter{}.get(L, index); + } }; template @@ -317,9 +317,9 @@ struct getter { type t = type_of(L, index); if (t == type::nil) return nullptr; - void* udata = lua_touserdata(L, index); - T** obj = static_cast(udata); - return *obj; + void* udata = lua_touserdata(L, index); + T** obj = static_cast(udata); + return *obj; } }; @@ -467,7 +467,7 @@ struct pusher::value>> { static int push(lua_State* L, const T& value) { lua_pushnumber(L, value); return 1; - } + } }; template @@ -539,7 +539,7 @@ struct pusher> { template<> struct pusher { - static int push(lua_State* L, const bool& b) { + static int push(lua_State* L, bool b) { lua_pushboolean(L, b); return 1; } @@ -547,12 +547,20 @@ struct pusher { template<> struct pusher { - static int push(lua_State* L, const nil_t&) { + static int push(lua_State* L, nil_t) { lua_pushnil(L); return 1; } }; +template<> +struct pusher> { + static int push(lua_State* L, lua_CFunction func, int n = 0) { + lua_pushcclosure(L, func, n); + return 1; + } +}; + template<> struct pusher { static int push(lua_State* L, lua_CFunction func, int n = 0) { @@ -597,7 +605,7 @@ template<> struct pusher { static int push(lua_State* L, userdata data) { void** ud = static_cast(lua_newuserdata(L, sizeof(void*))); - *ud = data.value; + *ud = data.value; return 1; } }; @@ -648,10 +656,10 @@ struct field_getter, b, C> { template void apply(std::index_sequence, lua_State* L, Key&& key, int tableindex) { get_field(L, std::get(key), tableindex); - detail::swallow{ (get_field(L, std::get(key)), 0)... }; + detail::swallow{ (get_field(L, std::get(key)), 0)... }; reference saved(L, -1); - lua_pop(L, static_cast(sizeof...(I) + 1)); - saved.push(); + lua_pop(L, static_cast(sizeof...(I) + 1)); + saved.push(); } template @@ -701,7 +709,7 @@ struct field_setter::value>> { template void set(lua_State* L, Key&& key, Value&& value, int = -2) { push(L, std::forward(value)); - lua_setglobal(L, &key[0]); + lua_setglobal(L, &key[0]); } }; @@ -759,10 +767,9 @@ template struct check_arguments { template static bool check(types, std::index_sequence, lua_State* L, int firstargument) { - bool checks = true; - if (!stack::check(L, firstargument + I0)) - return false; - return check(types(), std::index_sequence(), L, firstargument); + if (!stack::check(L, firstargument + I0)) + return false; + return check(types(), std::index_sequence(), L, firstargument); } static bool check(types<>, std::index_sequence<>, lua_State*, int) { @@ -780,24 +787,14 @@ struct check_arguments { template ::value>> inline R call(types, types ta, std::index_sequence tai, lua_State* L, int start, Fx&& fx, FxArgs&&... args) { - const int stacksize = lua_gettop(L); - const int firstargument = static_cast(start + stacksize - std::max(sizeof...(Args)-1, static_cast(0))); - - check_arguments{}.check(ta, tai, L, firstargument); - - return fx(std::forward(args)..., stack::get(L, firstargument + I)...); + check_arguments{}.check(ta, tai, L, start); + return fx(std::forward(args)..., stack::get(L, start + I)...); } template inline void call(types, types ta, std::index_sequence tai, lua_State* L, int start, Fx&& fx, FxArgs&&... args) { - const int stacksize = lua_gettop(L); - const int firstargument = static_cast(start + stacksize - std::max(sizeof...(Args)-1, static_cast(0))); - - bool checks = check_arguments{}.check(ta, tai, L, firstargument); - if ( !checks ) - throw error("Arguments not of the proper types for this function call"); - - fx(std::forward(args)..., stack::get(L, firstargument + I)...); + check_arguments{}.check(ta, tai, L, start); + fx(std::forward(args)..., stack::get(L, start + I)...); } } // stack_detail @@ -831,7 +828,12 @@ inline R call(types tr, types ta, lua_State* L, int start, Fx&& fx, template ::value>> inline R call(types tr, types ta, lua_State* L, Fx&& fx, FxArgs&&... args) { - return call(tr, ta, L, 0, std::forward(fx), std::forward(args)...); + return call(tr, ta, L, 1, std::forward(fx), std::forward(args)...); +} + +template ::value>> +inline R top_call(types tr, types ta, lua_State* L, Fx&& fx, FxArgs&&... args) { + return call(tr, ta, L, static_cast(lua_gettop(L) - sizeof...(Args)), std::forward(fx), std::forward(args)...); } template @@ -842,20 +844,25 @@ inline void call(types tr, types ta, lua_State* L, int start, Fx& template inline void call(types tr, types ta, lua_State* L, Fx&& fx, FxArgs&&... args) { - call(tr, ta, L, 0, std::forward(fx), std::forward(args)...); + call(tr, ta, L, 1, std::forward(fx), std::forward(args)...); } -template -inline int typed_call(types tr, types ta, Fx&& fx, lua_State* L, int start = 0) { - call(tr, ta, L, start, fx); +template +inline void top_call(types tr, types ta, lua_State* L, Fx&& fx, FxArgs&&... args) { + call(tr, ta, L, static_cast(lua_gettop(L) - sizeof...(Args)), std::forward(fx), std::forward(args)...); +} + +template +inline int typed_call(types tr, types ta, Fx&& fx, lua_State* L, int start = 1, FxArgs&&... fxargs) { + call(tr, ta, L, start, fx, std::forward(fxargs)...); int nargs = static_cast(sizeof...(Args)); lua_pop(L, nargs); return 0; } -template -inline int typed_call(types, types ta, Fx&& fx, lua_State* L, int start = 0) { - decltype(auto) r = call(types>(), ta, L, start, fx); +template>::value>> +inline int typed_call(types, types ta, Fx&& fx, lua_State* L, int start = 1, FxArgs&&... fxargs) { + decltype(auto) r = call(types>(), ta, L, start, fx, std::forward(fxargs)...); int nargs = static_cast(sizeof...(Args)); lua_pop(L, nargs); return push(L, std::forward(r)); diff --git a/sol/state_view.hpp b/sol/state_view.hpp index 7d5fa86b..1e9ebe65 100644 --- a/sol/state_view.hpp +++ b/sol/state_view.hpp @@ -177,9 +177,16 @@ public: return *this; } - template + template state_view& new_usertype(const std::string& name, Args&&... args) { - constructors> ctor{}; + usertype utype(std::forward(args)...); + set_usertype(name, utype); + return *this; + } + + template + state_view& new_usertype(const std::string& name, Args&&... args) { + constructors> ctor{}; return new_usertype(name, ctor, std::forward(args)...); } diff --git a/sol/table_core.hpp b/sol/table_core.hpp index d30611a2..d8d0ec9a 100644 --- a/sol/table_core.hpp +++ b/sol/table_core.hpp @@ -44,8 +44,8 @@ class table_core : public reference { auto tuple_get( types, std::index_sequence, Keys&& keys ) const -> decltype(stack::pop>(nullptr)){ auto pp = stack::push_popper(keys))...>::value>(*this); - int tableindex = lua_gettop(lua_state()); - detail::swallow{ ( stack::get_field(lua_state(), std::get(keys), tableindex), 0)... }; + int tableindex = lua_gettop(lua_state()); + detail::swallow{ ( stack::get_field(lua_state(), std::get(keys), tableindex), 0)... }; return stack::pop>( lua_state() ); } @@ -71,7 +71,7 @@ class table_core : public reference { template decltype(auto) traverse_get_deep( Key&& key, Keys&&... keys ) const { stack::get_field( lua_state( ), std::forward( key ) ); - return traverse_get_deep(std::forward(keys)...); + return traverse_get_deep(std::forward(keys)...); } template @@ -105,16 +105,16 @@ public: template decltype(auto) traverse_get( Keys&&... keys ) const { auto pp = stack::push_popper::value>(*this); - struct clean { lua_State* L; clean(lua_State* L) : L(L) {} ~clean() { lua_pop(L, static_cast(sizeof...(Keys))); } } c(lua_state()); - return traverse_get_deep(std::forward(keys)...); + struct clean { lua_State* L; clean(lua_State* L) : L(L) {} ~clean() { lua_pop(L, static_cast(sizeof...(Keys))); } } c(lua_state()); + return traverse_get_deep(std::forward(keys)...); } template table_core& traverse_set( Keys&&... keys ) { auto pp = stack::push_popper::value>(*this); - traverse_set_deep(std::forward(keys)...); - lua_pop(lua_state(), static_cast(sizeof...(Keys)-2)); - return *this; + traverse_set_deep(std::forward(keys)...); + lua_pop(lua_state(), static_cast(sizeof...(Keys)-2)); + return *this; } template diff --git a/sol/traits.hpp b/sol/traits.hpp index 5499a4d8..16b48424 100644 --- a/sol/traits.hpp +++ b/sol/traits.hpp @@ -60,7 +60,7 @@ struct remove_member_pointer { template struct remove_member_pointer { - typedef R type; + typedef R type; }; template @@ -125,6 +125,15 @@ struct find_in_pack_v : Bool { }; template struct find_in_pack_v : Or, find_in_pack_v> { }; +template +struct at_in_pack {}; + +template +using at_in_pack_t = typename at_in_pack::type; + +template +struct at_in_pack : std::conditional> {}; + template struct return_type { typedef std::tuple type; @@ -143,12 +152,6 @@ struct return_type<> { template using return_type_t = typename return_type::type; -template -using ReturnTypeOr = typename std::conditional<(sizeof...(Args) < 1), Empty, return_type_t>::type; - -template -using ReturnType = ReturnTypeOr; - namespace detail { template>::value> @@ -377,17 +380,17 @@ T& deref(const std::shared_ptr& item) { template inline T* ptr(T& val) { - return std::addressof(val); + return std::addressof(val); } template inline T* ptr(std::reference_wrapper val) { - return std::addressof(val.get()); + return std::addressof(val.get()); } template inline T* ptr(T* val) { - return val; + return val; } namespace detail { diff --git a/sol/tuple.hpp b/sol/tuple.hpp index c749e234..4bdabf88 100644 --- a/sol/tuple.hpp +++ b/sol/tuple.hpp @@ -30,15 +30,6 @@ template struct types { typedef std::index_sequence_for indices; static constexpr std::size_t size() { return sizeof...(Args); } }; namespace detail { -template -struct chop_one : types<> {}; - -template -struct chop_one> : types {}; - -template -struct chop_one> : types {}; - template struct tuple_types_ { typedef types type; }; @@ -53,12 +44,15 @@ template using tuple_types = typename detail::tuple_types_::type; template -struct remove_one_type : detail::chop_one {}; +struct pop_front_type; + +template +using pop_front_type_t = typename pop_front_type::type; + +template +struct pop_front_type> { typedef types type; }; -template -using constructors = sol::types; -const auto default_constructor = constructors>{}; } // sol diff --git a/sol/types.hpp b/sol/types.hpp index 4070c1ab..a933e206 100644 --- a/sol/types.hpp +++ b/sol/types.hpp @@ -57,9 +57,9 @@ struct userdata { }; struct c_closure { - lua_CFunction c_function; - int upvalues; - c_closure(lua_CFunction f, int upvalues = 0) : c_function(f), upvalues(upvalues) {} + lua_CFunction c_function; + int upvalues; + c_closure(lua_CFunction f, int upvalues = 0) : c_function(f), upvalues(upvalues) {} }; enum class call_syntax { diff --git a/sol/usertype.hpp b/sol/usertype.hpp index c9652c96..2b69d641 100644 --- a/sol/usertype.hpp +++ b/sol/usertype.hpp @@ -25,7 +25,7 @@ #include "state.hpp" #include "function_types.hpp" #include "usertype_traits.hpp" -#include "default_construct.hpp" +#include "raii.hpp" #include "deprecate.hpp" #include #include @@ -33,334 +33,379 @@ #include namespace sol { - const std::array meta_variable_names = { { - "__index", - "__newindex", - } }; +const std::array meta_variable_names = { { + "__index", + "__newindex", +} }; - const std::array meta_function_names = { { - "new", - "__index", - "__newindex", - "__mode", - "__call", - "__metatable", - "__tostring", - "__len", - "__unm", - "__add", - "__sub", - "__mul", - "__div", - "__mod", - "__pow", - "__concat", - "__eq", - "__lt", - "__le", - "__gc", - "__call", - } }; +const std::array meta_function_names = { { + "new", + "__index", + "__newindex", + "__mode", + "__call", + "__metatable", + "__tostring", + "__len", + "__unm", + "__add", + "__sub", + "__mul", + "__div", + "__mod", + "__pow", + "__concat", + "__eq", + "__lt", + "__le", + "__gc", + "__call", +} }; - enum class meta_function { - construct, - index, - new_index, - mode, - call, - metatable, - to_string, - length, - unary_minus, - addition, - subtraction, - multiplication, - division, - modulus, - power_of, - involution = power_of, - concatenation, - equal_to, - less_than, - less_than_or_equal_to, - garbage_collect, - call_function, - }; +enum class meta_function { + construct, + index, + new_index, + mode, + call, + metatable, + to_string, + length, + unary_minus, + addition, + subtraction, + multiplication, + division, + modulus, + power_of, + involution = power_of, + concatenation, + equal_to, + less_than, + less_than_or_equal_to, + garbage_collect, + call_function, +}; - namespace usertype_detail { - template - inline void push_metatable(lua_State* L, Funcs&& funcs, FuncTable&&, MetaFuncTable&& metafunctable) { - luaL_newmetatable(L, &usertype_traits::metatable[0]); - int metatableindex = lua_gettop(L); - if (funcs.size() < 1 && metafunctable.size() < 2) { - return; - } - // Metamethods directly on the metatable itself - int metaup = stack::stack_detail::push_upvalues(L, funcs); - if (std::is_pointer::value) { - // Insert nullptr before new/gc methods for pointer types; - // prevents calling new/GC on pointer-based tables. - luaL_Reg& oldref = metafunctable[metafunctable.size() - 3]; - luaL_Reg old = oldref; - oldref = { nullptr, nullptr }; - luaL_setfuncs(L, metafunctable.data(), metaup); - oldref = old; - } - else { - luaL_setfuncs(L, metafunctable.data(), metaup); - } - } +namespace usertype_detail { +struct add_destructor_tag {}; +struct check_destructor_tag {}; +struct verified_tag {} const verified {}; - template - inline void set_global_deleter(lua_State* L, lua_CFunction cleanup, Functions&& metafunctions) { - // Automatic deleter table -- stays alive until lua VM dies - // even if the user calls collectgarbage(), weirdly enough - lua_createtable(L, 0, 0); - lua_createtable(L, 0, 1); - int up = stack::stack_detail::push_upvalues(L, metafunctions); - lua_pushcclosure(L, cleanup, up); - lua_setfield(L, -2, "__gc"); - lua_setmetatable(L, -2); - // gctable name by default has ♻ part of it - lua_setglobal(L, &usertype_traits::gc_table[0]); - } +template +struct is_constructor : std::false_type {}; - template - static void do_constructor(lua_State* L, T* obj, call_syntax syntax, int, types) { - default_construct fx{}; - stack::call(types(), types(), L, -1 + static_cast(syntax), fx, obj); - } +template +struct is_constructor> : std::true_type {}; - template - static void match_constructor(lua_State*, T*, call_syntax, int) { - throw error("No matching constructor for the arguments provided"); - } +template +struct is_constructor> : std::true_type {}; - 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, syntax, argcount, t); - return; - } - match_constructor(L, obj, syntax, argcount, std::forward(args)...); - } - } +template +using has_constructor = Or>...>; - template - class usertype { - private: - typedef std::map> function_map_t; - std::vector functionnames; - std::vector> functions; - std::vector functiontable; - std::vector metafunctiontable; - base_function* indexfunc; - base_function* newindexfunc; - function_map_t indexwrapper, newindexwrapper; - base_function* constructfunc; - base_function* destructfunc; - lua_CFunction functiongc; +template +struct is_destructor : std::false_type {}; - template - struct constructor { - static int construct(lua_State* L) { - const auto& meta = usertype_traits::metatable; - call_syntax syntax = stack::get_call_syntax(L, meta); - int argcount = lua_gettop(L); +template +struct is_destructor> : std::true_type {}; - T** pointerpointer = reinterpret_cast(lua_newuserdata(L, sizeof(T*) + sizeof(T))); - T*& referencepointer = *pointerpointer; - T* obj = reinterpret_cast(pointerpointer + 1); - referencepointer = obj; - int userdataindex = lua_gettop(L); - usertype_detail::match_constructor(L, obj, syntax, argcount - static_cast(syntax), identity_t()...); +template +using has_destructor = Or>...>; - if (luaL_newmetatable(L, &meta[0]) == 1) { - lua_pop(L, 1); - std::string err = "unable to get usertype metatable for "; - err += meta; - throw error(err); - } - lua_setmetatable(L, userdataindex); +template +inline void push_metatable(lua_State* L, bool needsindexfunction, Funcs&& funcs, FuncTable&& functable, MetaFuncTable&& metafunctable) { + static const auto& gcname = meta_function_names[static_cast(meta_function::garbage_collect)]; + luaL_newmetatable(L, &usertype_traits::metatable[0]); + int metatableindex = lua_gettop(L); + if (funcs.size() < 1 && metafunctable.size() < 2) { + return; + } + // Metamethods directly on the metatable itself + int metaup = stack::stack_detail::push_upvalues(L, funcs); + if (refmeta && gcname == metafunctable[metafunctable.size()-2].name) { + // We can just "clip" out the __gc function, + // which we always put as the last entry in the meta function table. + luaL_Reg& target = metafunctable[metafunctable.size() - 2]; + luaL_Reg old = target; + target = { nullptr, nullptr }; + luaL_setfuncs(L, metafunctable.data(), metaup); + target = old; + } + else { + // Otherwise, just slap it in there. + luaL_setfuncs(L, metafunctable.data(), metaup); + } + if (needsindexfunction) { + // We don't need to do anything more + // since we've already bound the __index field using + // setfuncs above... + return; + } + // Otherwise, we use quick, fast table indexing for methods + // gives us performance boost in calling them + lua_createtable(L, 0, functable.size()); + int up = stack::stack_detail::push_upvalues(L, funcs); + luaL_setfuncs(L, functable.data(), up); + lua_setfield(L, metatableindex, "__index"); + return; +} - return 1; - } - }; +template +inline void set_global_deleter(lua_State* L, lua_CFunction cleanup, Functions&& functions) { + // Automatic deleter table -- stays alive until lua VM dies + // even if the user calls collectgarbage(), weirdly enough + lua_createtable(L, 0, 0); // global table that sits at toplevel + lua_createtable(L, 0, 1); // metatable for the global table + int up = stack::stack_detail::push_upvalues(L, functions); + stack::set_field(L, "__gc", c_closure(cleanup, up)); + lua_setmetatable(L, -2); + // gctable name by default has ♻ part of it + lua_setglobal(L, &usertype_traits::gc_table[0]); +} +} - static int destruct(lua_State* L) { - userdata udata = stack::get(L, 1); - // The first sizeof(T*) bytes are the reference: the rest is - // the actual data itself (if there is a reference at all) - T** pobj = reinterpret_cast(udata.value); - T*& obj = *pobj; - std::allocator alloc{}; - alloc.destroy(obj); - return 0; - } +template +class usertype { +private: + typedef std::map> function_map_t; + std::vector functionnames; + std::vector> functions; + std::vector functiontable; + std::vector metafunctiontable; + base_function* indexfunc; + base_function* newindexfunc; + function_map_t indexwrapper, newindexwrapper; + lua_CFunction constructfunc; + const char* destructfuncname; + lua_CFunction destructfunc; + lua_CFunction functiongcfunc; + bool needsindexfunction; - template - std::unique_ptr make_function(const std::string&, overload_set func) { - return std::make_unique>(func); - } + template + std::unique_ptr make_function(const std::string&, overload_set func) { + return std::make_unique>(std::move(func)); + } - template - std::unique_ptr make_function(const std::string&, Ret(*func)(Arg, Args...)) { - typedef Unqualified> Argu; - static_assert(std::is_base_of::value, "Any non-member-function must have a first argument which is covariant with the desired userdata type."); - typedef std::decay_t function_type; - return std::make_unique>(func); - } + template + std::unique_ptr make_function(const std::string&, constructor_wrapper func) { + return std::make_unique>(std::move(func)); + } - template - std::unique_ptr make_variable_function(std::true_type, const std::string&, Ret Base::* func) { - static_assert(std::is_base_of::value, "Any registered function must be part of the class"); - typedef std::decay_t function_type; - return std::make_unique>(func); - } + template + std::unique_ptr make_function(const std::string&, Ret(*func)(Arg, Args...)) { + typedef Unqualified> Argu; + static_assert(std::is_base_of::value, "Any non-member-function must have a first argument which is covariant with the desired userdata type."); + typedef std::decay_t function_type; + return std::make_unique>(func); + } - template - std::unique_ptr make_variable_function(std::false_type, const std::string&, Ret Base::* func) { - static_assert(std::is_base_of::value, "Any registered function must be part of the class"); - typedef std::decay_t function_type; - return std::make_unique>(func); - } + template + std::unique_ptr make_variable_function(std::true_type, const std::string&, Ret Base::* func) { + static_assert(std::is_base_of::value, "Any registered function must be part of the class"); + typedef std::decay_t function_type; + return std::make_unique>(func); + } - template - std::unique_ptr make_function(const std::string& name, Ret Base::* func) { - typedef std::decay_t function_type; - return make_variable_function(std::is_member_object_pointer(), name, func); - } + template + std::unique_ptr make_variable_function(std::false_type, const std::string&, Ret Base::* func) { + static_assert(std::is_base_of::value, "Any registered function must be part of the class"); + typedef std::decay_t function_type; + return std::make_unique>(func); + } - template - std::unique_ptr make_function(const std::string&, Fx&& func) { - typedef Unqualified Fxu; - typedef std::tuple_element_t<0, typename function_traits::args_tuple_type> Arg0; - typedef Unqualified> Argu; - static_assert(std::is_base_of::value, "Any non-member-function must have a first argument which is covariant with the desired usertype."); - typedef std::decay_t function_type; - return std::make_unique>(func); - } + template + std::unique_ptr make_function(const std::string& name, Ret Base::* func) { + typedef std::decay_t function_type; + return make_variable_function(std::is_member_object_pointer(), name, func); + } - template - void build_function(std::string funcname, Fx&& func) { - typedef std::is_member_object_pointer> is_variable; - typedef std::decay_t function_type; - functionnames.push_back(std::move(funcname)); - std::string& name = functionnames.back(); - auto baseptr = make_function(name, std::forward(func)); - functions.emplace_back(std::move(baseptr)); - if (is_variable::value) { - indexwrapper.insert({ name, { false, functions.back().get() } }); - newindexwrapper.insert({ name, { false, functions.back().get() } }); - return; - } - auto metamethodfind = std::find(meta_function_names.begin(), meta_function_names.end(), name); - if (metamethodfind != meta_function_names.end()) { - meta_function metafunction = static_cast(metamethodfind - meta_function_names.begin()); - switch (metafunction) { - case meta_function::index: - indexfunc = functions.back().get(); - break; - case meta_function::new_index: - newindexfunc = functions.back().get(); - break; - default: - break; - } - metafunctiontable.push_back({ name.c_str(), detail::usertype_call }); - return; - } - indexwrapper.insert({ name, { true, functions.back().get() } }); - functiontable.push_back({ name.c_str(), detail::usertype_call }); - } + template + std::unique_ptr make_function(const std::string&, Fx&& func) { + typedef Unqualified Fxu; + typedef std::tuple_element_t<0, typename function_traits::args_tuple_type> Arg0; + typedef Unqualified> Argu; + static_assert(std::is_base_of::value, "Any non-member-function must have a first argument which is covariant with the desired usertype."); + typedef std::decay_t function_type; + return std::make_unique>(func); + } - template - void build_function_tables(std::string funcname, Fx&& func, Args&&... args) { - build_function(std::move(funcname), std::forward(func)); - build_function_tables(std::forward(args)...); - } + template + void build_function(std::string funcname, constructors) { + functionnames.push_back(std::move(funcname)); + std::string& name = functionnames.back(); + // Insert bubble to keep with compile-time argument count (simpler and cheaper to do) + functions.push_back(nullptr); + constructfunc = construct; + metafunctiontable.push_back({ functionnames.back().c_str(), constructfunc }); + } - template - void build_function_tables(meta_function metafunc, Fx&& func, Args&&... args) { - std::size_t idx = static_cast(metafunc); - const std::string& funcname = meta_function_names[idx]; - build_function_tables(funcname, std::forward(func), std::forward(args)...); - } + template + void build_function(std::string funcname, destructor_wrapper) { + auto metamethodfind = std::find(meta_function_names.begin(), meta_function_names.end(), funcname); + if (metamethodfind == meta_function_names.end()) + throw error("cannot set destructor to anything but the metamethod \"__gc\""); + meta_function metafunction = static_cast(metamethodfind - meta_function_names.begin()); + if (metafunction != meta_function::garbage_collect) + throw error("cannot set destructor to anything but the metamethod \"__gc\""); + + functionnames.push_back(std::move(funcname)); + std::string& name = functionnames.back(); + destructfunc = destruct; + destructfuncname = name.c_str(); + // Insert bubble to stay with the compile-time count + functions.push_back(nullptr); + } - template - void build_function_tables() { - int variableend = 0; - if (!indexwrapper.empty()) { - functions.push_back(std::make_unique("__index", indexfunc, std::move(indexwrapper))); - metafunctiontable.push_back({ "__index", detail::usertype_call }); - ++variableend; - } - if (!newindexwrapper.empty()) { - functions.push_back(std::make_unique("__newindex", newindexfunc, std::move(newindexwrapper))); - metafunctiontable.push_back({ "__newindex", indexwrapper.empty() ? detail::usertype_call : detail::usertype_call }); - ++variableend; - } - switch (variableend) { - case 2: - functiongc = detail::usertype_gc; - break; - case 1: - functiongc = detail::usertype_gc; - break; - case 0: - functiongc = detail::usertype_gc; - break; - } - } + template + void build_function(std::string funcname, destructor_wrapper dx) { + auto metamethodfind = std::find(meta_function_names.begin(), meta_function_names.end(), funcname); + if (metamethodfind == meta_function_names.end()) + throw error("cannot set destructor to anything but the metamethod \"__gc\""); + meta_function metafunction = static_cast(metamethodfind - meta_function_names.begin()); + if (metafunction != meta_function::garbage_collect) + throw error("cannot set destructor to anything but the metamethod \"__gc\""); - public: - template - usertype(Args&&... args) : usertype(default_constructor, std::forward(args)...) {} + functionnames.push_back(std::move(funcname)); + std::string& name = functionnames.back(); + auto baseptr = make_function(name, std::move(dx.fx)); + functions.emplace_back(std::move(baseptr)); + destructfunc = detail::usertype_call; + destructfuncname = name.c_str(); + } - template - usertype(constructors, Args&&... args) - : indexfunc(nullptr), newindexfunc(nullptr), constructfunc(nullptr), destructfunc(nullptr), functiongc(nullptr) { - functionnames.reserve(sizeof...(args)+3); - functiontable.reserve(sizeof...(args)+3); - metafunctiontable.reserve(sizeof...(args)+3); + template + void build_function(std::string funcname, Fx&& func) { + typedef std::is_member_object_pointer> is_variable; + functionnames.push_back(std::move(funcname)); + std::string& name = functionnames.back(); + auto baseptr = make_function(name, std::forward(func)); + functions.emplace_back(std::move(baseptr)); + auto metamethodfind = std::find(meta_function_names.begin(), meta_function_names.end(), name); + if (metamethodfind != meta_function_names.end()) { + metafunctiontable.push_back({ name.c_str(), detail::usertype_call }); + meta_function metafunction = static_cast(metamethodfind - meta_function_names.begin()); + switch (metafunction) { + case meta_function::garbage_collect: + destructfuncname = name.c_str(); + destructfunc = detail::usertype_call; + return; + case meta_function::index: + indexfunc = functions.back().get(); + needsindexfunction = true; + break; + case meta_function::new_index: + newindexfunc = functions.back().get(); + break; + case meta_function::construct: + constructfunc = detail::usertype_call; + break; + default: + break; + } + return; + } + if (is_variable::value) { + needsindexfunction = true; + indexwrapper.insert({ name, { false, functions.back().get() } }); + newindexwrapper.insert({ name, { false, functions.back().get() } }); + return; + } + indexwrapper.insert({ name, { true, functions.back().get() } }); + functiontable.push_back({ name.c_str(), detail::usertype_call }); + } - build_function_tables<0>(std::forward(args)...); - - functionnames.push_back("new"); - metafunctiontable.push_back({ functionnames.back().c_str(), &constructor::construct }); - functionnames.push_back("__gc"); - metafunctiontable.push_back({ functionnames.back().c_str(), destruct }); + template + void build_function_tables(std::string funcname, Fx&& func, Args&&... args) { + build_function(std::move(funcname), std::forward(func)); + build_function_tables(std::forward(args)...); + } - // 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 - metafunctiontable.push_back({ nullptr, nullptr }); - functiontable.push_back({ nullptr, nullptr }); - } + template + void build_function_tables(meta_function metafunc, Fx&& func, Args&&... args) { + std::size_t idx = static_cast(metafunc); + const std::string& funcname = meta_function_names[idx]; + build_function_tables(funcname, std::forward(func), std::forward(args)...); + } - int push(lua_State* L) { - // push pointer tables first, - usertype_detail::push_metatable(L, functions, functiontable, metafunctiontable); - lua_pop(L, 1); - // but leave the regular T table on last - // so it can be linked to a type for usage with `.new(...)` or `:new(...)` - usertype_detail::push_metatable(L, functions, functiontable, metafunctiontable); - // Make sure to drop a table in the global namespace to properly destroy the pushed functions - // at some later point in life - usertype_detail::set_global_deleter(L, functiongc, functions); - return 1; - } - }; + template + void build_function_tables() { + int variableend = 0; + if (!indexwrapper.empty()) { + functions.push_back(std::make_unique("__index", indexfunc, std::move(indexwrapper))); + metafunctiontable.push_back({ "__index", detail::usertype_call }); + ++variableend; + } + if (!newindexwrapper.empty()) { + functions.push_back(std::make_unique("__newindex", newindexfunc, std::move(newindexwrapper))); + metafunctiontable.push_back({ "__newindex", indexwrapper.empty() ? detail::usertype_call : detail::usertype_call }); + ++variableend; + } + if (destructfunc != nullptr) { + metafunctiontable.push_back({ destructfuncname, destructfunc }); + } + switch (variableend) { + case 2: + functiongcfunc = detail::usertype_gc; + break; + case 1: + functiongcfunc = detail::usertype_gc; + break; + case 0: + functiongcfunc = detail::usertype_gc; + break; + } + } - namespace stack { - template - struct pusher> { - static int push(lua_State* L, usertype& user) { - return user.push(L); - } - }; - } // stack + template + usertype(usertype_detail::verified_tag, Args&&... args) : indexfunc(nullptr), newindexfunc(nullptr), constructfunc(nullptr), destructfunc(nullptr), functiongcfunc(nullptr), needsindexfunction(false) { + functionnames.reserve(sizeof...(args)+3); + functiontable.reserve(sizeof...(args)+3); + metafunctiontable.reserve(sizeof...(args)+3); + + build_function_tables<0>(std::forward(args)...); + metafunctiontable.push_back({ nullptr, nullptr }); + functiontable.push_back({ nullptr, nullptr }); + } + + template + usertype(usertype_detail::add_destructor_tag, Args&&... args) : usertype(usertype_detail::verified, "__gc", default_destructor, std::forward(args)...) {} + + template + usertype(usertype_detail::check_destructor_tag, Args&&... args) : usertype(If, Not>>, usertype_detail::add_destructor_tag, usertype_detail::verified_tag>(), std::forward(args)...) {} + +public: + + template + usertype(Args&&... args) : usertype(If, Not>>, decltype(default_constructor), usertype_detail::check_destructor_tag>(), std::forward(args)...) {} + + template + usertype(constructors constructorlist, Args&&... args) : usertype(usertype_detail::verified, "new", constructorlist, "__gc", default_destructor, std::forward(args)...) { + + } + + int push(lua_State* L) { + // push pointer tables first, + usertype_detail::push_metatable(L, needsindexfunction, functions, functiontable, metafunctiontable); + lua_pop(L, 1); + // but leave the regular T table on last + // so it can be linked to a type for usage with `.new(...)` or `:new(...)` + usertype_detail::push_metatable(L, needsindexfunction, functions, functiontable, metafunctiontable); + // Make sure to drop a table in the global namespace to properly destroy the pushed functions + // at some later point in life + usertype_detail::set_global_deleter(L, functiongcfunc, functions); + return 1; + } +}; + +namespace stack { +template +struct pusher> { + static int push(lua_State* L, usertype& user) { + return user.push(L); + } +}; +} // stack } // sol #endif // SOL_USERTYPE_HPP diff --git a/tests.cpp b/tests.cpp index b9c2d8d2..311c5f60 100644 --- a/tests.cpp +++ b/tests.cpp @@ -224,49 +224,84 @@ struct giver { }; +struct factory_test { +private: + factory_test() { a = true_a; } + ~factory_test() { a = 0; } +public: + static int num_saved; + static int num_killed; + + struct deleter { + void operator()(factory_test* f) { + f->~factory_test(); + } + }; + + static const int true_a = 156; + int a; + + static std::unique_ptr make() { + return std::unique_ptr( new factory_test(), deleter()); + } + + static void save(factory_test& f) { + new(&f)factory_test(); + ++num_saved; + } + + static void kill(factory_test& f) { + f.~factory_test(); + ++num_killed; + } +}; + +int factory_test::num_saved = 0; +int factory_test::num_killed = 0; + TEST_CASE("table/traversal", "ensure that we can chain requests and tunnel down into a value if we desire") { - sol::state lua; - int begintop = 0, endtop = 0; + sol::state lua; + int begintop = 0, endtop = 0; - lua.script("t1 = {t2 = {t3 = 24}};"); - { - stack_guard g(lua.lua_state(), begintop, endtop); - int traversex24 = lua.traverse_get("t1", "t2", "t3"); - REQUIRE(traversex24 == 24); - } REQUIRE(begintop == endtop); + lua.script("t1 = {t2 = {t3 = 24}};"); + { + stack_guard g(lua.lua_state(), begintop, endtop); + int traversex24 = lua.traverse_get("t1", "t2", "t3"); + REQUIRE(traversex24 == 24); + } REQUIRE(begintop == endtop); - { - stack_guard g(lua.lua_state(), begintop, endtop); - int x24 = lua["t1"]["t2"]["t3"]; - REQUIRE(x24 == 24); - } REQUIRE(begintop == endtop); + { + stack_guard g(lua.lua_state(), begintop, endtop); + int x24 = lua["t1"]["t2"]["t3"]; + REQUIRE(x24 == 24); + } REQUIRE(begintop == endtop); - { - stack_guard g(lua.lua_state(), begintop, endtop); - lua["t1"]["t2"]["t3"] = 64; - int traversex64 = lua.traverse_get("t1", "t2", "t3"); - REQUIRE(traversex64 == 64); - } REQUIRE(begintop == endtop); + { + stack_guard g(lua.lua_state(), begintop, endtop); + lua["t1"]["t2"]["t3"] = 64; + int traversex64 = lua.traverse_get("t1", "t2", "t3"); + REQUIRE(traversex64 == 64); + } REQUIRE(begintop == endtop); - { - stack_guard g(lua.lua_state(), begintop, endtop); - int x64 = lua["t1"]["t2"]["t3"]; - REQUIRE(x64 == 64); - } REQUIRE(begintop == endtop); + { + stack_guard g(lua.lua_state(), begintop, endtop); + int x64 = lua["t1"]["t2"]["t3"]; + REQUIRE(x64 == 64); + } REQUIRE(begintop == endtop); - { - stack_guard g(lua.lua_state(), begintop, endtop); - lua.traverse_set("t1", "t2", "t3", 13); - int traversex13 = lua.traverse_get("t1", "t2", "t3"); - REQUIRE(traversex13 == 13); - } REQUIRE(begintop == endtop); + { + stack_guard g(lua.lua_state(), begintop, endtop); + lua.traverse_set("t1", "t2", "t3", 13); + int traversex13 = lua.traverse_get("t1", "t2", "t3"); + REQUIRE(traversex13 == 13); + } REQUIRE(begintop == endtop); - { - stack_guard g(lua.lua_state(), begintop, endtop); - int x13 = lua["t1"]["t2"]["t3"]; - REQUIRE(x13 == 13); - } REQUIRE(begintop == endtop); + { + stack_guard g(lua.lua_state(), begintop, endtop); + int x13 = lua["t1"]["t2"]["t3"]; + REQUIRE(x13 == 13); + } REQUIRE(begintop == endtop); } TEST_CASE("simple/set", "Check if the set works properly.") { @@ -475,15 +510,15 @@ TEST_CASE("advanced/call_referenced_obj", "A C++ object is passed by pointer/ref int x = 0; auto objx = [&](int new_x) { - x = new_x; - return 0; + x = new_x; + return 0; }; lua.set_function("set_x", std::ref(objx)); int y = 0; auto objy = [&](int new_y) { - y = new_y; - return std::tuple(0, 0); + y = new_y; + return std::tuple(0, 0); }; lua.set_function("set_y", &decltype(objy)::operator(), std::ref(objy)); @@ -715,8 +750,8 @@ TEST_CASE("tables/operator[]", "Check if operator[] retrieval and setting works auto assert1 = [](const sol::table& t) { std::string a = t["foo"]; double b = t["bar"]; - REQUIRE(a == "goodbye"); - REQUIRE(b == 20.4); + REQUIRE(a == "goodbye"); + REQUIRE(b == 20.4); }; REQUIRE_NOTHROW(assert1(lua.global())); @@ -1307,6 +1342,36 @@ func(1,2,3) REQUIRE_THROWS(lua.script("func(1,2,'meow')")); } +TEST_CASE("usertype/private constructible", "Check to make sure special snowflake types from Enterprise thingamahjongs work properly.") { + int numsaved = factory_test::num_saved; + int numkilled = factory_test::num_killed; + { + sol::state lua; + lua.open_libraries(sol::lib::base); + + lua.new_usertype("factory_test", + "new", sol::constructor(factory_test::save), + "__gc", sol::destructor(factory_test::kill), + "a", &factory_test::a + ); + + std::unique_ptr f = factory_test::make(); + lua.set("true_a", factory_test::true_a, "f", f.get()); + REQUIRE_NOTHROW(lua.script(R"( +assert(f.a == true_a) +)")); + + REQUIRE_NOTHROW(lua.script(R"( +local fresh_f = factory_test:new() +assert(fresh_f.a == true_a) +)")); + } + int expectednumsaved = numsaved + 1; + int expectednumkilled = numkilled + 1; + REQUIRE(expectednumsaved == factory_test::num_saved); + REQUIRE(expectednumkilled == factory_test::num_killed); +} + TEST_CASE("usertype/overloading", "Check if overloading works properly for usertypes") { struct woof { int var; @@ -1364,7 +1429,7 @@ TEST_CASE("issues/stack-overflow", "make sure various operations repeated don't REQUIRE_NOTHROW( for (int i = 0; i < 1000000; ++i) { int result = t[0]; - t.size(); + t.size(); if (result != expected) throw std::logic_error("RIP"); }