diff --git a/docs/source/features.rst b/docs/source/features.rst index b1e9c718..8b47d995 100644 --- a/docs/source/features.rst +++ b/docs/source/features.rst @@ -74,18 +74,18 @@ Explanations for a few categories are below (rest are self-explanatory). * optional: Support for getting an element, or potentially not (and not forcing the default construction of what amounts to a bogus/dead object). Usually comes with ``std(::experimental)::optional``. It's a fairly new class, so a hand-rolled class internal to the library with similar semantics is also acceptable * tables: Some sort of abstraction for dealing with tables. Ideal support is ``mytable["some_key"] = value``, and everything that the syntax implies. -* table chaining: In conjunction with tables, having the ability to do nest deeply into tables ``mytable["key1"]["key2"]["key3"]``. Note that this becomes a tripping point for some libraries: crashing if ``"key1"`` doesn't exist while trying to access ``"key2"`` (Sol avoids this specifically when you use ``sol::optional``), and sometimes it's also a heavy performance bottleneck as expressions are not lazy-evaluated by a library. -* arbitrary keys: Letting C++ code use userdata, other tables, integers, etc. as keys for into a table without dropping to the plain API. -* user-defined types (udts): C++ types given form and function in lua code. +* table chaining: In conjunction with tables, having the ability to query deeply into tables ``mytable["key1"]["key2"]["key3"]``. Note that this becomes a tripping point for some libraries: crashing if ``"key1"`` doesn't exist while trying to access ``"key2"`` (Sol avoids this specifically when you use ``sol::optional``), and sometimes it's also a heavy performance bottleneck as expressions are not lazy-evaluated by a library. +* arbitrary keys: Letting C++ code use userdata, other tables, integers, etc. as keys for into a table. +* user-defined types (udts): C++ types given form and function in Lua code. * udts - member functions: C++ member functions on a type, usually callable with ``my_object:foo(1)`` or similar in Lua. * udts - variables: C++ member variables, manipulated by ``my_object.var = 24`` and friends * function binding: Support for binding all types of functions. Lambdas, member functions, free functions, in different contexts, etc... -* protected function: Use of ``lua_pcall`` to call a function, which offers error-handling and trampolining -* multi-return: returning multiple values from and to lua -* variadic/variant argument: being able to accept "anything" from lua, and even return "anything" to lua -* inheritance: allowing some degree of subtyping or inheritance on classes / userdata from lua +* protected function: Use of ``lua_pcall`` to call a function, which offers error-handling and trampolining (as well as the ability to opt-in / opt-out of this behavior) +* multi-return: returning multiple values from and to Lua (generally through ``std::tuple<...>`` or in some other way) +* variadic/variant argument: being able to accept "anything" from Lua, and even return "anything" to Lua (``object`` abstraction, variadic arguments, etc...) +* inheritance: allowing some degree of subtyping or inheritance on classes / userdata from Lua - this generally means that you can retrieve a base pointer from Lua even if you hand the library a derived pointer * overloading: the ability to call overloaded functions, matched based on arity or type (``foo( 1 )`` from lua calls a different function then ``foo( "bark" )``). -* lua thread: basic wrapping of the lua thread API; ties in with coroutine. +* Lua thread: basic wrapping of the lua thread API; ties in with coroutine. * coroutines: allowing a function to be called multiple times, resuming the execution of a Lua coroutine each time +---------------------------+-------------+------------+----------+---------+----------+-----------+-----------+----------------+----------+----------+-----------+-----------------+--------+ @@ -112,7 +112,7 @@ Explanations for a few categories are below (rest are self-explanatory). +---------------------------+-------------+------------+----------+---------+----------+-----------+-----------+----------------+----------+----------+-----------+-----------------+--------+ | function binding | ~ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ~ | +---------------------------+-------------+------------+----------+---------+----------+-----------+-----------+----------------+----------+----------+-----------+-----------------+--------+ -| protected function | ~ | ✗ | ~ | ~ | ~ | ✔ | ~ | ✔ | ~ | ~ | ~ | ~ | ~ | +| protected call | ~ | ✗ | ~ | ~ | ~ | ✔ | ~ | ✔ | ~ | ~ | ~ | ~ | ~ | +---------------------------+-------------+------------+----------+---------+----------+-----------+-----------+----------------+----------+----------+-----------+-----------------+--------+ | multi-return | ~ | ✗ | ✔ | ✔ | ✔ | ✔ | ~ | ✔ | ✔ | ~ | ✔ | ~ | ✗ | +---------------------------+-------------+------------+----------+---------+----------+-----------+-----------+----------------+----------+----------+-----------+-----------------+--------+ @@ -122,7 +122,7 @@ Explanations for a few categories are below (rest are self-explanatory). +---------------------------+-------------+------------+----------+---------+----------+-----------+-----------+----------------+----------+----------+-----------+-----------------+--------+ | overloading | ~ | ✗ | ✗ | ✗ | ✗ | ✔ | ✗ | ✗ | ✔ | ✔ | ✔ | ✗ | ✗ | +---------------------------+-------------+------------+----------+---------+----------+-----------+-----------+----------------+----------+----------+-----------+-----------------+--------+ -| lua thread | ~ | ✗ | ~ | ✗ | ✗ | ✔ | ✔ | ✗ | ✔ | ✗ | ✗ | ✔ | ✗ | +| Lua thread | ~ | ✗ | ~ | ✗ | ✗ | ✔ | ✔ | ✗ | ✔ | ✗ | ✗ | ✔ | ✗ | +---------------------------+-------------+------------+----------+---------+----------+-----------+-----------+----------------+----------+----------+-----------+-----------------+--------+ | coroutines | ~ | ✗ | ~ | ✔ | ✔ | ✔ | ✗ | ✗ | ✔ | ✗ | ✗ | ✔ | ✗ | +---------------------------+-------------+------------+----------+---------+----------+-----------+-----------+----------------+----------+----------+-----------+-----------------+--------+ diff --git a/sol/call.hpp b/sol/call.hpp index ca5977ed..f443f3a2 100644 --- a/sol/call.hpp +++ b/sol/call.hpp @@ -367,7 +367,7 @@ namespace sol { int operator()(types, index_value, types r, types a, lua_State* L, int, int start, F& f) { const auto& metakey = usertype_traits::metatable; T** pointerpointer = reinterpret_cast(lua_newuserdata(L, sizeof(T*) + sizeof(T))); - stack_reference userdataref(L, -1); + reference userdataref(L, -1); T*& referencepointer = *pointerpointer; T* obj = reinterpret_cast(pointerpointer + 1); referencepointer = obj; diff --git a/sol/inheritance.hpp b/sol/inheritance.hpp index d441ab56..120c36e5 100644 --- a/sol/inheritance.hpp +++ b/sol/inheritance.hpp @@ -23,9 +23,7 @@ #define SOL_INHERITANCE_HPP #include "types.hpp" -#if defined(SOL_NO_RTTI) && defined(SOL_NO_EXCEPTIONS) #include -#endif // No Runtime Type Information and No Exceptions namespace sol { template @@ -46,7 +44,6 @@ namespace sol { template bool has_derived::value = false; -#if defined(SOL_NO_RTTI) && defined(SOL_NO_EXCEPTIONS) inline std::size_t unique_id() { static std::atomic x(0); return ++x; @@ -59,7 +56,6 @@ namespace sol { template const std::size_t id_for::value = unique_id(); -#endif // No Runtime Type Information / No Exceptions inline decltype(auto) base_class_check_key() { static const auto& key = u8"♡o。.(✿ฺ。 ✿ฺ)"; @@ -71,112 +67,39 @@ namespace sol { return key; } -#ifndef SOL_NO_EXCEPTIONS - - template - void throw_as(void* p) { - throw static_cast(p); - } - - using throw_cast = decltype(&throw_as); - - template - inline T* catch_cast(void* p, throw_cast f) { - try { - f(static_cast(p)); - } - catch (T* ptr) { - return ptr; - } - catch (...) { - return static_cast(p); - } - return static_cast(p); - } - - template - inline bool catch_check(throw_cast f) { - try { - f(nullptr); - } - catch (T*) { - return true; - } - catch (...) { - return false; - } - return false; - } - -#elif !defined(SOL_NO_RTTI) template struct inheritance { - static bool type_check(types<>, const std::type_info&) { + static bool type_check_bases(types<>, std::size_t) { return false; } template - static bool type_check(types, const std::type_info& ti) { - return ti != typeid(Base) || type_check(types(), ti); - } - - static bool type_check(const std::type_info& ti) { - return ti != typeid(T) || type_check(types(), ti); - } - - static void* type_cast(types<>, T*, const std::type_info& ti) { - return nullptr; - } - - template - static void* type_cast(types, T* data, const std::type_info& ti) { - // Make sure to convert to T first, and then dynamic cast to the proper type - return ti != typeid(Base) ? type_cast(types(), data, ti) : static_cast(dynamic_cast(static_cast(data))); - } - - static void* type_cast(void* voiddata, const std::type_info& ti) { - T* data = static_cast(voiddata); - return static_cast(ti != typeid(T) ? type_cast(types(), data, ti) : data); - } - }; - - using inheritance_check_function = decltype(&inheritance::type_check); - using inheritance_cast_function = decltype(&inheritance::type_cast); -#else - template - struct inheritance { - static bool type_check(types<>, std::size_t) { - return false; - } - - template - static bool type_check(types, std::size_t ti) { - return ti != id_for::value || type_check(types(), ti); + static bool type_check_bases(types, std::size_t ti) { + return ti != id_for::value || type_check_bases(types(), ti); } static bool type_check(std::size_t ti) { - return ti != id_for::value || type_check(types(), ti); + return ti != id_for::value || type_check_bases(types(), ti); } - static void* type_cast(types<>, T*, std::size_t) { + static void* type_cast_bases(types<>, T*, std::size_t) { return nullptr; } template - static void* type_cast(types, T* data, std::size_t ti) { + static void* type_cast_bases(types, T* data, std::size_t ti) { // Make sure to convert to T first, and then dynamic cast to the proper type - return ti != id_for::value ? type_cast(types(), data, ti) : static_cast(static_cast(data)); + return ti != id_for::value ? type_cast_bases(types(), data, ti) : static_cast(static_cast(data)); } static void* type_cast(void* voiddata, std::size_t ti) { T* data = static_cast(voiddata); - return static_cast(ti != id_for::value ? type_cast(types(), data, ti) : data); + return static_cast(ti != id_for::value ? type_cast_bases(types(), data, ti) : data); } }; using inheritance_check_function = decltype(&inheritance::type_check); using inheritance_cast_function = decltype(&inheritance::type_cast); -#endif // No Exceptions and/or No Runtime Type Information } // detail } // sol diff --git a/sol/object.hpp b/sol/object.hpp index 33aff85e..c9bd70ac 100644 --- a/sol/object.hpp +++ b/sol/object.hpp @@ -93,7 +93,9 @@ namespace sol { object make_reference(lua_State* L, Args&&... args) { int backpedal = stack::push(L, std::forward(args)...); object r = stack::get(L, -backpedal); - lua_pop(L, backpedal); + if (should_pop) { + lua_pop(L, backpedal); + } return r; } diff --git a/sol/reference.hpp b/sol/reference.hpp index 00e01517..9409c2cf 100644 --- a/sol/reference.hpp +++ b/sol/reference.hpp @@ -27,6 +27,17 @@ namespace sol { namespace stack { + template + struct push_popper_n { + lua_State* L; + int t; + push_popper_n(lua_State* L, int x) : L(L), t(x) { } + ~push_popper_n() { lua_pop(L, t); } + }; + template <> + struct push_popper_n { + push_popper_n(lua_State*, int) { } + }; template struct push_popper { T t; @@ -42,6 +53,10 @@ namespace sol { push_popper push_pop(T&& x) { return push_popper(std::forward(x)); } + template + push_popper_n pop_n(lua_State* L, int x) { + return push_popper_n(L, x); + } } // stack namespace detail { @@ -135,9 +150,8 @@ namespace sol { } type get_type() const noexcept { - push(); + auto pp = stack::push_pop(*this); int result = lua_type(L, -1); - lua_pop(L, 1); return static_cast(result); } diff --git a/sol/stack.hpp b/sol/stack.hpp index bc8858e4..5f6939ff 100644 --- a/sol/stack.hpp +++ b/sol/stack.hpp @@ -148,12 +148,14 @@ namespace sol { template inline int call_into_lua(types tr, types ta, lua_State* L, int start, Fx&& fx, FxArgs&&... fxargs) { call(tr, ta, L, start, std::forward(fx), std::forward(fxargs)...); + lua_settop(L, 0); return 0; } template>::value>> inline int call_into_lua(types, types ta, lua_State* L, int start, Fx&& fx, FxArgs&&... fxargs) { decltype(auto) r = call(types>(), ta, L, start, std::forward(fx), std::forward(fxargs)...); + lua_settop(L, 0); return push_reference(L, std::forward(r)); } @@ -167,11 +169,10 @@ namespace sol { inline call_syntax get_call_syntax(lua_State* L, const std::string& key, int index = -2) { luaL_getmetatable(L, key.c_str()); + auto pn = pop_n(L, 1); if (lua_compare(L, -1, index, LUA_OPEQ) == 1) { - lua_pop(L, 1); return call_syntax::colon; } - lua_pop(L, 1); return call_syntax::dot; } @@ -190,8 +191,8 @@ namespace sol { inline void luajit_exception_handler(lua_State* L, int(*handler)(lua_State*, lua_CFunction) = detail::c_trampoline) { #ifdef SOL_LUAJIT lua_pushlightuserdata(L, (void*)handler); + auto pn = pop_n(L, 1); luaJIT_setmode(L, -1, LUAJIT_MODE_WRAPCFUNC | LUAJIT_MODE_ON); - lua_pop(L, 1); #else (void)L; (void)handler; diff --git a/sol/stack_check.hpp b/sol/stack_check.hpp index cf7f7115..5888ea3e 100644 --- a/sol/stack_check.hpp +++ b/sol/stack_check.hpp @@ -209,14 +209,14 @@ namespace sol { 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); + handler(L, index, t, type::function); return false; } lua_getfield(L, -1, &callkey[0]); if (lua_isnoneornil(L, -1)) { - handler(L, index, t, type::function); lua_pop(L, 2); + handler(L, index, t, type::function); return false; } // has call, is definitely a function @@ -274,30 +274,15 @@ namespace sol { if (stack_detail::check_metatable>(L)) return true; bool success = false; -#ifndef SOL_NO_EXCEPTIONS - lua_getfield(L, -1, &detail::base_class_check_key()[0]); - if (type_of(L, -1) != type::nil) { - void* basecastdata = lua_touserdata(L, -1); - detail::throw_cast basecast = (detail::throw_cast)basecastdata; - success = detail::catch_check(basecast); + { + auto pn = stack::pop_n(L, 2); + lua_getfield(L, -1, &detail::base_class_check_key()[0]); + if (type_of(L, -1) != type::nil) { + void* basecastdata = lua_touserdata(L, -1); + detail::inheritance_check_function ic = (detail::inheritance_check_function)basecastdata; + success = ic(detail::id_for::value); + } } -#elif !defined(SOL_NO_RTTI) - lua_getfield(L, -1, &detail::base_class_check_key()[0]); - if (type_of(L, -1) != type::nil) { - void* basecastdata = lua_touserdata(L, -1); - detail::inheritance_check_function ic = (detail::inheritance_check_function)basecastdata; - success = ic(typeid(T)); - } -#else - // Topkek - lua_getfield(L, -1, &detail::base_class_check_key()[0]); - if (type_of(L, -1) != type::nil) { - void* basecastdata = lua_touserdata(L, -1); - detail::inheritance_check_function ic = (detail::inheritance_check_function)basecastdata; - success = ic(detail::id_for::value); - } -#endif // No Runtime Type Information || Exceptions - lua_pop(L, 2); if (!success) { handler(L, index, type::userdata, indextype); return false; diff --git a/sol/stack_get.hpp b/sol/stack_get.hpp index ad8fd3cc..505d7b06 100644 --- a/sol/stack_get.hpp +++ b/sol/stack_get.hpp @@ -289,24 +289,6 @@ namespace sol { } static T* get_no_nil_from(lua_State* L, void* udata, int index = -1) { -#ifndef SOL_NO_EXCEPTIONS - if (detail::has_derived::value && luaL_getmetafield(L, index, &detail::base_class_check_key()[0]) != 0) { - void* basecastdata = lua_touserdata(L, -1); - detail::throw_cast basecast = (detail::throw_cast)basecastdata; - // use the casting function to properly adjust the pointer for the desired T - udata = detail::catch_cast(udata, basecast); - lua_pop(L, 1); - } -#elif !defined(SOL_NO_RTTI) - if (detail::has_derived::value && luaL_getmetafield(L, index, &detail::base_class_cast_key()[0]) != 0) { - void* basecastdata = lua_touserdata(L, -1); - detail::inheritance_cast_function ic = (detail::inheritance_cast_function)basecastdata; - // use the casting function to properly adjust the pointer for the desired T - udata = ic(udata, typeid(T)); - lua_pop(L, 1); - } -#else - // Lol, you motherfucker if (detail::has_derived::value && luaL_getmetafield(L, index, &detail::base_class_cast_key()[0]) != 0) { void* basecastdata = lua_touserdata(L, -1); detail::inheritance_cast_function ic = (detail::inheritance_cast_function)basecastdata; @@ -314,7 +296,6 @@ namespace sol { udata = ic(udata, detail::id_for::value); lua_pop(L, 1); } -#endif // No Runtime Type Information || Exceptions T* obj = static_cast(udata); return obj; } diff --git a/sol/table_core.hpp b/sol/table_core.hpp index e4361dc0..17aea60b 100644 --- a/sol/table_core.hpp +++ b/sol/table_core.hpp @@ -54,8 +54,8 @@ namespace sol { sol::object key(base_t::lua_state(), -2); sol::object value(base_t::lua_state(), -1); std::pair keyvalue(key, value); + auto pn = stack::pop_n(base_t::lua_state(), 1); fx(keyvalue); - lua_pop(base_t::lua_state(), 1); } } @@ -66,8 +66,8 @@ namespace sol { while (lua_next(base_t::lua_state(), -2)) { sol::object key(base_t::lua_state(), -2); sol::object value(base_t::lua_state(), -1); + auto pn = stack::pop_n(base_t::lua_state(), 1); fx(key, value); - lua_pop(base_t::lua_state(), 1); } } @@ -231,8 +231,8 @@ namespace sol { template basic_table_core& traverse_set(Keys&&... keys) { auto pp = stack::push_pop::value>(*this); + auto pn = stack::pop_n(base_t::lua_state(), static_cast(sizeof...(Keys)-2)); traverse_set_deep(std::forward(keys)...); - lua_pop(base_t::lua_state(), static_cast(sizeof...(Keys)-2)); return *this; } diff --git a/sol/usertype_metatable.hpp b/sol/usertype_metatable.hpp index c27604ff..2ee1fbb6 100644 --- a/sol/usertype_metatable.hpp +++ b/sol/usertype_metatable.hpp @@ -188,20 +188,11 @@ namespace sol { return; } (void)detail::swallow{ 0, ((detail::has_derived::value = true), 0)... }; -#ifndef SOL_NO_EXCEPTIONS - static_assert(sizeof(void*) <= sizeof(detail::throw_cast), "The size of this data pointer is too small to fit the inheritance checking function: file a bug report."); - baseclasscheck = baseclasscast = (void*)&detail::throw_as; -#elif !defined(SOL_NO_RTTI) + static_assert(sizeof(void*) <= sizeof(detail::inheritance_check_function), "The size of this data pointer is too small to fit the inheritance checking function: file a bug report."); static_assert(sizeof(void*) <= sizeof(detail::inheritance_cast_function), "The size of this data pointer is too small to fit the inheritance checking function: file a bug report."); - baseclasscheck = (void*)&detail::inheritance::type_check; - baseclasscast = (void*)&detail::inheritance::type_cast; -#else - static_assert(sizeof(void*) <= sizeof(detail::inheritance_check_function), "The size of this data pointer is too small to fit the inheritance checking function: file a bug report."); - static_assert(sizeof(void*) <= sizeof(detail::inheritance_cast_function), "The size of this data pointer is too small to fit the inheritance checking function: file a bug report."); - baseclasscheck = (void*)&detail::inheritance::type_check; - baseclasscast = (void*)&detail::inheritance::type_cast; -#endif // No Runtime Type Information vs. Throw-Style Inheritance + baseclasscheck = (void*)&detail::inheritance::type_check; + baseclasscast = (void*)&detail::inheritance::type_cast; } template , base_classes_tag, call_construction>::value>> diff --git a/test_usertypes.cpp b/test_usertypes.cpp index a2d08771..0cf4abf7 100644 --- a/test_usertypes.cpp +++ b/test_usertypes.cpp @@ -627,6 +627,23 @@ TEST_CASE("usertype/private-constructible", "Check to make sure special snowflak REQUIRE(expectednumkilled == factory_test::num_killed); } +TEST_CASE("usertype/const-pointer", "Make sure const pointers can be taken") { + struct A { int x = 201; }; + struct B { + int foo(const A* a) { return a->x; }; + }; + + sol::state lua; + lua.new_usertype("B", + "foo", &B::foo + ); + lua.set("a", A()); + lua.set("b", B()); + lua.script("x = b:foo(a)"); + int x = lua["x"]; + REQUIRE(x == 201); +} + TEST_CASE("usertype/overloading", "Check if overloading works properly for usertypes") { struct woof { int var;