From 1293213775d8d90057c7a0b77b268ba641d4ff8d Mon Sep 17 00:00:00 2001 From: ThePhD Date: Sat, 13 Feb 2016 20:14:31 -0500 Subject: [PATCH] Several improvements to tunnneling for the library, with included tests Some stack size tests are also included to prevent stack overflow as well. --- bootstrap.py | 6 +- sol/function.hpp | 16 ++-- sol/function_result.hpp | 57 +++++++++++-- sol/function_types_static.hpp | 6 +- sol/proxy.hpp | 28 +++++-- sol/reference.hpp | 2 +- sol/stack.hpp | 149 +++++++++++++++++++++++++++------- sol/state_view.hpp | 26 ++---- sol/table_core.hpp | 116 ++++++++++++-------------- sol/traits.hpp | 17 ++++ sol/tuple.hpp | 2 + tests.cpp | 110 +++++++++++++++++++++---- 12 files changed, 377 insertions(+), 158 deletions(-) diff --git a/bootstrap.py b/bootstrap.py index 0c8fe489..04e9ad9d 100755 --- a/bootstrap.py +++ b/bootstrap.py @@ -50,7 +50,7 @@ args = parser.parse_args() # general variables include = [ '.', os.path.join('Catch', 'include')] depends = [] -cxxflags = [ '-Wall', '-Wextra', '-pedantic', '-pedantic-errors', '-std=c++14', '-Wno-unused-variable' ] +cxxflags = [ '-Wall', '-Wextra', '-pedantic', '-pedantic-errors', '-std=c++14' ] ldflags = [] script_dir = os.path.dirname(os.path.realpath(sys.argv[0])) sol_dir = os.path.join(script_dir, 'sol') @@ -67,8 +67,8 @@ if args.debug: else: cxxflags.extend(['-DNDEBUG', '-O3']) -if args.cxx == 'clang++': - cxxflags.extend(['-Wno-unused-value', '-Wno-constexpr-not-const']) +#if args.cxx == 'clang++': +# cxxflags.extend(['-Wno-unused-value', '-Wno-constexpr-not-const']) if args.lua_dir: include.extend([os.path.join(args.lua_dir, 'include')]) diff --git a/sol/function.hpp b/sol/function.hpp index ba52b337..31aa3486 100644 --- a/sol/function.hpp +++ b/sol/function.hpp @@ -67,7 +67,7 @@ private: luacall(n, LUA_MULTRET); int poststacksize = lua_gettop( lua_state( ) ); int returncount = poststacksize - (firstreturn - 1); - return function_result( lua_state( ), firstreturn, returncount, returncount, call_error::ok ); + return function_result( lua_state( ), firstreturn, returncount ); } public: @@ -156,7 +156,7 @@ private: luacall(n, 0, h); } - function_result invoke(types<>, std::index_sequence<>, std::ptrdiff_t n, handler& h) const { + protected_function_result invoke(types<>, std::index_sequence<>, std::ptrdiff_t n, handler& h) const { bool handlerpushed = error_handler.valid(); int stacksize = lua_gettop(lua_state()); int firstreturn = std::max(1, stacksize - static_cast(n) - 1); @@ -173,12 +173,12 @@ private: h.stackindex = 0; stack::push(lua_state(), error.what()); firstreturn = lua_gettop(lua_state()); - return function_result(lua_state(), firstreturn, 0, 1, call_error::runtime); + return protected_function_result(lua_state(), firstreturn, 0, 1, call_error::runtime); } catch (...) { throw; } - return function_result(lua_state(), firstreturn + ( handlerpushed ? 0 : 1 ), returncount, returncount, code); + return protected_function_result(lua_state(), firstreturn + ( handlerpushed ? 0 : 1 ), returncount, returncount, code); } public: @@ -194,7 +194,7 @@ public: protected_function& operator=( protected_function&& ) = default; template - function_result operator()(Args&&... args) const { + protected_function_result operator()(Args&&... args) const { return call<>(std::forward(args)...); } @@ -297,7 +297,7 @@ struct pusher> { void* userobjdata = static_cast(userptr); lua_CFunction freefunc = &static_member_function, uFx>::call; - int upvalues = stack::detail::push_as_upvalues(L, memfxptr); + int upvalues = stack::stack_detail::push_as_upvalues(L, memfxptr); upvalues += stack::push(L, userobjdata); stack::push(L, freefunc, upvalues); @@ -308,7 +308,7 @@ struct pusher> { std::decay_t target(std::forward(fx)); lua_CFunction freefunc = &static_function::call; - int upvalues = stack::detail::push_as_upvalues(L, target); + int upvalues = stack::stack_detail::push_as_upvalues(L, target); stack::push(L, freefunc, upvalues); } @@ -328,7 +328,7 @@ struct pusher> { lua_pop(L, 1); } - stack::detail::push_userdata(L, metatablename, userdata); + stack::stack_detail::push_userdata(L, metatablename, userdata); stack::push(L, freefunc, 1); } diff --git a/sol/function_result.hpp b/sol/function_result.hpp index 6b428083..7f2896dc 100644 --- a/sol/function_result.hpp +++ b/sol/function_result.hpp @@ -33,17 +33,61 @@ private: lua_State* L; int index; int returncount; - int popcount; - call_error error; public: function_result() = default; - function_result(lua_State* L, int index = -1, int returncount = 0, int popcount = 0, call_error error = call_error::ok): L(L), index(index), returncount(returncount), popcount(popcount), error(error) { + function_result(lua_State* L, int index = -1, int returncount = 0): L(L), index(index), returncount(returncount) { } function_result(const function_result&) = default; function_result& operator=(const function_result&) = default; - function_result(function_result&& o) : L(o.L), index(o.index), returncount(o.returncount), error(o.error) { + function_result(function_result&& o) : L(o.L), index(o.index), returncount(o.returncount) { + // Must be manual, otherwise destructor will screw us + // return count being 0 is enough to keep things clean + // but will be thorough + o.L = nullptr; + o.index = 0; + o.returncount = 0; + } + function_result& operator=(function_result&& o) { + L = o.L; + index = o.index; + returncount = o.returncount; + // Must be manual, otherwise destructor will screw us + // return count being 0 is enough to keep things clean + // but will be thorough + o.L = nullptr; + o.index = 0; + o.returncount = 0; + return *this; + } + + template + T get() const { + return stack::get(L, index); + } + + ~function_result() { + lua_pop(L, returncount); + } +}; + +struct protected_function_result : public proxy_base { +private: + lua_State* L; + int index; + int returncount; + int popcount; + call_error error; + +public: + protected_function_result() = default; + protected_function_result(lua_State* L, int index = -1, int returncount = 0, int popcount = 0, call_error error = call_error::ok): L(L), index(index), returncount(returncount), popcount(popcount), error(error) { + + } + protected_function_result(const protected_function_result&) = default; + protected_function_result& operator=(const protected_function_result&) = default; + protected_function_result(protected_function_result&& o) : L(o.L), index(o.index), returncount(o.returncount), popcount(o.popcount), error(o.error) { // Must be manual, otherwise destructor will screw us // return count being 0 is enough to keep things clean // but will be thorough @@ -53,10 +97,11 @@ public: o.popcount = 0; o.error = call_error::runtime; } - function_result& operator=(function_result&& o) { + protected_function_result& operator=(protected_function_result&& o) { L = o.L; index = o.index; returncount = o.returncount; + popcount = o.popcount; error = o.error; // Must be manual, otherwise destructor will screw us // return count being 0 is enough to keep things clean @@ -78,7 +123,7 @@ public: return stack::get(L, index); } - ~function_result() { + ~protected_function_result() { stack::remove(L, index, popcount); } }; diff --git a/sol/function_types_static.hpp b/sol/function_types_static.hpp index c12807c4..ea3d41ea 100644 --- a/sol/function_types_static.hpp +++ b/sol/function_types_static.hpp @@ -31,7 +31,7 @@ struct static_function { typedef function_traits traits_type; static int call(lua_State* L) { - auto udata = stack::detail::get_as_upvalues(L); + auto udata = stack::stack_detail::get_as_upvalues(L); function_type* fx = udata.first; int r = stack::typed_call(tuple_types(), typename traits_type::args_type(), fx, L); return r; @@ -48,8 +48,8 @@ struct static_member_function { typedef function_traits traits_type; static int call(lua_State* L) { - auto memberdata = stack::detail::get_as_upvalues(L, 1); - auto objdata = stack::detail::get_as_upvalues(L, memberdata.second); + auto memberdata = stack::stack_detail::get_as_upvalues(L, 1); + auto objdata = stack::stack_detail::get_as_upvalues(L, memberdata.second); function_type& memfx = memberdata.first; T& item = *objdata.first; auto fx = [&item, &memfx](auto&&... args) -> typename traits_type::return_type { return (item.*memfx)(std::forward(args)...); }; diff --git a/sol/proxy.hpp b/sol/proxy.hpp index 3bc6e98c..91a3533c 100644 --- a/sol/proxy.hpp +++ b/sol/proxy.hpp @@ -31,8 +31,19 @@ namespace sol { template struct proxy : public proxy_base> { private: + typedef If, Key, std::tuple>, Key&, Unqualified>>> key_type; Table tbl; - If>, Key&, Unqualified> key; + key_type key; + + template + decltype(auto) tuple_get(std::index_sequence) const { + return tbl.template traverse_get( std::get(key)... ); + } + + template + void tuple_set(std::index_sequence, T&& value) const { + tbl.traverse_set( std::get(key)..., std::forward(value) ); + } public: @@ -41,7 +52,7 @@ public: template proxy& set(T&& item) { - tbl.set(key, std::forward(item)); + tuple_set( std::make_index_sequence>::value>(), std::forward(item) ); return *this; } @@ -53,24 +64,23 @@ public: template>> = 0> proxy& operator=(U&& other) { - tbl.set_function(key, std::forward(other)); - return *this; + return set_function(std::forward(other)); } template>> = 0> proxy& operator=(U&& other) { - tbl.set(key, std::forward(other)); - return *this; + return set(std::forward(other)); } template decltype(auto) get() const { - return tbl.template get( key ); + return tuple_get( std::make_index_sequence>::value>() ); } template - decltype(auto) operator[](K&& key) const { - return get()[std::forward(key)]; + decltype(auto) operator[](K&& k) const { + auto keys = tuplefy(key, std::forward(k)); + return proxy(tbl, std::move(keys)); } template diff --git a/sol/reference.hpp b/sol/reference.hpp index ad6011c7..00fed18f 100644 --- a/sol/reference.hpp +++ b/sol/reference.hpp @@ -34,7 +34,7 @@ struct push_pop { }; template struct push_pop { - push_pop (T x) {} + push_pop (T) {} ~push_pop() {} }; template diff --git a/sol/stack.hpp b/sol/stack.hpp index 577d783f..5c9c3b58 100644 --- a/sol/stack.hpp +++ b/sol/stack.hpp @@ -35,6 +35,10 @@ namespace sol { namespace stack { +template +struct field_getter; +template +struct field_setter; template struct getter; template @@ -63,8 +67,7 @@ inline int push_args(lua_State*) { template inline int push_args(lua_State* L, T&& t, Args&&... args) { int pushcount = push(L, std::forward(t)); - using swallow = char[]; - void(swallow{'\0', (pushcount += sol::stack::push(L, std::forward(args)), '\0')... }); + void(sol::detail::swallow{(pushcount += sol::stack::push(L, std::forward(args)), 0)... }); return pushcount; } @@ -93,7 +96,27 @@ bool check(lua_State* L, int index) { return check(L, index, handler); } -namespace detail { +template +void get_field(lua_State* L, Key&& key) { + field_getter, global>{}.get(L, std::forward(key)); +} + +template +void get_field(lua_State* L, Key&& key, int tableindex) { + field_getter, global>{}.get(L, std::forward(key), tableindex); +} + +template +void set_field(lua_State* L, Key&& key, Value&& value) { + field_setter, global>{}.set(L, std::forward(key), std::forward(value)); +} + +template +void set_field(lua_State* L, Key&& key, Value&& value, int tableindex) { + field_setter, global>{}.set(L, std::forward(key), std::forward(value), tableindex); +} + +namespace stack_detail { const bool default_check_arguments = #ifdef SOL_CHECK_ARGUMENTS true; @@ -136,9 +159,8 @@ struct userdata_pusher { template inline int push_tuple(std::index_sequence, lua_State* L, T&& tuplen) { - using swallow = char[1 + sizeof...(I)]; int pushcount = 0; - swallow {'\0', (pushcount += sol::stack::push(L, std::get(tuplen)), '\0')... }; + detail::swallow{(pushcount += sol::stack::push(L, std::get(tuplen)), '\0')... }; return pushcount; } @@ -179,7 +201,7 @@ template inline int push_userdata(lua_State* L, Key&& metatablekey, Args&&... args) { return push_userdata_pointer(L, std::forward(metatablekey), std::forward(args)...); } -} // detail +} // stack_detail template struct checker { @@ -391,8 +413,8 @@ struct popper { template struct popper> { inline decltype(auto) pop(lua_State* L) { - decltype(auto) r = get>(L, lua_gettop(L) - sizeof...(Args)); - lua_pop(L, sizeof...(Args)); + decltype(auto) r = get>(L, lua_gettop(L) - sizeof...(Args) + 1); + lua_pop(L, static_cast(sizeof...(Args))); return r; } }; @@ -400,11 +422,11 @@ struct popper> { template struct pusher { static int push(lua_State* L, T& t) { - return detail::push_userdata(L, usertype_traits::metatable, t); + return stack_detail::push_userdata(L, usertype_traits::metatable, t); } static int push(lua_State* L, T&& t) { - return detail::push_userdata(L, usertype_traits::metatable, std::move(t)); + return stack_detail::push_userdata(L, usertype_traits::metatable, std::move(t)); } }; @@ -472,7 +494,7 @@ struct pusher::value>> { template struct pusher { static int push(lua_State* L, T* obj) { - return detail::push_userdata(L, usertype_traits::metatable, obj); + return stack_detail::push_userdata(L, usertype_traits::metatable, obj); } }; @@ -568,11 +590,81 @@ template struct pusher> { template static int push(lua_State* L, Tuple&& tuplen) { - return detail::push_tuple(std::index_sequence_for(), L, std::forward(tuplen)); + return stack_detail::push_tuple(std::index_sequence_for(), L, std::forward(tuplen)); } }; -namespace detail { +template +struct field_getter { + template + void get(lua_State* L, Key&& key, int tableindex = -2) { + push( L, std::forward( key ) ); + lua_gettable( L, tableindex ); + } +}; + +template +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)... }; + reference saved(L, -1); + lua_pop(L, static_cast(sizeof...(I) + 1)); + saved.push(); + } + + template + void get(lua_State* L, Key&& key, int tableindex = -2) { + apply(std::index_sequence_for(), L, std::forward(key), tableindex); + } +}; + +template +struct field_getter::value>> { + template + void get(lua_State* L, Key&& key, int = -1) { + lua_getglobal(L, &key[0]); + } +}; + +template +struct field_getter::value>> { + template + void get(lua_State* L, Key&& key, int tableindex = -1) { + lua_getfield(L, tableindex, &key[0]); + } +}; + +template +struct field_setter { + template + void set(lua_State* L, Key&& key, Value&& value, int tableindex = -3) { + push(L, std::forward(key)); + push(L, std::forward(value)); + lua_settable(L, tableindex); + } +}; + +template +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]); + } +}; + +template +struct field_setter::value>> { + template + void set(lua_State* L, Key&& key, Value&& value, int tableindex = -2) { + push(L, std::forward(value)); + lua_setfield(L, tableindex, &key[0]); + } +}; + +namespace stack_detail { template inline int push_as_upvalues(lua_State* L, T& item) { typedef std::decay_t TValue; @@ -608,10 +700,7 @@ struct check_arguments { static bool check(types, std::index_sequence, lua_State* L, int firstargument) { bool checks = true; stack::check(L, firstargument + I0); - using swallow = int[sizeof...(Args)+2]; - (void)swallow { - 0, (checks &= stack::check(L, firstargument + I))..., 0 - }; + (void)detail::swallow{(checks &= stack::check(L, firstargument + I))...}; return checks; } @@ -628,28 +717,28 @@ struct check_arguments { } }; -template ::value>> +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))); - detail::check_arguments{}.check(ta, tai, L, firstargument); + check_arguments{}.check(ta, tai, L, firstargument); return fx(std::forward(args)..., stack::get(L, firstargument + I)...); } -template +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 = detail::check_arguments{}.check(ta, tai, L, firstargument); + 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)...); } -} // detail +} // stack_detail inline void remove( lua_State* L, int index, int count ) { if ( count < 1 ) @@ -673,29 +762,29 @@ inline void remove( lua_State* L, int index, int count ) { } } -template ::value>> +template ::value>> inline R call(types tr, types ta, lua_State* L, int start, Fx&& fx, FxArgs&&... args) { typedef typename types::indices args_indices; - return detail::call(tr, ta, args_indices(), L, start, std::forward(fx), std::forward(args)...); + return stack_detail::call(tr, ta, args_indices(), L, start, std::forward(fx), std::forward(args)...); } -template ::value>> +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)...); } -template +template inline void call(types tr, types ta, lua_State* L, int start, Fx&& fx, FxArgs&&... args) { typedef typename types::indices args_indices; - detail::call(tr, ta, args_indices(), L, start, std::forward(fx), std::forward(args)...); + stack_detail::call(tr, ta, args_indices(), L, start, std::forward(fx), std::forward(args)...); } -template +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)...); } -template +template inline int typed_call(types tr, types ta, Fx&& fx, lua_State* L, int start = 0) { call(tr, ta, L, start, fx); int nargs = static_cast(sizeof...(Args)); @@ -703,7 +792,7 @@ inline int typed_call(types tr, types ta, Fx&& fx, lua_State* L, return 0; } -template +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); int nargs = static_cast(sizeof...(Args)); diff --git a/sol/state_view.hpp b/sol/state_view.hpp index b720386f..7d5fa86b 100644 --- a/sol/state_view.hpp +++ b/sol/state_view.hpp @@ -145,8 +145,7 @@ public: } template - auto get(Keys&&... keys) const - -> decltype(globals.get(std::forward(keys)...)) { + decltype(auto) get(Keys&&... keys) const { return globals.get(std::forward(keys)...); } @@ -156,24 +155,15 @@ public: return *this; } - template - SOL_DEPRECATED state_view& set_userdata(usertype& user) { - return set_usertype(user); + template + decltype(auto) traverse_get(Keys&&... keys) const { + return globals.traverse_get(std::forward(keys)...); } - template - SOL_DEPRECATED state_view& set_userdata(Key&& key, usertype& user) { - return set_usertype(std::forward(key), user); - } - - template - SOL_DEPRECATED state_view& new_userdata(const std::string& name, Args&&... args) { - return new_usertype(name, std::forward(args)...); - } - - template - SOL_DEPRECATED state_view& new_userdata(const std::string& name, constructors ctor, Args&&... args) { - return new_usertype(name, std::move(ctor), std::forward(args)...); + template + state_view& traverse_set(Args&&... args) { + globals.traverse_set(std::forward(args)...); + return *this; } template diff --git a/sol/table_core.hpp b/sol/table_core.hpp index d42efc79..70386210 100644 --- a/sol/table_core.hpp +++ b/sol/table_core.hpp @@ -37,65 +37,52 @@ class table_core : public reference { friend class state; friend class state_view; - template, is_c_str> = 0> - decltype(auto) single_get( Key&& key ) const { - lua_getglobal(lua_state( ), &key[0]); - return stack::pop(lua_state()); + template + using is_global = And, is_c_str...>; + + template + 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)... }; + return stack::pop>( lua_state() ); } - template>, is_c_str> = 0> - decltype(auto) single_get( Key&& key ) const { - auto pp = stack::push_popper(*this); - lua_getfield( lua_state( ), -1, &key[0] ); - return stack::pop( lua_state( ) ); - } - - template>> = 0> - decltype(auto) single_get( Key&& key ) const { - auto pp = stack::push_popper(*this); - stack::push( lua_state( ), std::forward( key ) ); - lua_gettable( lua_state( ), -2 ); - return stack::pop( lua_state( ) ); - } - - template, is_c_str> = 0> - void single_set( Key&& key, Value&& value ) { - stack::push( lua_state( ), std::forward( value ) ); - lua_setglobal( lua_state( ), &key[0] ); - } - - template>, is_c_str> = 0> - void single_set(Key&& key, Value&& value) { - push(); - stack::push(lua_state(), std::forward(value)); - lua_setfield(lua_state(), -2, &key[0]); - pop(); - } - - template>> = 0> - void single_set(Key&& key, Value&& value) { - push(); - stack::push(lua_state(), std::forward(key)); - stack::push(lua_state(), std::forward(value)); - lua_settable(lua_state(), -3); - pop(); - } - - template - std::tuple(nullptr, 0))...> tuple_get( types, std::index_sequence, Keys&& keys ) const { - typedef std::tuple(0))...> tup; - return tup( single_get( std::get( keys ) )... ); - } - - template + template decltype(auto) tuple_get( types, std::index_sequence, Keys&& keys ) const { - return single_get( std::get( keys ) ); + auto pp = stack::push_popper(keys))>::value>(*this); + stack::get_field( lua_state( ), std::get( keys ) ); + return stack::pop( lua_state( ) ); } template void tuple_set( std::index_sequence, Pairs&& pairs ) { - using swallow = int[]; - swallow{ 0, ( single_set(std::get(pairs), std::get(pairs)) , 0)..., 0 }; + auto pp = stack::push_popper(pairs))...>::value>(*this); + detail::swallow{ (stack::set_field(lua_state(), std::get(pairs), std::get(pairs)), 0)... }; + } + + template + decltype(auto) traverse_get_deep( Key&& key ) const { + stack::get_field( lua_state( ), std::forward( key ) ); + return stack::get( lua_state( ) ); + } + + 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)...); + } + + template + void traverse_set_deep( Key&& key, Value&& value ) const { + stack::set_field( lua_state( ), std::forward( key ), std::forward(value) ); + } + + template + void traverse_set_deep( Key&& key, Keys&&... keys ) const { + stack::get_field( lua_state( ), std::forward( key ) ); + traverse_set_deep(std::forward(keys)...); } #if SOL_LUA_VERSION < 502 @@ -115,22 +102,27 @@ public: return tuple_get( types( ), std::index_sequence_for( ), std::forward_as_tuple(std::forward(keys)...)); } + 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)...); + } + + 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; + } + template table_core& set( Args&&... args ) { tuple_set(std::make_index_sequence(), std::forward_as_tuple(std::forward(args)...)); return *this; } - template - SOL_DEPRECATED table_core& set_userdata( usertype& user ) { - return set_usertype( user ); - } - - template - SOL_DEPRECATED table_core& set_userdata( Key&& key, usertype& user ) { - return set_usertype(std::forward(key), user); - } - template table_core& set_usertype( usertype& user ) { return set_usertype(usertype_traits::name, user); diff --git a/sol/traits.hpp b/sol/traits.hpp index b7ae5c42..d5c9d79b 100644 --- a/sol/traits.hpp +++ b/sol/traits.hpp @@ -380,6 +380,23 @@ template inline T* ptr(T* val) { return val; } + +namespace detail { +template , std::tuple>> = 0> +decltype(auto) force_tuple(T&& x) { + return std::forward_as_tuple(x); +} + +template , std::tuple>> = 0> +decltype(auto) force_tuple(T&& x) { + return std::forward(x); +} +} // detail + +template +decltype(auto) tuplefy(X&&... x ) { + return std::tuple_cat(detail::force_tuple(x)...); +} } // sol #endif // SOL_TRAITS_HPP diff --git a/sol/tuple.hpp b/sol/tuple.hpp index 9d6f8c4e..c749e234 100644 --- a/sol/tuple.hpp +++ b/sol/tuple.hpp @@ -45,6 +45,8 @@ struct tuple_types_ { typedef types type; }; template struct tuple_types_> { typedef types type; }; +using swallow = std::initializer_list; + } // detail template diff --git a/tests.cpp b/tests.cpp index 0fb2280a..4fca279f 100644 --- a/tests.cpp +++ b/tests.cpp @@ -5,6 +5,16 @@ #include #include +struct stack_guard { + lua_State* L; + int& begintop; + int& endtop; + stack_guard(lua_State* L, int& begintop, int& endtop) : L(L), begintop(begintop), endtop(endtop) { + begintop = lua_gettop(L); + } + ~stack_guard() { endtop = lua_gettop(L); } +}; + void test_free_func(std::function f) { f(); } @@ -214,37 +224,100 @@ struct giver { }; +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; + + 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); + 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); + 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); +} + TEST_CASE("simple/set", "Check if the set works properly.") { sol::state lua; - - lua.set("a", 9); + int begintop = 0, endtop = 0; + { + stack_guard g(lua.lua_state(), begintop, endtop); + lua.set("a", 9); + } REQUIRE(begintop == endtop); REQUIRE_NOTHROW(lua.script("if a ~= 9 then error('wrong value') end")); - - lua.set("d", "hello"); + { + stack_guard g(lua.lua_state(), begintop, endtop); + lua.set("d", "hello"); + } REQUIRE(begintop == endtop); REQUIRE_NOTHROW(lua.script("if d ~= 'hello' then error('expected \\'hello\\', got '.. tostring(d)) end")); - lua.set("e", std::string("hello"), "f", true); + { + stack_guard g(lua.lua_state(), begintop, endtop); + lua.set("e", std::string("hello"), "f", true); + } REQUIRE(begintop == endtop); REQUIRE_NOTHROW(lua.script("if d ~= 'hello' then error('expected \\'hello\\', got '.. tostring(d)) end")); REQUIRE_NOTHROW(lua.script("if f ~= true then error('wrong value') end")); } TEST_CASE("simple/get", "Tests if the get function works properly.") { sol::state lua; + int begintop = 0, endtop = 0; lua.script("a = 9"); - auto a = lua.get("a"); - REQUIRE(a == 9.0); + { + stack_guard g(lua.lua_state(), begintop, endtop); + auto a = lua.get("a"); + REQUIRE(a == 9.0); + } REQUIRE(begintop == endtop); lua.script("b = nil"); - REQUIRE_NOTHROW(lua.get("b")); + { + stack_guard g(lua.lua_state(), begintop, endtop); + REQUIRE_NOTHROW(lua.get("b")); + } REQUIRE(begintop == endtop); lua.script("d = 'hello'"); lua.script("e = true"); - std::string d; - bool e; - std::tie( d, e ) = lua.get("d", "e"); - REQUIRE(d == "hello"); - REQUIRE(e == true); + { + stack_guard g(lua.lua_state(), begintop, endtop); + std::string d; + bool e; + std::tie( d, e ) = lua.get("d", "e"); + REQUIRE(d == "hello"); + REQUIRE(e == true); + } REQUIRE(begintop == endtop); } TEST_CASE("simple/set-get-global-integer", "Tests if the get function works properly with global integers") { @@ -641,8 +714,9 @@ TEST_CASE("tables/operator[]", "Check if operator[] retrieval and setting works // test const table retrieval auto assert1 = [](const sol::table& t) { std::string a = t["foo"]; - int b = t["bar"]; - std::cout << a << ',' << b << '\n'; + double b = t["bar"]; + REQUIRE(a == "goodbye"); + REQUIRE(b == 20.4); }; REQUIRE_NOTHROW(assert1(lua.global())); @@ -1086,13 +1160,13 @@ TEST_CASE( "functions/function_result-protected_function", "Function result shou func.error_handler = luahandler; luafunc.error_handler = cpphandler; - sol::function_result result1 = func(); + sol::protected_function_result result1 = func(); int test = lua_gettop(lua.lua_state()); REQUIRE(!result1.valid()); std::string errorstring = result1; REQUIRE(errorstring == errormessage1); - sol::function_result result2 = luafunc(); + sol::protected_function_result result2 = luafunc(); REQUIRE(!result2.valid()); errorstring = result2; REQUIRE(errorstring == errormessage2); @@ -1292,4 +1366,4 @@ TEST_CASE("issues/stack-overflow", "make sure various operations repeated don't throw std::logic_error("RIP"); } ); -} \ No newline at end of file +}