diff --git a/docs/source/features.rst b/docs/source/features.rst index 33ff8345..77e360bc 100644 --- a/docs/source/features.rst +++ b/docs/source/features.rst @@ -87,7 +87,7 @@ Explanations for a few categories are below (rest are self-explanatory). +===========================+=============+============+==========+=========+==========+===========+===========+================+==========+==========+===========+=================+ | optional | ~ | ✗ | ✗ | ✗ | ✗ | ✔ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | +---------------------------+-------------+------------+----------+---------+----------+-----------+-----------+----------------+----------+----------+-----------+-----------------+ -| tables | ~ | ~ | ✔ | ✔ | ✔ | ✔ | ~ | ✔ | ✔ | ✗ | ✗ | ~ | +| tables | ~ | ~ | ~ | ✔ | ✔ | ✔ | ~ | ✔ | ✔ | ✗ | ✗ | ~ | +---------------------------+-------------+------------+----------+---------+----------+-----------+-----------+----------------+----------+----------+-----------+-----------------+ | table chaining | ~ | ~ | ~ | ✔ | ✔ | ✔ | ✗ | ✔ | ✔ | ✗ | ✗ | ~ | +---------------------------+-------------+------------+----------+---------+----------+-----------+-----------+----------------+----------+----------+-----------+-----------------+ diff --git a/docs/source/tutorial/getting-started.rst b/docs/source/tutorial/getting-started.rst index bb78d61e..aac971c0 100644 --- a/docs/source/tutorial/getting-started.rst +++ b/docs/source/tutorial/getting-started.rst @@ -41,7 +41,24 @@ Or using your favorite IDE / tool after setting up your include paths and librar If you get an avalanche of errors (particularly referring to ``auto``), you may not have enabled C++14 / C++17 mode for your compiler. Add one of ``std=c++14``, ``std=c++1z`` OR ``std=c++1y`` to your compiler options. By default, this is always-on for VC++ compilers in Visual Studio and friends, but g++ and clang++ require a flag (unless you're on `GCC 6.0`_). -If this works, you're ready to start! The first line creates the ``lua_State`` and will hold onto it for the duration of the scope its declared in (e.g., from the opening ``{`` to the closing ``}``). It will automatically close / cleanup that lua state when it gets destructed. The second line opens a single lua-provided library, "base". There are several other libraries that come with lua that you can open by default, and those are included in the :ref:`sol::lib` enumeration. +If this works, you're ready to start! The first line creates the ``lua_State`` and will hold onto it for the duration of the scope its declared in (e.g., from the opening ``{`` to the closing ``}``). It will automatically close / cleanup that lua state when it gets destructed. The second line opens a single lua-provided library, "base". There are several other libraries that come with lua that you can open by default, and those are included in the :ref:`sol::lib` enumeration. You can open multiple base libraries by specifying multiple ``sol::lib`` arguments: + +.. code-block:: cpp + :linenos: + :caption: test.cpp: the first snippet + :name: the-first-snippet + + #include + + int main (int argc, char* argv[]) { + + sol::state lua; + lua.open_libraries( sol::lib::base, sol::lib::coroutine, sol::lib::string, sol::lib::io ); + + lua.script( "print('bark bark bark!')" ); + + return 0; + } If you're interested in integrating Sol with a project that already uses some other library or Lua in the codebase, check out the :doc:`existing example` to see how to work with Sol when you add it to a project! diff --git a/docs/source/tutorial/variables.rst b/docs/source/tutorial/variables.rst index d1c5aa56..20105bf8 100644 --- a/docs/source/tutorial/variables.rst +++ b/docs/source/tutorial/variables.rst @@ -124,7 +124,7 @@ Writing gets a lot simpler. Even without scripting a file or a string, you can r sol::state lua; // open those basic lua libraries again, like print() etc. - lua.open_libs( sol::lib::base ); + lua.open_libraries( sol::lib::base ); // value in the global table lua["bark"] = 50; @@ -136,7 +136,8 @@ Writing gets a lot simpler. Even without scripting a file or a string, you can r lua["bark"], "the key is 50 and this string is its value!" ); - // Run a plain ol' string + // Run a plain ol' string of lua code + // Note you can interact with things set through Sol in C++ with lua! // Using a "Raw String Literal" to have multi-line goodness: http://en.cppreference.com/w/cpp/language/string_literal lua.script(R"( diff --git a/sol/coroutine.hpp b/sol/coroutine.hpp index 635192b2..65bcbd59 100644 --- a/sol/coroutine.hpp +++ b/sol/coroutine.hpp @@ -67,14 +67,14 @@ private: } public: - coroutine(lua_State* L, int index = -1) : reference(L, index) { -#ifdef SOL_CHECK_ARGUMENTS - type_assert(L, index, type::function); -#endif // Safety - } coroutine() noexcept = default; coroutine(const coroutine&) noexcept = default; coroutine& operator=(const coroutine&) noexcept = default; + coroutine(lua_State* L, int index = -1) : reference(L, index) { +#ifdef SOL_CHECK_ARGUMENTS + stack::check(L, index, type_panic); +#endif // Safety + } call_status status() const noexcept { return stats; diff --git a/sol/function.hpp b/sol/function.hpp index 7309e5bf..3b31c53f 100644 --- a/sol/function.hpp +++ b/sol/function.hpp @@ -73,7 +73,7 @@ public: basic_function& operator=(basic_function&& ) = default; basic_function(lua_State* L, int index = -1): base_t(L, index) { #ifdef SOL_CHECK_ARGUMENTS - type_assert(L, index, type::function); + stack::check(L, index, type_panic); #endif // Safety } diff --git a/sol/function_types.hpp b/sol/function_types.hpp index 6c67421f..c37fb615 100644 --- a/sol/function_types.hpp +++ b/sol/function_types.hpp @@ -46,6 +46,13 @@ function_arguments function_args( Args&&... args ) { return function_arguments(std::forward(args)...); } +// Allow someone to make a member variable readonly (const) +template +auto readonly( R T::* v ) { + typedef const R C; + return static_cast( v ); +} + namespace stack { template struct pusher> { @@ -107,7 +114,7 @@ struct pusher> { select_reference_member_variable(is_reference(), L, std::forward(fx), std::forward(obj), std::forward(args)...); } - template + template static void select_member_variable(std::true_type, lua_State* L, Fx&& fx) { typedef typename meta::bind_traits>::object_type C; lua_CFunction freefunc = &function_detail::upvalue_this_member_variable::call; @@ -145,7 +152,7 @@ struct pusher> { select_reference_member_function(is_reference(), L, std::forward(fx), std::forward(obj), std::forward(args)...); } - template + template static void select_member_function(std::true_type, lua_State* L, Fx&& fx) { typedef typename meta::bind_traits>::object_type C; lua_CFunction freefunc = &function_detail::upvalue_this_member_function::call; @@ -211,7 +218,23 @@ struct pusher> { template struct pusher> { static int push(lua_State* L, std::function fx) { - return pusher>{}.push(L, std::move(fx)); + return pusher>{}.push(L, std::move(fx)); + } +}; + +template +struct pusher::value>> { + template + static int push(lua_State* L, F&& f) { + return pusher>{}.push(L, std::forward(f)); + } +}; + +template +struct pusher, meta::Not>, meta::Not>>>::value>> { + template + static int push(lua_State* L, F&& f) { + return pusher>{}.push(L, std::forward(f)); } }; diff --git a/sol/function_types_allocator.hpp b/sol/function_types_allocator.hpp index 1869b72e..e9470985 100644 --- a/sol/function_types_allocator.hpp +++ b/sol/function_types_allocator.hpp @@ -43,7 +43,7 @@ struct constructor_match { constructor_match(T* obj) : obj(obj) {} template - int operator()(types, Index, types r, types a, lua_State* L, int, int start) const { + int operator()(types, index_value, types r, types a, lua_State* L, int, int start) const { default_construct func{}; return stack::call_into_lua<0, false>(r, a, L, start, func, obj); } @@ -105,7 +105,7 @@ struct usertype_constructor_function : base_function { usertype_constructor_function(Functions... fxs) : overloads(fxs...) {} template - int call(types, Index, types r, types a, lua_State* L, int, int start) { + int call(types, index_value, 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; diff --git a/sol/function_types_basic.hpp b/sol/function_types_basic.hpp index 3638b575..8d2da650 100644 --- a/sol/function_types_basic.hpp +++ b/sol/function_types_basic.hpp @@ -77,6 +77,30 @@ 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"); +} + +template +int set_assignable(std::true_type, lua_State* L, M& mem, V& var) { + (mem.*var) = stack::get(L, N); + lua_pop(L, N); + return 0; +} + +template +int set_variable(std::true_type, lua_State* L, M& mem, V& var) { + return set_assignable(std::is_assignable, R>(), L, mem, 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"); +} + template struct upvalue_member_variable { typedef std::remove_pointer_t> function_type; @@ -97,8 +121,7 @@ struct upvalue_member_variable { stack::push(L, (mem.*var)); return 1; case 1: - (mem.*var) = stack::get(L, 1); - lua_pop(L, 1); + set_variable<1, typename traits_type::return_type>(meta::Not>(), L, mem, var); return 0; default: return luaL_error(L, "sol: incorrect number of arguments to member variable function"); @@ -158,8 +181,7 @@ struct upvalue_this_member_variable { stack::push(L, (mem.*var)); return 1; case 2: - (mem.*var) = stack::get(L, 2); - lua_pop(L, 2); + set_variable<2, typename traits_type::return_type>(meta::Not>(), L, mem, var); return 0; default: return luaL_error(L, "sol: incorrect number of arguments to member variable function"); diff --git a/sol/function_types_member.hpp b/sol/function_types_member.hpp index ed8cc4ab..4f38dc1a 100644 --- a/sol/function_types_member.hpp +++ b/sol/function_types_member.hpp @@ -26,6 +26,26 @@ namespace sol { namespace function_detail { +template +struct free_function : public base_function { + typedef meta::Unwrapped> Function; + typedef meta::function_return_t return_type; + typedef meta::function_args_t args_types; + Function fx; + + template + free_function(Args&&... args): fx(std::forward(args)...) {} + + int call(lua_State* L) { + return stack::call_into_lua(meta::tuple_types(), args_types(), L, 1, fx); + } + + virtual int operator()(lua_State* L) override { + auto f = [&](lua_State* L) -> int { return this->call(L);}; + return detail::trampoline(L, f); + } +}; + template struct functor_function : public base_function { typedef meta::Unwrapped> Function; diff --git a/sol/function_types_overload.hpp b/sol/function_types_overload.hpp index 19295975..5157c6c9 100644 --- a/sol/function_types_overload.hpp +++ b/sol/function_types_overload.hpp @@ -51,7 +51,7 @@ inline int overload_match_arity(types, std::index_sequence typedef typename 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 (meta::find_in_pack_v, Index...>::value) { + if (meta::find_in_pack_v, index_value...>::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) { @@ -60,7 +60,7 @@ inline int overload_match_arity(types, std::index_sequence if (!stack::stack_detail::check_types().check(args_type(), args_indices(), L, start, no_panic)) { return overload_match_arity(types(), std::index_sequence(), std::index_sequence(), std::forward(matchfx), L, fxarity, start, std::forward(args)...); } - return matchfx(types(), Index(), return_types(), args_type(), L, fxarity, start, std::forward(args)...); + return matchfx(types(), index_value(), return_types(), args_type(), L, fxarity, start, std::forward(args)...); } } // internals @@ -90,7 +90,7 @@ struct overloaded_function : base_function { } template - int call(types, Index, types r, types a, lua_State* L, int, int start) { + int call(types, index_value, types r, types a, lua_State* L, int, int start) { auto& func = std::get(overloads); return stack::call_into_lua<0, false>(r, a, L, start, func); } @@ -110,7 +110,7 @@ struct usertype_overloaded_function : base_function { usertype_overloaded_function(std::tuple set) : overloads(std::move(set)) {} template - int call(types, Index, types r, types a, lua_State* L, int, int start) { + int call(types, index_value, types r, types a, lua_State* L, int, int start) { auto& func = std::get(overloads); func.item = detail::ptr(stack::get(L, 1)); return stack::call_into_lua<0, false>(r, a, L, start, func); diff --git a/sol/function_types_usertype.hpp b/sol/function_types_usertype.hpp index 115fcf7a..dd77284d 100644 --- a/sol/function_types_usertype.hpp +++ b/sol/function_types_usertype.hpp @@ -64,7 +64,7 @@ struct usertype_function_core : public base_function { } template - int operator()(types tr, types ta, Index, lua_State* L) { + 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); @@ -72,7 +72,7 @@ struct usertype_function_core : public base_function { } template - int operator()(types tr, types ta, Index, lua_State* L) { + 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); @@ -94,7 +94,7 @@ struct usertype_function : public usertype_function_core { int prelude(lua_State* L) { this->fx.item = detail::ptr(stack::get(L, 1)); - return static_cast(*this)(meta::tuple_types(), args_type(), Index<2>(), L); + return static_cast(*this)(meta::tuple_types(), args_type(), index_value<2>(), L); } virtual int operator()(lua_State* L) override { @@ -119,7 +119,7 @@ struct usertype_variable_function : public usertype_function_core } int set_assignable(std::true_type, lua_State* L) { - return static_cast(*this)(meta::tuple_types(), args_type(), Index<3>(), L); + return static_cast(*this)(meta::tuple_types(), args_type(), index_value<3>(), L); } int set_variable(std::false_type, lua_State* L) { @@ -136,7 +136,7 @@ struct usertype_variable_function : public usertype_function_core this->fx.item = stack::get(L, 1); switch(argcount) { case 2: - return static_cast(*this)(meta::tuple_types(), types<>(), Index<2>(), L); + return static_cast(*this)(meta::tuple_types(), types<>(), index_value<2>(), L); case 3: return set_variable(meta::Not>(), L); default: diff --git a/sol/protected_function.hpp b/sol/protected_function.hpp index 981498f1..0de58c87 100644 --- a/sol/protected_function.hpp +++ b/sol/protected_function.hpp @@ -137,7 +137,7 @@ public: basic_protected_function& operator=(basic_protected_function&& ) = default; basic_protected_function(lua_State* L, int index = -1): base_t(L, index), error_handler(get_default_handler()) { #ifdef SOL_CHECK_ARGUMENTS - type_assert(L, index, type::function); + stack::check(L, index, type_panic); #endif // Safety } diff --git a/sol/stack_check.hpp b/sol/stack_check.hpp index a7ca4c63..363cb48f 100644 --- a/sol/stack_check.hpp +++ b/sol/stack_check.hpp @@ -151,12 +151,60 @@ struct checker { template struct checker, type::userdata, C> : checker::value, C> {}; -template -struct checker : stack_detail::basic_check {}; -template -struct checker, X, C> : checker {}; -template -struct checker : checker {}; +template +struct checker : stack_detail::basic_check {}; +template +struct checker, type::function, C> : checker {}; +template +struct checker : checker {}; + +template +struct checker { + template + static bool check(lua_State* L, int index, Handler&& handler) { + type t = type_of(L, index); + if (t == type::function) { + return true; + } + if (t != type::userdata && t != type::table) { + handler(L, index, t, type::function); + return false; + } + // Do advanced check for call-style userdata? + static const auto& callkey = name_of(meta_function::call); + lua_getmetatable(L, index); + if (lua_isnoneornil(L, -1)) { + handler(L, index, t, type::function); + lua_pop(L, 1); + return false; + } + lua_getfield(L, -1, &callkey[0]); + if (lua_isnoneornil(L, -1)) { + handler(L, index, t, type::function); + lua_pop(L, 2); + return false; + } + // has call, is definitely a function + lua_pop(L, 2); + return true; + } +}; + +template +struct checker { + template + static bool check(lua_State* L, int index, Handler&& handler) { + type t = type_of(L, index); + if (t == type::table) { + return true; + } + if (t != type::userdata) { + handler(L, index, t, type::function); + return false; + } + return true; + } +}; template struct checker { diff --git a/sol/stack_field.hpp b/sol/stack_field.hpp index f31ae354..de761fe8 100644 --- a/sol/stack_field.hpp +++ b/sol/stack_field.hpp @@ -38,6 +38,13 @@ struct field_getter { } }; +template +struct field_getter { + void get(lua_State* L, metatable_key_t, int tableindex = -1) { + lua_getmetatable( L, tableindex ); + } +}; + template struct field_getter, b, C> { template @@ -117,6 +124,15 @@ struct field_setter { } }; +template +struct field_setter { + template + void set(lua_State* L, metatable_key_t, Value&& value, int tableindex = -2) { + push(L, std::forward(value)); + lua_setmetatable(L, tableindex); + } +}; + template struct field_setter::value>> { template diff --git a/sol/stack_get.hpp b/sol/stack_get.hpp index 29ef5ef1..fbbfa23f 100644 --- a/sol/stack_get.hpp +++ b/sol/stack_get.hpp @@ -134,6 +134,13 @@ struct getter { } }; +template<> +struct pusher { + static std::nullptr_t get(lua_State*, int = -1) { + return nullptr; + } +}; + template<> struct getter { static nullopt_t get(lua_State*, int = -1) { diff --git a/sol/stack_push.hpp b/sol/stack_push.hpp index 6a09d648..8db179ab 100644 --- a/sol/stack_push.hpp +++ b/sol/stack_push.hpp @@ -208,6 +208,14 @@ struct pusher { } }; +template<> +struct pusher { + static int push(lua_State* L, metatable_key_t) { + lua_pushlstring(L, "__mt", 4); + return 1; + } +}; + template<> struct pusher> { static int push(lua_State* L, lua_CFunction func, int n = 0) { diff --git a/sol/table_core.hpp b/sol/table_core.hpp index e97ae558..b0fbedbd 100644 --- a/sol/table_core.hpp +++ b/sol/table_core.hpp @@ -159,7 +159,7 @@ public: basic_table_core(stack_reference&& r) : basic_table_core(r.lua_state(), r.stack_index()) {} basic_table_core( lua_State* L, int index = -1 ) : base_t( L, index ) { #ifdef SOL_CHECK_ARGUMENTS - type_assert( L, index, type::table ); + stack::check(L, index, type_panic); #endif // Safety } diff --git a/sol/traits.hpp b/sol/traits.hpp index 436b8d9b..401d4ec1 100644 --- a/sol/traits.hpp +++ b/sol/traits.hpp @@ -29,7 +29,7 @@ namespace sol { template -using Index = std::integral_constant; +using index_value = std::integral_constant; namespace meta { template @@ -120,6 +120,9 @@ using Unqualified = std::remove_cv_t>; template using Unwrapped = typename unwrapped::type; +template +using tuple_element = std::tuple_element>; + template using tuple_element_t = std::tuple_element_t>; @@ -237,6 +240,15 @@ struct Function : Bool::value> {}; namespace meta_detail { +template +struct void_tuple_element : meta::tuple_element {}; + +template +struct void_tuple_element> { typedef void type; }; + +template +using void_tuple_element_t = typename void_tuple_element::type; + template::value> struct fx_traits; @@ -258,7 +270,7 @@ struct fx_traits { typedef R return_type; typedef std::remove_pointer_t signature_type; template - using arg = meta::tuple_element_t; + using arg_at = void_tuple_element_t; }; template @@ -274,7 +286,7 @@ struct fx_traits { typedef R return_type; typedef std::remove_pointer_t signature_type; template - using arg = meta::tuple_element_t; + using arg_at = void_tuple_element_t; }; template @@ -289,7 +301,7 @@ struct fx_traits { typedef R return_type; typedef std::remove_pointer_t signature_type; template - using arg = meta::tuple_element_t; + using arg_at = void_tuple_element_t; }; template @@ -304,7 +316,7 @@ struct fx_traits { typedef R return_type; typedef std::remove_pointer_t signature_type; template - using arg = meta::tuple_element_t; + using arg_at = void_tuple_element_t; }; template::value> @@ -326,7 +338,7 @@ struct callable_traits { typedef R(*function_pointer_type)(Arg); typedef R(*free_function_pointer_type)(Arg); template - using arg = meta::tuple_element_t; + using arg_at = void_tuple_element_t; }; } // meta_detail diff --git a/sol/types.hpp b/sol/types.hpp index f0d48b14..e067eb97 100644 --- a/sol/types.hpp +++ b/sol/types.hpp @@ -25,6 +25,7 @@ #include "compatibility.hpp" #include "traits.hpp" #include "optional.hpp" +#include #include namespace sol { @@ -85,6 +86,8 @@ inline int c_trampoline(lua_State* L, lua_CFunction f) { } struct nil_t {}; const nil_t nil {}; +struct metatable_key_t {}; +const metatable_key_t metatable_key = {}; inline bool operator==(nil_t, nil_t) { return true; } inline bool operator!=(nil_t, nil_t) { return false; } @@ -168,6 +171,67 @@ enum class type : int { table | boolean | function | userdata | lightuserdata }; +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 = call, +}; + +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", +} }; + +inline const std::string& name_of( meta_function mf ) { + return meta_function_names[static_cast(mf)]; +} + +inline type type_of(lua_State* L, int index) { + return static_cast(lua_type(L, index)); +} + inline int type_panic(lua_State* L, int index, type expected, type actual) { return luaL_error(L, "stack index %d, expected %s, received %s", index, expected == type::poly ? "anything" : lua_typename(L, static_cast(expected)), @@ -195,13 +259,11 @@ inline void type_assert(lua_State* L, int index, type expected, type actual) { } inline void type_assert(lua_State* L, int index, type expected) { - int actual = lua_type(L, index); - if(expected != type::poly && static_cast(expected) != actual) { - type_error(L, static_cast(expected), actual); - } + type actual = type_of(L, index); + type_assert(L, index, expected, actual); } -inline std::string type_name(lua_State*L, type t) { +inline std::string type_name(lua_State* L, type t) { return lua_typename(L, static_cast(t)); } @@ -263,17 +325,8 @@ struct lua_type_of : std::integral_constant { }; template <> struct lua_type_of : std::integral_constant { }; -template <> -struct lua_type_of : std::integral_constant { }; - -template <> -struct lua_type_of : std::integral_constant { }; - -template <> -struct lua_type_of : std::integral_constant { }; - -template <> -struct lua_type_of : std::integral_constant { }; +template +struct lua_type_of> : std::integral_constant { }; template <> struct lua_type_of : std::integral_constant {}; @@ -305,6 +358,9 @@ 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 {}; @@ -364,10 +420,6 @@ template inline type type_of() { return lua_type_of>::value; } - -inline type type_of(lua_State* L, int index) { - return static_cast(lua_type(L, index)); -} } // sol #endif // SOL_TYPES_HPP diff --git a/sol/usertype.hpp b/sol/usertype.hpp index 4f542ae9..2ee526d2 100644 --- a/sol/usertype.hpp +++ b/sol/usertype.hpp @@ -34,60 +34,6 @@ #include namespace sol { -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", -} }; - -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 { struct add_destructor_tag {}; struct check_destructor_tag {}; @@ -151,6 +97,13 @@ inline void push_metatable(lua_State* L, bool needsindexfunction, std::vector(functable.size())); - int up = push_upvalues(L, funcs); + up = push_upvalues(L, funcs); functable.push_back({nullptr, nullptr}); luaL_setfuncs(L, functable.data(), up); functable.pop_back(); @@ -244,12 +197,24 @@ private: return std::make_unique>(std::move(func.set)); } - template - std::unique_ptr make_function(const std::string&, Ret(*func)(Arg, Args...)) { - typedef meta::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_free_function(std::true_type, const std::string&, Fx&& func) { + typedef std::decay_t> function_type; + return std::make_unique>(func); + } + + template + std::unique_ptr make_free_function(std::false_type, const std::string&, Fx&& func) { + typedef std::decay_t> function_type; + return std::make_unique>(func); + } + + template + std::unique_ptr make_function(const std::string& name, Ret(*func)(Args...)) { + typedef meta::bind_traits btraits; + typedef typename btraits::template arg_at<0> Argu; + typedef meta::Unqualified> Arg0; + return make_free_function(std::is_base_of(), name, func); } template @@ -273,13 +238,24 @@ private: } template - std::unique_ptr make_function(const std::string&, Fx&& func) { + 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); + } + + template + std::unique_ptr make_functor_function(std::false_type, const std::string&, Fx&& func) { + typedef std::decay_t> function_type; + return std::make_unique>(func); + } + + template + std::unique_ptr make_function(const std::string& name, Fx&& func) { typedef meta::Unqualified Fxu; - typedef meta::tuple_element_t<0, typename meta::bind_traits::args_tuple_type> Arg0; - typedef meta::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); + typedef meta::bind_traits btraits; + typedef typename btraits::template arg_at<0> Argu; + typedef meta::Unqualified> Arg0; + return make_functor_function(std::is_base_of(), name, std::forward(func)); } template diff --git a/test_functions.cpp b/test_functions.cpp index 0cf591bd..1d050147 100644 --- a/test_functions.cpp +++ b/test_functions.cpp @@ -442,6 +442,14 @@ TEST_CASE("functions/all-kinds", "Register all kinds of functions, make sure the } }; + struct inner { + const int z = 5653; + }; + + struct nested { + inner i; + }; + auto a = []() { return 500; }; auto b = [&]() { return 501; }; auto c = [&]() { return 502; }; @@ -568,6 +576,22 @@ N = n(1, 2, 3) REQUIRE( M1 == 256 ); REQUIRE( N == 13 ); + + // Work that compiler, WORK IT! + lua.set("o", &test_1::bark); + lua.set("p", test_1::x_bark); + lua.set("q", sol::c_call); + lua.set("r", &test_2::a); + lua.set("s", sol::readonly(&test_2::a)); + lua.set_function("t", sol::readonly(&test_2::a), test_2()); + lua.set_function("u", &nested::i, nested()); + lua.set("v", &nested::i); + lua.set("nested", nested()); + lua.set("inner", inner()); + REQUIRE_THROWS(lua.script("s(o2, 2)")); + REQUIRE_THROWS(lua.script("t(2)")); + REQUIRE_THROWS(lua.script("u(inner)")); + REQUIRE_THROWS(lua.script("v(nested, inner)")); } TEST_CASE("simple/call-with-parameters", "Lua function is called with a few parameters from C++") { diff --git a/tests.cpp b/tests.cpp index 29d51bef..ac6d7c56 100644 --- a/tests.cpp +++ b/tests.cpp @@ -181,6 +181,10 @@ int factory_test::num_saved = 0; int factory_test::num_killed = 0; const int factory_test::true_a = 156; +bool something() { + return true; +} + TEST_CASE("table/traversal", "ensure that we can chain requests and tunnel down into a value if we desire") { sol::state lua; @@ -867,3 +871,76 @@ TEST_CASE("usertype/reference-and-constness", "Make sure constness compiles prop REQUIRE_THROWS(lua.script("f(n, 50)")); REQUIRE_THROWS(lua.script("o.n = 25")); } + +TEST_CASE("usertype/readonly-and-static-functions", "Check if static functions can be called on userdata and from their originating (meta)tables") { + struct bark { + int var = 50; + + void func() {} + + int operator()(int x) { + return x; + } + }; + + sol::state lua; + lua.open_libraries(sol::lib::base); + lua.new_usertype("bark", + "var", &bark::var, + "var2", sol::readonly( &bark::var ), + "something", something, + "something2", [](int x, int y) { return x + y; }, + "func", &bark::func, + sol::meta_function::call_function, &bark::operator() + ); + + bark b; + lua.set("b", &b); + + sol::table b_table = lua["b"]; + sol::function member_func = b_table["func"]; + sol::function s = b_table["something"]; + sol::function s2 = b_table["something2"]; + + sol::table b_metatable = b_table[sol::metatable_key]; + bool isvalidmt = b_metatable.valid(); + REQUIRE(isvalidmt); + sol::function b_call = b_metatable["__call"]; + sol::function b_as_function = lua["b"]; + + int x = b_as_function(1); + int y = b_call(b, 1); + bool z = s(); + int w = s2(2, 3); + REQUIRE(x == 1); + REQUIRE(y == 1); + REQUIRE(z); + REQUIRE(w == 5); + + lua.script(R"( +lx = b(1) +ly = getmetatable(b).__call(b, 1) +lz = b.something() +lz2 = bark.something() +lw = b.something2(2, 3) +lw2 = bark.something2(2, 3) + )"); + + int lx = lua["lx"]; + int ly = lua["ly"]; + bool lz = lua["lz"]; + int lw = lua["lw"]; + bool lz2 = lua["lz2"]; + int lw2 = lua["lw2"]; + REQUIRE(lx == 1); + REQUIRE(ly == 1); + REQUIRE(lz); + REQUIRE(lz2); + REQUIRE(lw == 5); + REQUIRE(lw2 == 5); + REQUIRE(lx == ly); + REQUIRE(lz == lz2); + REQUIRE(lw == lw2); + + REQUIRE_THROWS(lua.script("b.var2 = 2")); +}