diff --git a/sol/call.hpp b/sol/call.hpp index 7a837825..aced5c19 100644 --- a/sol/call.hpp +++ b/sol/call.hpp @@ -30,11 +30,6 @@ namespace sol { namespace call_detail { - template - inline decltype(auto) pick(std::integral_constant, F&& f) { - return std::forward(f); - } - template inline auto& pick(std::true_type, property_wrapper& f) { return f.read; @@ -76,12 +71,12 @@ namespace sol { namespace overload_detail { template - inline int overload_match_arity(sol::types<>, std::index_sequence<>, std::index_sequence, Match&&, lua_State* L, int, int, Args&&...) { + inline int overload_match_arity(types<>, std::index_sequence<>, std::index_sequence, Match&&, lua_State* L, int, int, Args&&...) { return luaL_error(L, "sol: no matching function call takes this number of arguments and the specified types"); } template - inline int overload_match_arity(sol::types, std::index_sequence, std::index_sequence, Match&& matchfx, lua_State* L, int fxarity, int start, Args&&... args) { + inline int overload_match_arity(types, std::index_sequence, std::index_sequence, Match&& matchfx, lua_State* L, int fxarity, int start, Args&&... args) { typedef lua_bind_traits> traits; typedef meta::tuple_types return_types; typedef typename traits::free_args_list args_list; @@ -146,41 +141,51 @@ namespace sol { template struct agnostic_lua_call_wrapper { - template - static int var_call(std::true_type, lua_State* L, Fx&& f) { + template + static int call(lua_State* L, Fx&& f, Args&&... args) { typedef wrapper> wrap; typedef typename wrap::returns_list returns_list; typedef typename wrap::free_args_list args_list; typedef typename wrap::caller caller; - return stack::call_into_lua(returns_list(), args_list(), L, boost + ( is_index ? 2 : 3 ), caller(), std::forward(f)); - } - - template - static int var_call(std::false_type, lua_State* L, Fx&& f) { - typedef wrapper> wrap; - typedef typename wrap::free_args_list args_list; - typedef typename wrap::returns_list returns_list; - typedef typename wrap::caller caller; - return stack::call_into_lua(returns_list(), args_list(), L, boost + 1, caller(), std::forward(f)); - } - - template - static int call(lua_State* L, Fx&& f) { - return var_call(std::integral_constant(), L, std::forward(f)); + return stack::call_into_lua(returns_list(), args_list(), L, boost + 1, caller(), std::forward(f), std::forward(args)...); } }; - template - struct agnostic_lua_call_wrapper, is_index, is_variable, checked, boost, C> { + template + struct agnostic_lua_call_wrapper, true, is_variable, checked, boost, C> { template static int call(lua_State* L, F&& f) { - if (is_index) { - return stack::push(L, detail::unwrap(f.value)); - } - else { - detail::unwrap(f.value) = stack::get>(L, 3 + boost); - return 0; - } + return stack::push_reference(L, detail::unwrap(f.value)); + } + }; + + template + struct agnostic_lua_call_wrapper, false, is_variable, checked, boost, C> { + template + static int call_assign(std::true_type, lua_State* L, V&& f) { + detail::unwrap(f.value) = stack::get>(L, boost + (is_variable ? 3 : 1)); + return 0; + } + + template + static int call_assign(std::false_type, lua_State* L, Args&&...) { + return luaL_error(L, "sol: cannot write to this variable: copy assignment/constructor not available"); + } + + template + static int call_const(std::false_type, lua_State* L, Args&&... args) { + typedef meta::unwrapped_t R; + return call_assign(std::is_assignable>, R>(), L, std::forward(args)...); + } + + template + static int call_const(std::true_type, lua_State* L, Args&&...) { + return luaL_error(L, "sol: cannot write to a readonly (const) variable"); + } + + template + static int call(lua_State* L, V&& f) { + return call_const(std::is_const>(), L, f); } }; @@ -254,7 +259,7 @@ namespace sol { template struct lua_call_wrapper::value>> { - typedef sol::lua_bind_traits traits_type; + typedef lua_bind_traits traits_type; typedef wrapper> wrap; typedef typename wrap::object_type object_type; @@ -312,7 +317,7 @@ namespace sol { template struct lua_call_wrapper::value>> { - typedef sol::lua_bind_traits traits_type; + typedef lua_bind_traits traits_type; typedef wrapper> wrap; typedef typename wrap::object_type object_type; @@ -343,8 +348,8 @@ namespace sol { }; template - struct lua_call_wrapper, is_index, is_variable, checked, boost, C> { - typedef sol::constructor_list F; + struct lua_call_wrapper, is_index, is_variable, checked, boost, C> { + typedef constructor_list F; static int call(lua_State* L, F&) { const auto& metakey = usertype_traits::metatable; @@ -373,8 +378,8 @@ namespace sol { }; template - struct lua_call_wrapper, is_index, is_variable, checked, boost, C> { - typedef sol::constructor_wrapper F; + struct lua_call_wrapper, is_index, is_variable, checked, boost, C> { + typedef constructor_wrapper F; struct onmatch { template @@ -413,8 +418,8 @@ namespace sol { }; template - struct lua_call_wrapper, is_index, is_variable, checked, boost, std::enable_if_t::value>> { - typedef sol::destructor_wrapper F; + struct lua_call_wrapper, is_index, is_variable, checked, boost, std::enable_if_t::value>> { + typedef destructor_wrapper F; static int call(lua_State* L, const F&) { return destruct(L); @@ -422,8 +427,8 @@ namespace sol { }; template - struct lua_call_wrapper, is_index, is_variable, checked, boost, std::enable_if_t::value>> { - typedef sol::destructor_wrapper F; + struct lua_call_wrapper, is_index, is_variable, checked, boost, std::enable_if_t::value>> { + typedef destructor_wrapper F; static int call(lua_State* L, const F& f) { T& obj = stack::get(L); @@ -449,6 +454,60 @@ namespace sol { } }; + template + struct lua_call_wrapper, is_index, is_variable, checked, boost, C> { + typedef std::conditional_t P; + typedef meta::unqualified_t

U; + typedef lua_bind_traits traits_type; + + template + static int self_call(lua_State* L, F&& f) { + typedef wrapper wrap; + typedef meta::unqualified_t> object_type; + typedef meta::pop_front_type_t args_list; + typedef T Ta; +#ifdef SOL_SAFE_USERTYPE + object_type* po = static_cast(stack::get(L, 1)); + if (po == nullptr) { + if (is_variable) { + return luaL_error(L, "sol: 'self' argument is nil (bad '.' access?)"); + } + return luaL_error(L, "sol: 'self' argument is nil (pass 'self' as first argument)"); + } + object_type& o = *po; +#else + object_type& o = static_cast(stack::get(L, 1)); +#endif // Safety + typedef typename wrap::returns_list returns_list; + typedef typename wrap::caller caller; + return stack::call_into_lua(returns_list(), args_list(), L, boost + (is_variable ? 3 : 2), caller(), f, o); + } + + template + static int defer_call(std::false_type, lua_State* L, F&& f, Args&&... args) { + return self_call(L, pick(meta::boolean(), f), std::forward(args)...); + } + + template + static int defer_call(std::true_type, lua_State* L, F&& f, Args&&... args) { + auto& p = pick(meta::boolean(), std::forward(f)); + return lua_call_wrapper, is_index, is_variable, checked, boost>{}.call(L, p, std::forward(args)...); + } + + template + static int call(lua_State* L, F&& f, Args&&... args) { + typedef meta::any< + std::is_void, + std::is_same, + meta::is_specialization_of, + meta::is_specialization_of, + meta::is_specialization_of, + std::is_member_pointer + > is_specialized; + return defer_call(is_specialized(), L, std::forward(f), std::forward(args)...); + } + }; + template struct lua_call_wrapper, is_index, is_variable, checked, boost, C> { typedef protect_t F; diff --git a/sol/property.hpp b/sol/property.hpp index 5a785d3c..703cac92 100644 --- a/sol/property.hpp +++ b/sol/property.hpp @@ -60,17 +60,20 @@ namespace sol { template inline decltype(auto) property(F&& f, G&& g) { - using namespace sol; typedef lua_bind_traits> left_traits; typedef lua_bind_traits> right_traits; - return property_detail::property(meta::boolean<(left_traits::arity < right_traits::arity)>(), std::forward(f), std::forward(g)); + return property_detail::property(meta::boolean<(left_traits::free_arity < right_traits::free_arity)>(), std::forward(f), std::forward(g)); } template inline decltype(auto) property(F&& f) { - using namespace sol; typedef lua_bind_traits> left_traits; - return property_detail::property(meta::boolean<(left_traits::arity == 0)>(), std::forward(f)); + return property_detail::property(meta::boolean<(left_traits::free_arity < 2)>(), std::forward(f)); + } + + template + inline decltype(auto) readonly_property(F&& f) { + return property_detail::property(std::true_type(), std::forward(f)); } // Allow someone to make a member variable readonly (const) diff --git a/sol/usertype_metatable.hpp b/sol/usertype_metatable.hpp index 179cba72..7ea66ddd 100644 --- a/sol/usertype_metatable.hpp +++ b/sol/usertype_metatable.hpp @@ -287,7 +287,7 @@ namespace sol { template static int real_call_with(lua_State* L, usertype_metatable& um) { - auto& f = call_detail::pick(std::integral_constant(), std::get(um.functions)); + auto& f = std::get(um.functions); return call_detail::call_wrapped(L, f); } diff --git a/test_usertypes.cpp b/test_usertypes.cpp index d90129bd..a7e783c8 100644 --- a/test_usertypes.cpp +++ b/test_usertypes.cpp @@ -1298,3 +1298,45 @@ print(test.ref_global2) REQUIRE(rtv == 35); REQUIRE(through_variable == 35); } + +TEST_CASE("usertypes/var-and-property", "make sure const vars are readonly and properties can handle lambdas") { + const static int arf = 20; + + struct test { + int value = 10; + }; + + sol::state lua; + lua.open_libraries(); + + lua.new_usertype("test", + "prop", sol::property( + [](test& t) { + return t.value; + }, + [](test& t, int x) { + t.value = x; + } + ), + "global", sol::var(std::ref(arf)) + ); + + lua.script(R"( +t = test.new() +print(t.prop) +t.prop = 50 +print(t.prop) + )"); + + test& t = lua["t"]; + REQUIRE(t.value == 50); + + + REQUIRE_THROWS( + lua.script(R"( +t = test.new() +print(t.global) +t.global = 20 +print(t.global) + )")); +}