From e06d2fb581a320f71f28a23bdfdcca1b7166b1df Mon Sep 17 00:00:00 2001 From: ThePhD Date: Sun, 10 Dec 2017 15:56:49 -0500 Subject: [PATCH] add single, add examples --- CMakeLists.txt | 10 +- docs/source/api/stack.rst | 27 ++++ docs/source/api/usertype.rst | 7 +- examples/customization.cpp | 1 - examples/customization_convert_on_get.cpp | 68 ++++++++ single.py | 4 + single/sol/sol.hpp | 187 ++++++++++++++++------ single/sol/sol_forward.hpp | 5 +- sol/container_traits.hpp | 3 +- sol/feature_test.hpp | 2 +- sol/simple_usertype_metatable.hpp | 55 ++++--- sol/stack_check.hpp | 41 +++-- sol/stack_core.hpp | 57 ++++++- sol/types.hpp | 5 + sol/usertype_metatable.hpp | 21 ++- tests/test_containers.cpp | 8 +- tests/test_customizations.cpp | 56 +++++++ tests/test_simple_usertypes.cpp | 7 +- 18 files changed, 446 insertions(+), 118 deletions(-) create mode 100644 examples/customization_convert_on_get.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 483c312c..ceae0350 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -61,7 +61,7 @@ if (EXAMPLES) source_group(examples FILES ${EXAMPLES_SRC}) foreach(example_source_file ${EXAMPLES_SRC}) get_filename_component(example_name ${example_source_file} NAME_WE) - set(example_name "example-${example_name}") + set(example_name "example_${example_name}") add_executable(${example_name} ${example_source_file} ${HEADER_SRCS}) target_link_libraries(${example_name} ${LUA_LIBRARIES}) target_compile_features(${example_name} PUBLIC ${CXX_FEATURES}) @@ -74,7 +74,7 @@ if (TESTS) source_group(tests FILES ${TEST_SRC}) add_executable(tests ${TEST_SRC} ${HEADER_SRCS}) - target_include_directories(tests PRIVATE ./Catch/include/) + target_include_directories(tests PRIVATE ${CMAKE_SOURCE_DIR}/Catch/include/) find_package(Threads) target_link_libraries(tests Threads::Threads ${LUA_LIBRARIES}) @@ -83,10 +83,10 @@ endif() find_package(PythonInterp 3) if (PYTHONINTERP_FOUND) - add_custom_command(OUTPUT single/sol/sol.hpp single/sol/sol_forward.hpp COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/single.py --output ${CMAKE_CURRENT_SOURCE_DIR}/single/sol/sol.hpp DEPENDS ${HEADER_SRCS}) + add_custom_command(OUTPUT single/sol/sol.hpp single/sol/sol_forward.hpp COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_SOURCE_DIR}/single.py --output ${CMAKE_CURRENT_BINARY_DIR}/single/sol/sol.hpp DEPENDS ${HEADER_SRCS}) add_custom_target(single_sol DEPENDS single/sol/sol.hpp single/sol/sol_forward.hpp) - install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/single/sol/sol.hpp" DESTINATION include/sol) - install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/single/sol/sol_forward.hpp" DESTINATION include/sol) + install(FILES "${CMAKE_SOURCE_DIR}/single/sol/sol.hpp" DESTINATION include/sol) + install(FILES "${CMAKE_SOURCE_DIR}/single/sol/sol_forward.hpp" DESTINATION include/sol) message(STATUS "single_sol can be generated as python 3 has been found.") else() message(STATUS "single_sol cannot be generated as python 3 has not been found.") diff --git a/docs/source/api/stack.rst b/docs/source/api/stack.rst index 613c3c90..53c34044 100644 --- a/docs/source/api/stack.rst +++ b/docs/source/api/stack.rst @@ -88,6 +88,32 @@ You may also retrieve an :doc:`sol::optional\` from this as well, t Checks if the object at ``index`` is of type ``T``. If it is not, it will call the ``handler`` function with ``lua_State*``, ``int index``, ``type`` expected, and ``type`` actual as arguments. If you do not pass your own handler, a ``no_panic`` handler will be passed. +.. code-block:: cpp + :caption: function: get_usertype + :name: stack-get-usertype + + template + auto get_usertype( lua_State* L, int index = -1 ) + template + auto get_usertype( lua_State* L, int index, record& tracking ) + +Directly attempts to rertieve the type ``T`` using sol2's usertype mechanisms. Similar to a regular ``get`` for a user-defined type. Useful when you need to access sol2's usertype getter mechanism while at the same time `providing your own customization`_. + +.. code-block:: cpp + :caption: function: check_usertype + :name: stack-check + + template + bool check_usertype( lua_State* L, int index = -1 ) + + template + bool check_usertype( lua_State* L, int index, Handler&& handler ) + + template + bool check_usertype( lua_State* L, int index, Handler&& handler, record& tracking ) + +Checks if the object at ``index`` is of type ``T`` and stored as a sol2 usertype. Useful when you need to access sol2's usertype checker mechanism while at the same time `providing your own customization`_. + .. code-block:: cpp :caption: function: check_get :name: stack-check-get @@ -305,3 +331,4 @@ This is an SFINAE-friendly struct that is meant to expose a function ``get`` tha .. _Lua stack works in general: https://www.lua.org/pil/24.2.html .. _calling C functions works: https://www.lua.org/pil/26.html .. _interop examples: https://github.com/ThePhD/sol2/blob/develop/examples/interop +.. _providing your own customization: https://github.com/ThePhD/sol2/blob/develop/examples/customization_convert_on_get.cpp diff --git a/docs/source/api/usertype.rst b/docs/source/api/usertype.rst index 704a46cc..392c69c1 100644 --- a/docs/source/api/usertype.rst +++ b/docs/source/api/usertype.rst @@ -238,6 +238,8 @@ If you don't specify a ``sol::meta_function`` name (or equivalent string metamet - a ``sol::meta_function::pairs`` operator is generated for you - Allows you to iterate using ``for k, v in pairs( obj ) do ... end`` in Lua - **Lua 5.2 and better only: LuaJIT does not allow this, Lua 5.1 does NOT allow this** +* for cases where ``.size()`` exists on the C++ type + - the length operator of Lua (``#my_obj``) operator is generated for you * for comparison operations where ``operator <`` and ``operator <=`` exist on the C++ type - These two ``sol::meta_function::less_than(_or_equal_to)`` are generated for you - ``>`` and ``>=`` operators are generated in Lua based on ``<`` and ``<=`` operators @@ -264,8 +266,9 @@ Otherwise, the following is used to specify functions to bind on the specific us - Binds a typical variable to ``"{name}"``, but gets and sets using the specified setter and getter functions. Not that if you do not pass a setter function, the variable will be read-only. Also not that if you do not pass a getter function, it will be write-only * ``"{name}", sol::var( some_value )`` or ``"{name}", sol::var( std::ref( some_value ) )`` - Binds a typical variable to ``"{name}"``, optionally by reference (e.g., refers to the same memory in C++). This is useful for global variables / static class variables and the like -* ``"{name}", sol::overloaded( Func1, Func2, ... )`` - - Creates an oveloaded member function that discriminates on number of arguments and types. +* ``"{name}", sol::overload( Func1, Func2, ... )`` + - Creates an oveloaded member function that discriminates on number of arguments and types + - Dumping multiple functions out with the same name **does not make an overload**: you must use **this syntax** in order for it to work * ``sol::base_classes, sol::bases`` - Tells a usertype what its base classes are. You need this to have derived-to-base conversions work properly. See :ref:`inheritance` diff --git a/examples/customization.cpp b/examples/customization.cpp index 107e53ed..945c2597 100644 --- a/examples/customization.cpp +++ b/examples/customization.cpp @@ -2,7 +2,6 @@ #include #include -#include #include struct two_things { diff --git a/examples/customization_convert_on_get.cpp b/examples/customization_convert_on_get.cpp new file mode 100644 index 00000000..ba024f87 --- /dev/null +++ b/examples/customization_convert_on_get.cpp @@ -0,0 +1,68 @@ +#define SOL_CHECK_ARGUMENTS 1 +#include + +#include +#include +#include + +struct number_shim { + double num = 0; +}; + +namespace sol { + + template <> + struct lua_type_of : std::integral_constant {}; + + namespace stack { + template <> + struct checker { + template + static bool check(lua_State* L, int index, Handler&& handler, record& tracking) { + // check_usertype is a backdoor for directly checking sol2 usertypes + if (!check_usertype(L, index) && !stack::check(L, index)) { + handler(L, index, type_of(L, index), type::userdata, "expected a number_shim or a number"); + return false; + } + tracking.use(1); + return true; + } + }; + + template <> + struct getter { + static number_shim get(lua_State* L, int index, record& tracking) { + if (check_usertype(L, index)) { + number_shim& ns = get_usertype(L, index, tracking); + return ns; + } + number_shim ns{}; + ns.num = stack::get(L, index, tracking); + return ns; + } + }; + + } // namespace stack +} // namespace sol + +int main() { + sol::state lua; + + // Create a pass-through style of function + lua.safe_script("function f ( a ) return a end"); + lua.set_function("g", [](double a) { + number_shim ns; + ns.num = a; + return ns; + }); + + lua.script("vf = f(25) vg = g(35)"); + + number_shim thingsf = lua["vf"]; + number_shim thingsg = lua["vg"]; + + assert(thingsf.num == 25); + assert(thingsg.num == 35); + + return 0; +} diff --git a/single.py b/single.py index 9a085f78..56be98bd 100644 --- a/single.py +++ b/single.py @@ -191,7 +191,11 @@ if not args.quiet: print('finished creating single forward declaration header for sol\n') with open(single_file, 'w', encoding='utf-8') as f: + if not args.quiet: + print('writing {}...'.format(single_file)) f.write(result) with open(forward_single_file, 'w', encoding='utf-8') as f: + if not args.quiet: + print('writing {}...'.format(single_file)) f.write(forward_result) diff --git a/single/sol/sol.hpp b/single/sol/sol.hpp index 377c5c57..b2db59b3 100644 --- a/single/sol/sol.hpp +++ b/single/sol/sol.hpp @@ -20,8 +20,8 @@ // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // This file was generated with a script. -// Generated 2017-12-10 06:34:23.982786 UTC -// This header was generated with sol v2.19.0 (revision 87b4dd6) +// Generated 2017-12-10 20:56:35.224200 UTC +// This header was generated with sol v2.19.0 (revision c3c7f42) // https://github.com/ThePhD/sol2 #ifndef SOL_SINGLE_INCLUDE_HPP @@ -72,7 +72,6 @@ #if defined(__cpp_noexcept_function_type) || ((defined(_MSC_VER) && _MSC_VER > 1911) && ((defined(_HAS_CXX17) && _HAS_CXX17 == 1) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L))) #ifndef SOL_NOEXCEPT_FUNCTION_TYPE -#define SOL_NOEXCEPT_FUNCTION_TYPE 1 #endif // noexcept is part of a function's type #endif @@ -4439,6 +4438,11 @@ namespace sol { int static_trampoline_noexcept(lua_State* L) noexcept { return f(L); } +#else + template + int static_trampoline_noexcept(lua_State* L) noexcept { + return f(L); + } #endif template @@ -7099,6 +7103,20 @@ namespace sol { use_reference_tag; return pusher>>{}.push(L, std::forward(arg), std::forward(args)...); } + + template + bool check_usertype(std::false_type, lua_State* L, int index, type indextype, Handler&& handler, record& tracking) { + typedef meta::unqualified_t Tu; + typedef detail::as_value_tag detail_t; + return checker{}.check(types>(), L, index, indextype, std::forward(handler), tracking); + } + + template + bool check_usertype(std::true_type, lua_State* L, int index, type indextype, Handler&& handler, record& tracking) { + typedef meta::unqualified_t>> Tu; + typedef detail::as_pointer_tag detail_t; + return checker{}.check(L, index, indextype, std::forward(handler), tracking); + } } // namespace stack_detail inline bool maybe_indexable(lua_State* L, int index = -1) { @@ -7198,6 +7216,24 @@ namespace sol { return check(L, index, handler); } + template + bool check_usertype(lua_State* L, int index, Handler&& handler, record& tracking) { + type indextype = type_of(L, index); + return stack_detail::check_usertype(std::is_pointer(), L, index, indextype, std::forward(handler), tracking); + } + + template + bool check_usertype(lua_State* L, int index, Handler&& handler) { + record tracking{}; + return check_usertype(L, index, std::forward(handler), tracking); + } + + template + bool check_usertype(lua_State* L, int index = -lua_size>::value) { + auto handler = no_panic; + return check_usertype(L, index, handler); + } + template inline decltype(auto) check_get(lua_State* L, int index, Handler&& handler, record& tracking) { typedef meta::unqualified_t Tu; @@ -7226,6 +7262,11 @@ namespace sol { auto op = check_get(L, index, type_panic_c_str, tracking); return *std::move(op); } + + template + inline decltype(auto) tagged_get(types>, lua_State* L, int index, record& tracking) { + return stack_detail::unchecked_get>(L, index, tracking); + } #else template inline decltype(auto) tagged_get(types, lua_State* L, int index, record& tracking) { @@ -7233,11 +7274,6 @@ namespace sol { } #endif - template - inline decltype(auto) tagged_get(types>, lua_State* L, int index, record& tracking) { - return stack_detail::unchecked_get>(L, index, tracking); - } - template struct check_types { template @@ -7295,6 +7331,21 @@ namespace sol { return multi_check(L, index); } + template + inline decltype(auto) get_usertype(lua_State* L, int index, record& tracking) { +#ifdef SOL_SAFE_GETTER + return stack_detail::tagged_get(types::value, detail::as_pointer_tag>, detail::as_value_tag>>(), L, index, tracking); +#else + return stack_detail::unchecked_get::value, detail::as_pointer_tag>, detail::as_value_tag>>(L, index, tracking); +#endif + } + + template + inline decltype(auto) get_usertype(lua_State* L, int index = -lua_size>::value) { + record tracking{}; + return get_usertype(L, index, tracking); + } + template inline decltype(auto) get(lua_State* L, int index, record& tracking) { return stack_detail::tagged_get(types(), L, index, tracking); @@ -7578,17 +7629,21 @@ namespace stack { int isnum = 0; lua_tointegerx(L, index, &isnum); const bool success = isnum != 0; + if (!success) { + // expected type, actual type + handler(L, index, type::number, type_of(L, index), "not a numeric type or numeric string"); + } #else // this check is precise, does not convert if (lua_isinteger(L, index) == 1) { return true; } const bool success = false; -#endif // If numbers are enabled, use the imprecise check if (!success) { // expected type, actual type handler(L, index, type::number, type_of(L, index), "not a numeric type"); } +#endif // If numbers are enabled, use the imprecise check return success; #else #ifndef SOL_STRINGS_ARE_NUMBERS @@ -7887,6 +7942,12 @@ namespace stack { template struct checker, type::userdata, C> { + template + static bool check(lua_State* L, int index, Handler&& handler, record& tracking) { + const type indextype = type_of(L, index); + return check(types(), L, index, indextype, handler, tracking); + } + template static bool check(types, lua_State* L, int index, type indextype, Handler&& handler, record& tracking) { #ifdef SOL_ENABLE_INTEROP @@ -7937,11 +7998,28 @@ namespace stack { }; template - struct checker { + struct checker, type::userdata, C> { + template + static bool check(lua_State* L, int index, type indextype, Handler&& handler, record& tracking) { + if (indextype == type::lua_nil) { + tracking.use(1); + return true; + } + return stack_detail::check_usertype(std::false_type(), L, index, indextype, std::forward(handler), tracking); + } + template static bool check(lua_State* L, int index, Handler&& handler, record& tracking) { const type indextype = type_of(L, index); - return checker, type::userdata, C>{}.check(types(), L, index, indextype, std::forward(handler), tracking); + return check(L, index, handler, indextype, tracking); + } + }; + + template + struct checker { + template + static bool check(lua_State* L, int index, Handler&& handler, record& tracking) { + return check_usertype(L, index, std::forward(handler), tracking); } }; @@ -7949,13 +8027,7 @@ namespace stack { struct checker { template static bool check(lua_State* L, int index, Handler&& handler, record& tracking) { - const type indextype = type_of(L, index); - // Allow lua_nil to be transformed to nullptr - if (indextype == type::lua_nil) { - tracking.use(1); - return true; - } - return checker, type::userdata, C>{}.check(L, index, std::forward(handler), tracking); + return check_usertype(L, index, std::forward(handler), tracking); } }; @@ -15148,9 +15220,8 @@ namespace sol { }; static auto& get_src(lua_State* L) { - typedef std::remove_pointer_t> Tu; #ifdef SOL_SAFE_USERTYPE - auto p = stack::check_get(L, 1); + auto p = stack::check_get(L, 1); if (!p) { luaL_error(L, "sol: 'self' is not of type '%s' (pass 'self' as first argument with ':' or call on proper type)", detail::demangle().c_str()); } @@ -17050,14 +17121,21 @@ namespace sol { if (toplevel && stack::get(L, keyidx) != type::string) { return is_index ? f.indexfunc(L) : f.newindexfunc(L); } - std::string name = stack::get(L, keyidx); - auto memberit = f.mapping.find(name); - if (memberit != f.mapping.cend()) { - const usertype_detail::call_information& ci = memberit->second; - const usertype_detail::member_search& member = is_index ? ci.index : ci.new_index; - return (member)(L, static_cast(&f), ci.runtime_target); + int runtime_target = 0; + usertype_detail::member_search member = nullptr; + { + std::string name = stack::get(L, keyidx); + auto memberit = f.mapping.find(name); + if (memberit != f.mapping.cend()) { + const usertype_detail::call_information& ci = memberit->second; + member = is_index ? ci.index : ci.new_index; + runtime_target = ci.runtime_target; + } } - string_view accessor = name; + if (member != nullptr) { + return (member)(L, static_cast(&f), runtime_target); + } + string_view accessor = stack::get(L, keyidx); int ret = 0; bool found = false; // Otherwise, we need to do propagating calls through the bases @@ -17311,7 +17389,7 @@ namespace sol { function_map& functions = sm.functions; static const int keyidx = -2 + static_cast(is_index); if (toplevel) { - if (stack::get(L, keyidx) != type::string) { + if (type_of(L, keyidx) != type::string) { if (has_indexing) { object& indexingfunc = is_index ? sm.index @@ -17326,35 +17404,44 @@ namespace sol { } } string_view accessor = stack::get(L, keyidx); - std::string accessorkey = accessor.data(); - auto vit = variables.find(accessorkey); - if (vit != variables.cend()) { - auto& varwrap = *(vit->second); - if (is_index) { - return varwrap.index(L); + variable_wrapper* varwrap = nullptr; + { + std::string accessorkey(accessor.data(), accessor.size()); + auto vit = variables.find(accessorkey); + if (vit != variables.cend()) { + varwrap = vit->second.get(); } - return varwrap.new_index(L); } - auto fit = functions.find(accessorkey); - if (fit != functions.cend()) { - object& func = fit->second; - if (is_index) { - return stack::push(L, func); - } - else { - if (has_indexing && !is_toplevel(L)) { - object& indexingfunc = is_index - ? sm.index - : sm.newindex; - return call_indexing_object(L, indexingfunc); + if (varwrap != nullptr) { + return is_index ? varwrap->index(L) : varwrap->new_index(L); + } + bool function_failed = false; + { + std::string accessorkey(accessor.data(), accessor.size()); + auto fit = functions.find(accessorkey); + if (fit != functions.cend()) { + object& func = fit->second; + if (is_index) { + return stack::push(L, func); } else { - return is_index - ? indexing_fail(L) - : metatable_newindex(L); + function_failed = true; } } } + if (function_failed) { + if (has_indexing && !is_toplevel(L)) { + object& indexingfunc = is_index + ? sm.index + : sm.newindex; + return call_indexing_object(L, indexingfunc); + } + else { + return is_index + ? indexing_fail(L) + : metatable_newindex(L); + } + } /* Check table storage first for a method that works luaL_getmetatable(L, sm.metakey); if (type_of(L, -1) != type::lua_nil) { diff --git a/single/sol/sol_forward.hpp b/single/sol/sol_forward.hpp index ea3aa333..31a37724 100644 --- a/single/sol/sol_forward.hpp +++ b/single/sol/sol_forward.hpp @@ -20,8 +20,8 @@ // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // This file was generated with a script. -// Generated 2017-12-10 06:34:24.172779 UTC -// This header was generated with sol v2.19.0 (revision 87b4dd6) +// Generated 2017-12-10 20:56:35.410742 UTC +// This header was generated with sol v2.19.0 (revision c3c7f42) // https://github.com/ThePhD/sol2 #ifndef SOL_SINGLE_INCLUDE_FORWARD_HPP @@ -39,7 +39,6 @@ #if defined(__cpp_noexcept_function_type) || ((defined(_MSC_VER) && _MSC_VER > 1911) && ((defined(_HAS_CXX17) && _HAS_CXX17 == 1) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L))) #ifndef SOL_NOEXCEPT_FUNCTION_TYPE -#define SOL_NOEXCEPT_FUNCTION_TYPE 1 #endif // noexcept is part of a function's type #endif diff --git a/sol/container_traits.hpp b/sol/container_traits.hpp index 1375da9e..86974179 100644 --- a/sol/container_traits.hpp +++ b/sol/container_traits.hpp @@ -538,9 +538,8 @@ namespace sol { }; static auto& get_src(lua_State* L) { - typedef std::remove_pointer_t> Tu; #ifdef SOL_SAFE_USERTYPE - auto p = stack::check_get(L, 1); + auto p = stack::check_get(L, 1); if (!p) { luaL_error(L, "sol: 'self' is not of type '%s' (pass 'self' as first argument with ':' or call on proper type)", detail::demangle().c_str()); } diff --git a/sol/feature_test.hpp b/sol/feature_test.hpp index 0a0f8d52..78118c74 100644 --- a/sol/feature_test.hpp +++ b/sol/feature_test.hpp @@ -30,7 +30,7 @@ #if defined(__cpp_noexcept_function_type) || ((defined(_MSC_VER) && _MSC_VER > 1911) && ((defined(_HAS_CXX17) && _HAS_CXX17 == 1) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L))) #ifndef SOL_NOEXCEPT_FUNCTION_TYPE -#define SOL_NOEXCEPT_FUNCTION_TYPE 1 +//#define SOL_NOEXCEPT_FUNCTION_TYPE 1 #endif // noexcept is part of a function's type #endif diff --git a/sol/simple_usertype_metatable.hpp b/sol/simple_usertype_metatable.hpp index d44c2890..05831e25 100644 --- a/sol/simple_usertype_metatable.hpp +++ b/sol/simple_usertype_metatable.hpp @@ -51,7 +51,7 @@ namespace sol { function_map& functions = sm.functions; static const int keyidx = -2 + static_cast(is_index); if (toplevel) { - if (stack::get(L, keyidx) != type::string) { + if (type_of(L, keyidx) != type::string) { if (has_indexing) { object& indexingfunc = is_index ? sm.index @@ -66,35 +66,44 @@ namespace sol { } } string_view accessor = stack::get(L, keyidx); - std::string accessorkey = accessor.data(); - auto vit = variables.find(accessorkey); - if (vit != variables.cend()) { - auto& varwrap = *(vit->second); - if (is_index) { - return varwrap.index(L); + variable_wrapper* varwrap = nullptr; + { + std::string accessorkey(accessor.data(), accessor.size()); + auto vit = variables.find(accessorkey); + if (vit != variables.cend()) { + varwrap = vit->second.get(); } - return varwrap.new_index(L); } - auto fit = functions.find(accessorkey); - if (fit != functions.cend()) { - object& func = fit->second; - if (is_index) { - return stack::push(L, func); - } - else { - if (has_indexing && !is_toplevel(L)) { - object& indexingfunc = is_index - ? sm.index - : sm.newindex; - return call_indexing_object(L, indexingfunc); + if (varwrap != nullptr) { + return is_index ? varwrap->index(L) : varwrap->new_index(L); + } + bool function_failed = false; + { + std::string accessorkey(accessor.data(), accessor.size()); + auto fit = functions.find(accessorkey); + if (fit != functions.cend()) { + object& func = fit->second; + if (is_index) { + return stack::push(L, func); } else { - return is_index - ? indexing_fail(L) - : metatable_newindex(L); + function_failed = true; } } } + if (function_failed) { + if (has_indexing && !is_toplevel(L)) { + object& indexingfunc = is_index + ? sm.index + : sm.newindex; + return call_indexing_object(L, indexingfunc); + } + else { + return is_index + ? indexing_fail(L) + : metatable_newindex(L); + } + } /* Check table storage first for a method that works luaL_getmetatable(L, sm.metakey); if (type_of(L, -1) != type::lua_nil) { diff --git a/sol/stack_check.hpp b/sol/stack_check.hpp index b366c0dd..efecd715 100644 --- a/sol/stack_check.hpp +++ b/sol/stack_check.hpp @@ -99,17 +99,21 @@ namespace stack { int isnum = 0; lua_tointegerx(L, index, &isnum); const bool success = isnum != 0; + if (!success) { + // expected type, actual type + handler(L, index, type::number, type_of(L, index), "not a numeric type or numeric string"); + } #else // this check is precise, does not convert if (lua_isinteger(L, index) == 1) { return true; } const bool success = false; -#endif // If numbers are enabled, use the imprecise check if (!success) { // expected type, actual type handler(L, index, type::number, type_of(L, index), "not a numeric type"); } +#endif // If numbers are enabled, use the imprecise check return success; #else #ifndef SOL_STRINGS_ARE_NUMBERS @@ -408,6 +412,12 @@ namespace stack { template struct checker, type::userdata, C> { + template + static bool check(lua_State* L, int index, Handler&& handler, record& tracking) { + const type indextype = type_of(L, index); + return check(types(), L, index, indextype, handler, tracking); + } + template static bool check(types, lua_State* L, int index, type indextype, Handler&& handler, record& tracking) { #ifdef SOL_ENABLE_INTEROP @@ -458,11 +468,28 @@ namespace stack { }; template - struct checker { + struct checker, type::userdata, C> { + template + static bool check(lua_State* L, int index, type indextype, Handler&& handler, record& tracking) { + if (indextype == type::lua_nil) { + tracking.use(1); + return true; + } + return stack_detail::check_usertype(std::false_type(), L, index, indextype, std::forward(handler), tracking); + } + template static bool check(lua_State* L, int index, Handler&& handler, record& tracking) { const type indextype = type_of(L, index); - return checker, type::userdata, C>{}.check(types(), L, index, indextype, std::forward(handler), tracking); + return check(L, index, handler, indextype, tracking); + } + }; + + template + struct checker { + template + static bool check(lua_State* L, int index, Handler&& handler, record& tracking) { + return check_usertype(L, index, std::forward(handler), tracking); } }; @@ -470,13 +497,7 @@ namespace stack { struct checker { template static bool check(lua_State* L, int index, Handler&& handler, record& tracking) { - const type indextype = type_of(L, index); - // Allow lua_nil to be transformed to nullptr - if (indextype == type::lua_nil) { - tracking.use(1); - return true; - } - return checker, type::userdata, C>{}.check(L, index, std::forward(handler), tracking); + return check_usertype(L, index, std::forward(handler), tracking); } }; diff --git a/sol/stack_core.hpp b/sol/stack_core.hpp index 0fabbc52..2a7850d0 100644 --- a/sol/stack_core.hpp +++ b/sol/stack_core.hpp @@ -550,6 +550,20 @@ namespace sol { use_reference_tag; return pusher>>{}.push(L, std::forward(arg), std::forward(args)...); } + + template + bool check_usertype(std::false_type, lua_State* L, int index, type indextype, Handler&& handler, record& tracking) { + typedef meta::unqualified_t Tu; + typedef detail::as_value_tag detail_t; + return checker{}.check(types>(), L, index, indextype, std::forward(handler), tracking); + } + + template + bool check_usertype(std::true_type, lua_State* L, int index, type indextype, Handler&& handler, record& tracking) { + typedef meta::unqualified_t>> Tu; + typedef detail::as_pointer_tag detail_t; + return checker{}.check(L, index, indextype, std::forward(handler), tracking); + } } // namespace stack_detail inline bool maybe_indexable(lua_State* L, int index = -1) { @@ -649,6 +663,24 @@ namespace sol { return check(L, index, handler); } + template + bool check_usertype(lua_State* L, int index, Handler&& handler, record& tracking) { + type indextype = type_of(L, index); + return stack_detail::check_usertype(std::is_pointer(), L, index, indextype, std::forward(handler), tracking); + } + + template + bool check_usertype(lua_State* L, int index, Handler&& handler) { + record tracking{}; + return check_usertype(L, index, std::forward(handler), tracking); + } + + template + bool check_usertype(lua_State* L, int index = -lua_size>::value) { + auto handler = no_panic; + return check_usertype(L, index, handler); + } + template inline decltype(auto) check_get(lua_State* L, int index, Handler&& handler, record& tracking) { typedef meta::unqualified_t Tu; @@ -677,6 +709,11 @@ namespace sol { auto op = check_get(L, index, type_panic_c_str, tracking); return *std::move(op); } + + template + inline decltype(auto) tagged_get(types>, lua_State* L, int index, record& tracking) { + return stack_detail::unchecked_get>(L, index, tracking); + } #else template inline decltype(auto) tagged_get(types, lua_State* L, int index, record& tracking) { @@ -684,11 +721,6 @@ namespace sol { } #endif - template - inline decltype(auto) tagged_get(types>, lua_State* L, int index, record& tracking) { - return stack_detail::unchecked_get>(L, index, tracking); - } - template struct check_types { template @@ -746,6 +778,21 @@ namespace sol { return multi_check(L, index); } + template + inline decltype(auto) get_usertype(lua_State* L, int index, record& tracking) { +#ifdef SOL_SAFE_GETTER + return stack_detail::tagged_get(types::value, detail::as_pointer_tag>, detail::as_value_tag>>(), L, index, tracking); +#else + return stack_detail::unchecked_get::value, detail::as_pointer_tag>, detail::as_value_tag>>(L, index, tracking); +#endif + } + + template + inline decltype(auto) get_usertype(lua_State* L, int index = -lua_size>::value) { + record tracking{}; + return get_usertype(L, index, tracking); + } + template inline decltype(auto) get(lua_State* L, int index, record& tracking) { return stack_detail::tagged_get(types(), L, index, tracking); diff --git a/sol/types.hpp b/sol/types.hpp index cbdb98d7..e94ce944 100644 --- a/sol/types.hpp +++ b/sol/types.hpp @@ -57,6 +57,11 @@ namespace sol { int static_trampoline_noexcept(lua_State* L) noexcept { return f(L); } +#else + template + int static_trampoline_noexcept(lua_State* L) noexcept { + return f(L); + } #endif template diff --git a/sol/usertype_metatable.hpp b/sol/usertype_metatable.hpp index 7d767585..89dfd494 100644 --- a/sol/usertype_metatable.hpp +++ b/sol/usertype_metatable.hpp @@ -555,14 +555,21 @@ namespace sol { if (toplevel && stack::get(L, keyidx) != type::string) { return is_index ? f.indexfunc(L) : f.newindexfunc(L); } - std::string name = stack::get(L, keyidx); - auto memberit = f.mapping.find(name); - if (memberit != f.mapping.cend()) { - const usertype_detail::call_information& ci = memberit->second; - const usertype_detail::member_search& member = is_index ? ci.index : ci.new_index; - return (member)(L, static_cast(&f), ci.runtime_target); + int runtime_target = 0; + usertype_detail::member_search member = nullptr; + { + std::string name = stack::get(L, keyidx); + auto memberit = f.mapping.find(name); + if (memberit != f.mapping.cend()) { + const usertype_detail::call_information& ci = memberit->second; + member = is_index ? ci.index : ci.new_index; + runtime_target = ci.runtime_target; + } } - string_view accessor = name; + if (member != nullptr) { + return (member)(L, static_cast(&f), runtime_target); + } + string_view accessor = stack::get(L, keyidx); int ret = 0; bool found = false; // Otherwise, we need to do propagating calls through the bases diff --git a/tests/test_containers.cpp b/tests/test_containers.cpp index 5330f1e5..d18dd1d3 100644 --- a/tests/test_containers.cpp +++ b/tests/test_containers.cpp @@ -772,9 +772,7 @@ y = a.readonly_seq std::list& seqrefy = lua["y"]; REQUIRE(&seqrefx == &seqrefy); REQUIRE(seqrefx.size() == 3); - auto result = lua.do_string(R"( -a.readonly_seq = value; -)"); + auto result = lua.safe_script("a.readonly_seq = value", sol::script_pass_on_error); REQUIRE_FALSE(result.valid()); } @@ -903,7 +901,7 @@ TEST_CASE("containers/non_copyable", "make sure non-copyable types in containers lua["v"] = std::vector{}; - auto pfr = lua.safe_script("t = test.new()\nt.b = v", sol::script_pass_on_error); + auto pfr = lua.safe_script("t = test.new() t.b = v", sol::script_pass_on_error); REQUIRE_FALSE(pfr.valid()); } SECTION("simple") { @@ -913,7 +911,7 @@ TEST_CASE("containers/non_copyable", "make sure non-copyable types in containers lua["v"] = std::vector{}; - auto pfr = lua.safe_script("t = test.new()\nt.b = v", sol::script_pass_on_error); + auto pfr = lua.safe_script("t = test.new() t.b = v", sol::script_pass_on_error); REQUIRE_FALSE(pfr.valid()); } } diff --git a/tests/test_customizations.cpp b/tests/test_customizations.cpp index 76ec7ccc..33227db4 100644 --- a/tests/test_customizations.cpp +++ b/tests/test_customizations.cpp @@ -12,6 +12,10 @@ struct two_things { bool b; }; +struct number_shim { + double num = 0; +}; + namespace sol { // First, the expected size @@ -23,6 +27,10 @@ namespace sol { template <> struct lua_type_of : std::integral_constant {}; + // do note specialize size for this because it is our type + template <> + struct lua_type_of : std::integral_constant {}; + // Now, specialize various stack structures namespace stack { @@ -62,6 +70,33 @@ namespace sol { } }; + template <> + struct checker { + template + static bool check(lua_State* L, int index, Handler&& handler, record& tracking) { + // check_usertype is a backdoor for directly checking sol2 usertypes + if (!check_usertype(L, index) && !stack::check(L, index)) { + handler(L, index, type_of(L, index), type::userdata, "expected a number_shim or a number"); + return false; + } + tracking.use(1); + return true; + } + }; + + template <> + struct getter { + static number_shim get(lua_State* L, int index, record& tracking) { + if (check_usertype(L, index)) { + number_shim& ns = get_usertype(L, index, tracking); + return ns; + } + number_shim ns{}; + ns.num = stack::get(L, index, tracking); + return ns; + } + }; + } // namespace stack } // namespace sol @@ -89,3 +124,24 @@ TEST_CASE("customization/split struct", "using the newly documented customizatio REQUIRE_FALSE(thingsg.b); REQUIRE(d == 36.5); } + +TEST_CASE("customization/get_ check_usertype", "using the newly documented customization points to handle different kinds of classes") { + sol::state lua; + + // Create a pass-through style of function + lua.safe_script("function f ( a ) return a end"); + lua.set_function("g", [](double a) { + number_shim ns; + ns.num = a; + return ns; + }); + + auto result = lua.safe_script("vf = f(25) vg = g(35)", sol::script_pass_on_error); + REQUIRE(result.valid()); + + number_shim thingsf = lua["vf"]; + number_shim thingsg = lua["vg"]; + + REQUIRE(thingsf.num == 25); + REQUIRE(thingsg.num == 35); +} diff --git a/tests/test_simple_usertypes.cpp b/tests/test_simple_usertypes.cpp index 8a46fb11..d06d7a0a 100644 --- a/tests/test_simple_usertypes.cpp +++ b/tests/test_simple_usertypes.cpp @@ -635,15 +635,14 @@ end REQUIRE_FALSE(result.valid()); } - REQUIRE_NOTHROW([&lua]() { + { auto result = lua.safe_script(R"( function t:runtime_func(a) return a + 52 end - )", - sol::script_pass_on_error); + )", sol::script_pass_on_error); REQUIRE_FALSE(result.valid()); - }()); + } lua.safe_script("val = t:func(2)"); val = lua["val"];