From 88155d44e00dfa80b0e3e1421442a0b9db4ef7bd Mon Sep 17 00:00:00 2001 From: ThePhD Date: Sun, 17 Apr 2016 02:18:34 -0400 Subject: [PATCH] sol::property is implemented for usertypes now sol::this_state is a transparent argument that gets the current state at any position in any callback sol::variadic_args allows a person to get something that can reference the "rest of the arguments", though it doesn't enforce that it has to be the last argument Closes #57 Closes #59 Closes #60 --- sol/function_types.hpp | 1 - sol/function_types_basic.hpp | 4 +- sol/function_types_core.hpp | 191 +++++++++++++++++++++++++------- sol/function_types_member.hpp | 4 +- sol/function_types_usertype.hpp | 80 ++++++------- sol/object.hpp | 1 + sol/stack.hpp | 18 +-- sol/stack_check.hpp | 18 ++- sol/stack_get.hpp | 23 ++-- sol/stack_proxy.hpp | 21 ++++ sol/stack_push.hpp | 16 +-- sol/traits.hpp | 11 ++ sol/types.hpp | 24 +++- sol/usertype.hpp | 19 ++-- sol/variadic_args.hpp | 102 +++++++++++++++++ test_functions.cpp | 36 +++++- test_stack_guard.hpp | 6 +- test_tables.cpp | 2 +- tests.cpp | 112 +++++++++++++++++-- 19 files changed, 539 insertions(+), 150 deletions(-) create mode 100644 sol/variadic_args.hpp diff --git a/sol/function_types.hpp b/sol/function_types.hpp index c37fb615..645abc47 100644 --- a/sol/function_types.hpp +++ b/sol/function_types.hpp @@ -33,7 +33,6 @@ #include "resolve.hpp" namespace sol { - template struct function_arguments { std::tuple params; diff --git a/sol/function_types_basic.hpp b/sol/function_types_basic.hpp index 8d2da650..d1a80807 100644 --- a/sol/function_types_basic.hpp +++ b/sol/function_types_basic.hpp @@ -80,7 +80,7 @@ struct upvalue_member_function { template int set_assignable(std::false_type, lua_State* L, M&, V&) { lua_pop(L, N); - return luaL_error(L, "cannot write to this type: copy assignment/constructor not available"); + return luaL_error(L, "sol: cannot write to this type: copy assignment/constructor not available"); } template @@ -98,7 +98,7 @@ int set_variable(std::true_type, lua_State* L, M& mem, V& var) { template int set_variable(std::false_type, lua_State* L, M&, V&) { lua_pop(L, N); - return luaL_error(L, "cannot write to a const variable"); + return luaL_error(L, "sol: cannot write to a const variable"); } template diff --git a/sol/function_types_core.hpp b/sol/function_types_core.hpp index cb208e96..0b0d297d 100644 --- a/sol/function_types_core.hpp +++ b/sol/function_types_core.hpp @@ -26,6 +26,108 @@ #include namespace sol { +template +struct member_property { + typedef std::conditional_t::value, detail::empty, RSig> R; + typedef std::conditional_t::value, detail::empty, WSig> W; + + R read; + W write; + + member_property(R read, W write) : read(std::move(read)), write(std::move(write)) {} + + template + void write_if(std::true_type, T& mem, Arg&& arg) { + write(mem, arg); + } + + template + void write_if(std::false_type, T&, Arg&&) { + // This is a fatal error if we get here... + // Should never happen but... + // Crash horrifically, for safety? + std::abort(); + } + + template + void operator()(T& mem, Arg&& arg) { + write_if(meta::Not>(), mem, arg); + } + + template + decltype(auto) read_if(std::true_type, T& mem) { + return read(mem); + } + + template + decltype(auto) read_if(std::false_type, T&) { + typedef typename meta::bind_traits::template arg_at<1> Arg; + typedef std::add_pointer_t> pret; + // This is a fatal error if we get here... + // Should never happen but... + // Crash horrifically, for safety? + std::abort(); + return *pret(); + } + + template + decltype(auto) operator()(T& mem) { + return read_if(meta::Not>(), mem); + } +}; + +template +inline decltype(auto) property( R(T::* readfunc )() const) { + auto rf = [readfunc](T& mem) -> R {return (mem.*readfunc)();}; + return member_property(std::move(rf), detail::empty()); +} + +template +inline decltype(auto) property( R(T::* readfunc )()) { + auto rf = [readfunc](T& mem) -> R {return (mem.*readfunc)();}; + return member_property(std::move(rf), detail::empty()); +} + +template +inline decltype(auto) property(R(T::* writefunc)(Arg)) { + auto wf = [writefunc](T& mem, Arg arg) {(mem.*writefunc)(std::forward(arg));}; + return member_property(detail::empty(), std::move(wf)); +} + +template +inline decltype(auto) property(R(T::* writefunc)(Arg) const) { + auto wf = [writefunc](T& mem, Arg arg) {(mem.*writefunc)(std::forward(arg));}; + return member_property(detail::empty(), std::move(wf)); +} + +template +inline decltype(auto) property(RR(RT::* readfunc)(), WR(WT::* writefunc)(Arg)) { + auto rf = [readfunc](RT& mem) -> RR {return (mem.*readfunc)();}; + auto wf = [writefunc](WT& mem, Arg arg) {(mem.*writefunc)(std::forward(arg));}; + return member_property(std::move(rf), std::move(wf)); +} + +template +inline decltype(auto) property(RR(RT::* readfunc)() const, WR(WT::* writefunc)(Arg)) { + auto rf = [readfunc](RT& mem) -> RR {return (mem.*readfunc)();}; + auto wf = [writefunc](WT& mem, Arg arg) {(mem.*writefunc)(std::forward(arg));}; + return member_property(std::move(rf), std::move(wf)); +} + +template +inline decltype(auto) property(RR(RT::* readfunc)(), WR(WT::* writefunc)(Arg) const) { + auto rf = [readfunc](RT& mem) -> RR {return (mem.*readfunc)();}; + auto wf = [writefunc](WT& mem, Arg arg) {(mem.*writefunc)(std::forward(arg));}; + return member_property(std::move(rf), std::move(wf)); +} + +template +inline decltype(auto) property(RR(RT::* readfunc)() const, WR(WT::* writefunc)(Arg) const) { + auto rf = [readfunc](RT& mem) -> RR {return (mem.*readfunc)();}; + auto wf = [writefunc](WT& mem, Arg arg) {(mem.*writefunc)(std::forward(arg));}; + return member_property(std::move(rf), std::move(wf)); +} + namespace function_detail { template struct implicit_wrapper { @@ -48,35 +150,62 @@ inline decltype(auto) cleanup_key() { template struct functor { typedef meta::bind_traits traits_type; - typedef typename traits_type::args_type args_type; + typedef meta::pop_front_type_t args_type; typedef typename traits_type::return_type return_type; - static const std::size_t arity = traits_type::arity; - + typedef meta::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; T* item; - Func invocation; + function_type invocation; template functor(Args&&... args): item(nullptr), invocation(std::forward(args)...) {} - bool check () const { - return invocation != nullptr; - } - template void call(types, Args&&... args) { T& member = *item; - (member.*invocation)(std::forward(args)...); + invocation(implicit_wrapper(member), std::forward(args)...); } template Ret call(types, Args&&... args) { T& member = *item; - return (member.*invocation)(std::forward(args)...); + return invocation(implicit_wrapper(member), std::forward(args)...); + } + + template + auto operator()(Args&&... args) -> decltype(std::declval().call(types{}, std::forward(args)...)) { + return this->call(types(), std::forward(args)...); + } +}; + +template +struct functor, C> { + typedef meta::bind_traits::value, RSig, WSig>> traits_type; + typedef meta::pop_front_type_t args_type; + typedef std::conditional_t::value, typename traits_type::template arg_at<0>, typename traits_type::return_type> return_type; + typedef member_property function_type; + typedef meta::Not> can_read; + typedef meta::Not> can_write; + T* item; + function_type invocation; + + template + functor(Args&&... args): item(nullptr), invocation(std::forward(args)...) {} + + template + void call(Arg&& arg) { + T& member = *item; + invocation(member, std::forward(arg)); + } + + decltype(auto) call() { + T& member = *item; + return invocation(member); } template decltype(auto) operator()(Args&&... args) { - return this->call(types{}, std::forward(args)...); + return this->call(std::forward(args)...); } }; @@ -86,16 +215,15 @@ struct functor::va typedef typename traits_type::args_type args_type; typedef typename traits_type::return_type return_type; static const std::size_t arity = traits_type::arity; + typedef std::true_type can_read; + typedef std::true_type can_write; + T* item; Func invocation; template functor(Args&&... args): item(nullptr), invocation(std::forward(args)...) {} - bool check () const { - return this->fx.invocation != nullptr; - } - template void call(Arg&& arg) { T& member = *item; @@ -114,50 +242,33 @@ struct functor::va }; template -struct functor::value || std::is_class::value>> { +struct functor::value>> { typedef meta::bind_traits traits_type; - typedef meta::pop_front_type_t args_type; + typedef typename traits_type::args_type args_type; typedef typename traits_type::return_type return_type; static const std::size_t arity = traits_type::arity; - typedef meta::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; - static_assert(std::is_base_of>, T>::value, "Any non-member-function must have a first argument which is covariant with the desired userdata type."); + T* item; - function_type invocation; - -private: - bool check(std::false_type) const { - return true; - } - - bool check(std::true_type) const { - return this->invocation != nullptr; - } - -public: + Func invocation; template functor(Args&&... args): item(nullptr), invocation(std::forward(args)...) {} - bool check () const { - return this->check(std::is_function()); - } - template void call(types, Args&&... args) { T& member = *item; - invocation(implicit_wrapper(member), std::forward(args)...); + (member.*invocation)(std::forward(args)...); } template Ret call(types, Args&&... args) { T& member = *item; - return invocation(implicit_wrapper(member), std::forward(args)...); + return (member.*invocation)(std::forward(args)...); } template - auto operator()(Args&&... args) -> decltype(std::declval().call(types{}, std::forward(args)...)) { - return this->call(types(), std::forward(args)...); + decltype(auto) operator()(Args&&... args) { + return this->call(types{}, std::forward(args)...); } }; diff --git a/sol/function_types_member.hpp b/sol/function_types_member.hpp index 4f38dc1a..922154dc 100644 --- a/sol/function_types_member.hpp +++ b/sol/function_types_member.hpp @@ -113,7 +113,7 @@ struct member_variable : public base_function { int set_assignable(std::false_type, lua_State* L, M) { lua_pop(L, 1); - return luaL_error(L, "cannot write to this type: copy assignment/constructor not available"); + return luaL_error(L, "sol: cannot write to this type: copy assignment/constructor not available"); } int set_assignable(std::true_type, lua_State* L, M mem) { @@ -128,7 +128,7 @@ struct member_variable : public base_function { int set_variable(std::false_type, lua_State* L, M) { lua_pop(L, 1); - return luaL_error(L, "cannot write to a const variable"); + return luaL_error(L, "sol: cannot write to a const variable"); } int call(lua_State* L) { diff --git a/sol/function_types_usertype.hpp b/sol/function_types_usertype.hpp index dd77284d..cfc08ca6 100644 --- a/sol/function_types_usertype.hpp +++ b/sol/function_types_usertype.hpp @@ -28,7 +28,7 @@ namespace sol { namespace function_detail { -template +template struct usertype_function_core : public base_function { typedef std::remove_pointer_t T; typedef std::remove_pointer_t> function_type; @@ -42,48 +42,15 @@ struct usertype_function_core : public base_function { template usertype_function_core(Args&&... args): fx(std::forward(args)...) {} - template> - std::enable_if_t::value, int> push(lua_State* L, Return&& r) { - if(detail::ptr(detail::unwrap(r)) == fx.item) { - // push nothing - // note that pushing nothing with the ':' - // syntax means we leave the instance of what - // was pushed onto the stack by lua to do the - // function call alone, - // and naturally lua returns that. - // It's an "easy" way to return *this, - // without allocating an extra userdata, apparently! - return 1; - } - return stack::push_reference(L, std::forward(r)); - } - - template> - std::enable_if_t::value, int> push(lua_State* L, Return&& r) { - return stack::push_reference(L, std::forward(r)); - } - - template - int operator()(types tr, types ta, index_value, lua_State* L) { - stack::call_into_lua(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, index_value, 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)); - return pushcount; + return stack::call_into_lua<1>(tr, ta, L, static_cast(Start), fx); } }; -template -struct usertype_function : public usertype_function_core { - typedef usertype_function_core base_t; +template +struct usertype_function : public usertype_function_core { + typedef usertype_function_core base_t; typedef std::remove_pointer_t T; typedef typename base_t::traits_type traits_type; typedef typename base_t::args_type args_type; @@ -102,41 +69,62 @@ struct usertype_function : public usertype_function_core { } }; -template -struct usertype_variable_function : public usertype_function_core { - typedef usertype_function_core base_t; +template +struct usertype_variable_function : public usertype_function_core { + typedef usertype_function_core base_t; typedef std::remove_pointer_t T; + typedef typename base_t::fx_t fx_t; typedef typename base_t::traits_type traits_type; typedef typename base_t::args_type args_type; typedef typename base_t::return_type return_type; + typedef typename fx_t::can_read can_read; + typedef typename fx_t::can_write can_write; template usertype_variable_function(Args&&... args): base_t(std::forward(args)...) {} int set_assignable(std::false_type, lua_State* L) { - lua_pop(L, 2); - return luaL_error(L, "cannot write to this type: copy assignment/constructor not available"); + lua_pop(L, 3); + return luaL_error(L, "sol: cannot write to this type: copy assignment/constructor not available"); } int set_assignable(std::true_type, lua_State* L) { + return set_writable(can_write(), L); + } + + int set_writable(std::false_type, lua_State* L) { + lua_pop(L, 3); + return luaL_error(L, "sol: cannot write to readonly variable"); + } + + int set_writable(std::true_type, lua_State* L) { return static_cast(*this)(meta::tuple_types(), args_type(), index_value<3>(), L); } int set_variable(std::false_type, lua_State* L) { - lua_pop(L, 2); - return luaL_error(L, "cannot write to a const variable"); + lua_pop(L, 3); + return luaL_error(L, "sol: cannot write to a const variable"); } int set_variable(std::true_type, lua_State* L) { return set_assignable(std::is_assignable, return_type>(), L); } + int get_variable(std::false_type, lua_State* L) { + lua_pop(L, 2); + return luaL_error(L, "sol: cannot read from a readonly property"); + } + + int get_variable(std::true_type, lua_State* L) { + return static_cast(*this)(meta::tuple_types(), types<>(), index_value<2>(), L); + } + int prelude(lua_State* L) { int argcount = lua_gettop(L); this->fx.item = stack::get(L, 1); switch(argcount) { case 2: - return static_cast(*this)(meta::tuple_types(), types<>(), index_value<2>(), L); + return get_variable(can_read(), L); case 3: return set_variable(meta::Not>(), L); default: diff --git a/sol/object.hpp b/sol/object.hpp index e25d4dfb..5b73fa30 100644 --- a/sol/object.hpp +++ b/sol/object.hpp @@ -25,6 +25,7 @@ #include "reference.hpp" #include "userdata.hpp" #include "stack.hpp" +#include "variadic_args.hpp" namespace sol { template diff --git a/sol/stack.hpp b/sol/stack.hpp index 8bdc147e..1f469d9f 100644 --- a/sol/stack.hpp +++ b/sol/stack.hpp @@ -69,14 +69,18 @@ inline std::pair get_as_upvalues(lua_State* L, int index = 1) { template ::value>> inline decltype(auto) call(types, types ta, std::index_sequence tai, lua_State* L, int start, Fx&& fx, FxArgs&&... args) { + typedef meta::index_in_pack state_pack_index; + typedef std::integral_constant::max() : static_cast(state_pack_index::value)> state_idx; check_types{}.check(ta, tai, L, start, type_panic); - return fx(std::forward(args)..., stack_detail::unchecked_get(L, start + I)...); + return fx(std::forward(args)..., stack_detail::unchecked_get(L, start + (state_idx::value < I ? I - 1 : I))...); } template inline void call(types, types ta, std::index_sequence tai, lua_State* L, int start, Fx&& fx, FxArgs&&... args) { + typedef meta::index_in_pack state_pack_index; + typedef std::integral_constant::max() : static_cast(state_pack_index::value)> state_idx; check_types{}.check(ta, tai, L, start, type_panic); - fx(std::forward(args)..., stack_detail::unchecked_get(L, start + I)...); + fx(std::forward(args)..., stack_detail::unchecked_get(L, start + (state_idx::value < I ? I - 1 : I))...); } } // stack_detail @@ -104,7 +108,7 @@ inline void remove( lua_State* L, int index, int count ) { template ::value>> inline decltype(auto) call(types tr, types ta, lua_State* L, int start, Fx&& fx, FxArgs&&... args) { - typedef typename types::indices args_indices; + typedef std::make_index_sequence args_indices; return stack_detail::call(tr, ta, args_indices(), L, start, std::forward(fx), std::forward(args)...); } @@ -115,7 +119,7 @@ inline decltype(auto) call(types tr, types ta, lua_State* L, Fx&& fx template inline void call(types tr, types ta, lua_State* L, int start, Fx&& fx, FxArgs&&... args) { - typedef typename types::indices args_indices; + typedef std::make_index_sequence args_indices; stack_detail::call(tr, ta, args_indices(), L, start, std::forward(fx), std::forward(args)...); } @@ -147,11 +151,11 @@ inline int call_into_lua(types, types ta, lua_State* L, i decltype(auto) r = call(types>(), ta, L, start, std::forward(fx), std::forward(fxargs)...); int nargs = static_cast(sizeof...(Args)) + additionalpop; lua_pop(L, nargs); - return push(L, std::forward(r)); + return push_reference(L, std::forward(r)); } -inline call_syntax get_call_syntax(lua_State* L, const std::string& meta) { - luaL_getmetatable(L, meta.c_str()); +inline call_syntax get_call_syntax(lua_State* L, const std::string& key) { + luaL_getmetatable(L, key.c_str()); if (lua_compare(L, -1, -2, LUA_OPEQ) == 1) { lua_pop(L, 1); return call_syntax::colon; diff --git a/sol/stack_check.hpp b/sol/stack_check.hpp index 363cb48f..30559331 100644 --- a/sol/stack_check.hpp +++ b/sol/stack_check.hpp @@ -65,7 +65,7 @@ struct check_types { static bool check(types, std::index_sequence, lua_State* L, int firstargument, Handler&& handler) { if (!stack::check(L, firstargument + I0, handler)) return false; - return check(types(), std::index_sequence(), L, firstargument, std::forward(handler)); + return check(types(), std::index_sequence(), L, firstargument - static_cast(std::is_same>::value), std::forward(handler)); } template @@ -121,6 +121,22 @@ struct checker { template struct checker : checker {}; +template +struct checker { + template + static bool check (lua_State*, int, Handler&&) { + return true; + } +}; + +template +struct checker { + template + static bool check (lua_State*, int, Handler&&) { + return true; + } +}; + template struct checker { template diff --git a/sol/stack_get.hpp b/sol/stack_get.hpp index fbbfa23f..08acf0e7 100644 --- a/sol/stack_get.hpp +++ b/sol/stack_get.hpp @@ -63,26 +63,12 @@ struct getter, std::is_unsigne }; template -struct getter::value>> { +struct getter::value || std::is_base_of::value>> { static T get(lua_State* L, int index = -1) { return T(L, index); } }; -template -struct getter::value>> { - static T get(lua_State* L, int index = -1) { - return T(L, index); - } -}; - -template<> -struct getter { - static stack_reference get(lua_State* L, int index = -1) { - return stack_reference(L, index); - } -}; - template<> struct getter { static userdata_value get(lua_State* L, int index = -1) { @@ -148,6 +134,13 @@ struct getter { } }; +template<> +struct getter { + static this_state get(lua_State* L, int = -1) { + return this_state{L}; + } +}; + template<> struct getter { static lua_CFunction get(lua_State* L, int index = -1) { diff --git a/sol/stack_proxy.hpp b/sol/stack_proxy.hpp index 4099d120..76935fae 100644 --- a/sol/stack_proxy.hpp +++ b/sol/stack_proxy.hpp @@ -39,9 +39,30 @@ public: return stack::get(L, stack_index()); } + int push () const { + lua_pushvalue(L, index); + return 1; + } + lua_State* lua_state() const { return L; } int stack_index() const { return index; } }; + +namespace stack { +template <> +struct getter { + static stack_proxy get(lua_State* L, int index = -1) { + return stack_proxy(L, index); + } +}; + +template <> +struct pusher { + static int push(lua_State*, const stack_proxy& ref) { + return ref.push(); + } +}; +} // stack } // sol #endif // SOL_STACK_PROXY_HPP diff --git a/sol/stack_push.hpp b/sol/stack_push.hpp index 8db179ab..c0817502 100644 --- a/sol/stack_push.hpp +++ b/sol/stack_push.hpp @@ -175,7 +175,7 @@ struct pusher, meta::has_ke }; template -struct pusher::value>> { +struct pusher::value || std::is_base_of::value>> { static int push(lua_State*, T& ref) { return ref.push(); } @@ -185,13 +185,6 @@ struct pusher::value>> { } }; -template<> -struct pusher { - static int push(lua_State*, const stack_reference& ref) { - return ref.push(); - } -}; - template<> struct pusher { static int push(lua_State* L, bool b) { @@ -333,6 +326,13 @@ struct pusher { return stack::push(L, nil); } }; + +template<> +struct pusher { + static int push(lua_State*, const this_state&) { + return 0; + } +}; } // stack } // sol diff --git a/sol/traits.hpp b/sol/traits.hpp index 401d4ec1..fe5464d5 100644 --- a/sol/traits.hpp +++ b/sol/traits.hpp @@ -132,6 +132,17 @@ struct find_in_pack_v : Bool { }; template struct find_in_pack_v : Or, find_in_pack_v> { }; +namespace meta_detail { + template + struct index_in_pack : std::integral_constant { }; + + template + struct index_in_pack : std::conditional_t::value, std::integral_constant, index_in_pack> { }; +} + +template +struct index_in_pack : meta_detail::index_in_pack<0, T, Args...> { }; + template struct at_in_pack {}; diff --git a/sol/types.hpp b/sol/types.hpp index e067eb97..3a47b289 100644 --- a/sol/types.hpp +++ b/sol/types.hpp @@ -83,7 +83,8 @@ inline int c_trampoline(lua_State* L, lua_CFunction f) { return trampoline(L, f); } #endif // Exceptions vs. No Exceptions -} +struct empty { void operator()() {} }; +} // detail struct nil_t {}; const nil_t nil {}; struct metatable_key_t {}; @@ -91,7 +92,7 @@ const metatable_key_t metatable_key = {}; inline bool operator==(nil_t, nil_t) { return true; } inline bool operator!=(nil_t, nil_t) { return false; } -typedef std::add_lvalue_reference_t> lua_r_CFunction; +typedef std::remove_pointer_t lua_r_CFunction; template struct unique_usertype {}; @@ -132,6 +133,16 @@ struct c_closure { c_closure(lua_CFunction f, int upvalues = 0) : c_function(f), upvalues(upvalues) {} }; +struct this_state { + lua_State* L; + operator lua_State* () const { + return L; + } + lua_State* operator-> () const { + return L; + } +}; + enum class call_syntax { dot = 0, colon = 1 @@ -295,6 +306,7 @@ template class basic_userdata; template class basic_lightuserdata; +struct variadic_args; using object = basic_object; using stack_object = basic_object; using userdata = basic_userdata; @@ -377,7 +389,10 @@ template struct lua_type_of> : std::integral_constant{}; template -struct lua_type_of> : std::integral_constant{}; +struct lua_type_of> : std::integral_constant {}; + +template <> +struct lua_type_of : std::integral_constant {}; template struct lua_type_of : std::integral_constant {}; @@ -388,6 +403,9 @@ struct lua_type_of::value>> : std::int template struct lua_type_of::value>> : std::integral_constant {}; +template <> +struct lua_type_of : std::integral_constant {}; + template struct is_lua_primitive : std::integral_constant>::value diff --git a/sol/usertype.hpp b/sol/usertype.hpp index 2ee526d2..ce9c16fa 100644 --- a/sol/usertype.hpp +++ b/sol/usertype.hpp @@ -197,10 +197,15 @@ private: return std::make_unique>(std::move(func.set)); } + template + std::unique_ptr make_function(const std::string&, member_property func) { + return std::make_unique>>(std::move(func)); + } + template std::unique_ptr make_free_function(std::true_type, const std::string&, Fx&& func) { typedef std::decay_t> function_type; - return std::make_unique>(func); + return std::make_unique>(func); } template @@ -219,16 +224,16 @@ private: 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"); + static_assert(std::is_base_of::value, "Any registered member variable must be part of the class"); typedef std::decay_t function_type; - return std::make_unique>(func); + 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"); + static_assert(std::is_base_of::value, "Any registered member function must be part of the class"); typedef std::decay_t function_type; - return std::make_unique>(func); + return std::make_unique>(func); } template @@ -240,7 +245,7 @@ private: template std::unique_ptr make_functor_function(std::true_type, const std::string&, Fx&& func) { typedef std::decay_t> function_type; - return std::make_unique>(func); + return std::make_unique>(func); } template @@ -339,7 +344,7 @@ private: template void build_function(std::string funcname, Fx&& func) { - typedef std::is_member_object_pointer> is_variable; + typedef meta::Or>, meta::is_specialization_of>> is_variable; functionnames.push_back(std::move(funcname)); std::string& name = functionnames.back(); auto baseptr = make_function(name, std::forward(func)); diff --git a/sol/variadic_args.hpp b/sol/variadic_args.hpp new file mode 100644 index 00000000..6a4bef01 --- /dev/null +++ b/sol/variadic_args.hpp @@ -0,0 +1,102 @@ +// The MIT License (MIT) + +// Copyright (c) 2013-2016 Rapptz, ThePhD 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_VARIADIC_ARGS_HPP +#define SOL_VARIADIC_ARGS_HPP + +#include "stack.hpp" +#include "stack_proxy.hpp" + +namespace sol { +struct variadic_args { +private: + lua_State* L; + int index; + int stacktop; + +public: + variadic_args() = default; + variadic_args(lua_State* L, int index = -1): L(L), index(lua_absindex(L, index)), stacktop(lua_gettop(L)) {} + variadic_args(const variadic_args&) = default; + variadic_args& operator=(const variadic_args&) = default; + variadic_args(variadic_args&& o) : L(o.L), index(o.index), stacktop(o.stacktop) { + // 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.stacktop = 0; + } + variadic_args& operator=(variadic_args&& o) { + L = o.L; + index = o.index; + stacktop = o.stacktop; + // 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.stacktop = 0; + return *this; + } + + int push () const { + int pushcount = 0; + for (int i = index; i <= stacktop; ++i) { + lua_pushvalue(L, i); + pushcount += 1; + } + return pushcount; + } + + template + decltype(auto) get(int start = 0) const { + return stack::get(L, index + start); + } + + stack_proxy operator[](int start) const { + return stack_proxy(L, index + start); + } + + lua_State* lua_state() const { return L; }; + int stack_index() const { return index; }; + int leftover_count() const { return stacktop - (index - 1); } + int top() const { return stacktop; } +}; + +namespace stack { +template <> +struct getter { + static variadic_args get(lua_State* L, int index = -1) { + return variadic_args(L, index); + } +}; + +template <> +struct pusher { + static int push(lua_State*, const variadic_args& ref) { + return ref.push(); + } +}; +} // stack +} // sol + +#endif // SOL_VARIADIC_ARGS_HPP diff --git a/test_functions.cpp b/test_functions.cpp index 1d050147..68cd0c18 100644 --- a/test_functions.cpp +++ b/test_functions.cpp @@ -443,11 +443,11 @@ TEST_CASE("functions/all-kinds", "Register all kinds of functions, make sure the }; struct inner { - const int z = 5653; + const int z = 5653; }; struct nested { - inner i; + inner i; }; auto a = []() { return 500; }; @@ -749,3 +749,35 @@ end)"); REQUIRE(b == 2); REQUIRE(c == 3); } + +TEST_CASE("functions/variadic_args", "Check to see we can receive multiple arguments through a variadic") { + struct structure { + int x; + bool b; + }; + + sol::state lua; + lua.open_libraries(sol::lib::base); + lua.set_function("v", [](sol::this_state, sol::variadic_args va) -> structure { + int r = 0; + for (int i = 0; i < va.leftover_count(); ++i) { + int v = va[i]; + r += v; + } + return{ r, r > 200 }; + }); + + lua.script("x = v(25, 25)"); + lua.script("x2 = v(25, 25, 100, 50, 250, 150)"); + lua.script("x3 = v(1, 2, 3, 4, 5, 6)"); + + structure& lx = lua["x"]; + structure& lx2 = lua["x2"]; + structure& lx3 = lua["x3"]; + REQUIRE(lx.x == 50); + REQUIRE(lx2.x == 600); + REQUIRE(lx3.x == 21); + REQUIRE_FALSE(lx.b); + REQUIRE(lx2.b); + REQUIRE_FALSE(lx3.b); +} diff --git a/test_stack_guard.hpp b/test_stack_guard.hpp index 06b4fe43..3fae4e65 100644 --- a/test_stack_guard.hpp +++ b/test_stack_guard.hpp @@ -1,11 +1,11 @@ #pragma once -struct stack_guard { +struct test_stack_guard { lua_State* L; int& begintop; int& endtop; - stack_guard(lua_State* L, int& begintop, int& endtop) : L(L), begintop(begintop), endtop(endtop) { + test_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); } + ~test_stack_guard() { endtop = lua_gettop(L); } }; diff --git a/test_tables.cpp b/test_tables.cpp index 82f10c72..f8990102 100644 --- a/test_tables.cpp +++ b/test_tables.cpp @@ -168,7 +168,7 @@ TEST_CASE("tables/iterators", "Testing the use of iteratrs to get values from a int begintop = 0; int endtop = 0; { - stack_guard s(lua.lua_state(), begintop, endtop); + test_stack_guard s(lua.lua_state(), begintop, endtop); for (auto& kvp : tbl) { [&iterations](sol::object key, sol::object value) { ++iterations; diff --git a/tests.cpp b/tests.cpp index ac6d7c56..79f95883 100644 --- a/tests.cpp +++ b/tests.cpp @@ -192,39 +192,39 @@ TEST_CASE("table/traversal", "ensure that we can chain requests and tunnel down lua.script("t1 = {t2 = {t3 = 24}};"); { - stack_guard g(lua.lua_state(), begintop, endtop); + test_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); + test_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); + test_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); + test_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); + test_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); + test_stack_guard g(lua.lua_state(), begintop, endtop); int x13 = lua["t1"]["t2"]["t3"]; REQUIRE(x13 == 13); } REQUIRE(begintop == endtop); @@ -234,18 +234,18 @@ TEST_CASE("simple/set", "Check if the set works properly.") { sol::state lua; int begintop = 0, endtop = 0; { - stack_guard g(lua.lua_state(), begintop, endtop); + test_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")); { - stack_guard g(lua.lua_state(), begintop, endtop); + test_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")); { - stack_guard g(lua.lua_state(), begintop, endtop); + test_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")); @@ -258,21 +258,21 @@ TEST_CASE("simple/get", "Tests if the get function works properly.") { lua.script("a = 9"); { - stack_guard g(lua.lua_state(), begintop, endtop); + test_stack_guard g(lua.lua_state(), begintop, endtop); auto a = lua.get("a"); REQUIRE(a == 9.0); } REQUIRE(begintop == endtop); lua.script("b = nil"); { - stack_guard g(lua.lua_state(), begintop, endtop); + test_stack_guard g(lua.lua_state(), begintop, endtop); REQUIRE_NOTHROW(lua.get("b")); } REQUIRE(begintop == endtop); lua.script("d = 'hello'"); lua.script("e = true"); { - stack_guard g(lua.lua_state(), begintop, endtop); + test_stack_guard g(lua.lua_state(), begintop, endtop); std::string d; bool e; std::tie( d, e ) = lua.get("d", "e"); @@ -944,3 +944,91 @@ lw2 = bark.something2(2, 3) REQUIRE_THROWS(lua.script("b.var2 = 2")); } + +TEST_CASE("usertype/properties", "Check if member properties/variables work") { + struct bark { + int var = 50; + int var2 = 25; + + int get_var2() const { + return var2; + } + + int get_var3() { + return var2; + } + + void set_var2( int x ) { + var2 = x; + } + }; + + sol::state lua; + lua.open_libraries(sol::lib::base); + lua.new_usertype("bark", + "var", &bark::var, + "var2", sol::readonly( &bark::var2 ), + "a", sol::property(&bark::get_var2, &bark::set_var2), + "b", sol::property(&bark::get_var2), + "c", sol::property(&bark::get_var3), + "d", sol::property(&bark::set_var2) + ); + + bark b; + lua.set("b", &b); + + lua.script("b.a = 59"); + lua.script("var2_0 = b.a"); + lua.script("var2_1 = b.b"); + lua.script("b.d = 1568"); + lua.script("var2_2 = b.c"); + + int var2_0 = lua["var2_0"]; + int var2_1 = lua["var2_1"]; + int var2_2 = lua["var2_2"]; + REQUIRE(var2_0 == 59); + REQUIRE(var2_1 == 59); + REQUIRE(var2_2 == 1568); + + REQUIRE_THROWS(lua.script("b.var2 = 24")); + REQUIRE_THROWS(lua.script("r = b.d")); + REQUIRE_THROWS(lua.script("r = b.d")); + REQUIRE_THROWS(lua.script("b.b = 25")); + REQUIRE_THROWS(lua.script("b.c = 11")); +} + +TEST_CASE("utilities/this_state", "Ensure this_state argument can be gotten anywhere in the function.") { + struct bark { + int with_state(sol::this_state l, int a, int b) { + lua_State* L = l; + int c = lua_gettop(L); + return a + b + (c - c); + } + + static int with_state_2(int a, sol::this_state l, int b) { + lua_State* L = l; + int c = lua_gettop(L); + return a * b + (c - c); + } + }; + + sol::state lua; + lua.open_libraries(sol::lib::base); + lua.new_usertype("bark", + "with_state", &bark::with_state + ); + + bark b; + lua.set("b", &b); + lua.set("with_state_2", bark::with_state_2); + sol::function fx = lua["with_state_2"]; + int a = fx(25, 25); + lua.script("a = with_state_2(25, 25)"); + lua.script("c = b:with_state(25, 25)"); + int la = lua["a"]; + int lc = lua["c"]; + + REQUIRE(lc == 50); + REQUIRE(a == 625); + REQUIRE(la == 625); +}