diff --git a/.gitmodules b/.gitmodules index 570fde0f..f6ae162a 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "Catch"] path = Catch - url = https://github.com/philsquared/Catch.git \ No newline at end of file + url = https://github.com/philsquared/Catch.git +[submodule "Optional"] + path = Optional + url = https://github.com/ThePhD/Optional.git diff --git a/Optional b/Optional new file mode 160000 index 00000000..dfd23923 --- /dev/null +++ b/Optional @@ -0,0 +1 @@ +Subproject commit dfd239237279fe3e1793daf23fffbe92209c8ae2 diff --git a/docs/source/api/stack.rst b/docs/source/api/stack.rst index dc57043a..381f02af 100644 --- a/docs/source/api/stack.rst +++ b/docs/source/api/stack.rst @@ -21,7 +21,7 @@ functions template auto get( lua_State* L, int index = -1 ) -Retrieves the value of the object at ``index`` in the stack. The return type varies based on ``T``: with primitive types, it is usually ``T``: for all unrecognized ``T``, it is generally a ``T&`` or whatever the extension point :ref:`stack::getter\` implementation returns. The type ``T`` has top-level ``const`` qualifiers and reference modifiers removed before being forwarded to the extension point :ref:`stack::getter\` struct. +Retrieves the value of the object at ``index`` in the stack. The return type varies based on ``T``: with primitive types, it is usually ``T``: for all unrecognized ``T``, it is generally a ``T&`` or whatever the extension point :ref:`stack::getter\` implementation returns. The type ``T`` has top-level ``const`` qualifiers and reference modifiers removed before being forwarded to the extension point :ref:`stack::getter\` struct. ``stack::get`` will default to forwarding all arguments to the :ref:`stack::check_get` function with a handler of ``type_panic`` to strongly alert for errors, if you ask for the :ref:`safety`. .. code-block:: cpp :caption: function: check @@ -53,6 +53,17 @@ Checks if the object at ``index`` is of type ``T``. If it is not, it will call t Based on how it is called, pushes a variable amount of objects onto the stack. in 99% of cases, returns for 1 object pushed onto the stack. For the case of a ``std::tuple<...>``, it recursively pushes each object contained inside the tuple, from left to right, resulting in a variable number of things pushed onto the stack (this enables multi-valued returns when binding a C++ function to a Lua). Can be called with ``sol::stack::push( L, args... )`` to have arguments different from the type that wants to be pushed, or ``sol::stack::push( L, arg, args... )`` where ``T`` will be inferred from ``arg``. The final form of this function is ``sol::stack::multi_push``, which will call one ``sol::stack::push`` for each argument. The ``T`` that describes what to push is first sanitized by removing top-level ``const`` qualifiers and reference qualifiers before being forwarded to the extension point :ref:`stack::pusher\` struct. +.. code-block:: cpp + :caption: function: check_get + :name: stack-check-get + + template + auto check_get( lua_State* L, int index = -1 ) + template + auto check_get( lua_State* L, int index, Handler&& handler ) + +Retrieves the value of the object at ``index`` in the stack, but does so safely. It returns an ``optional``, where ``U`` in this case is the return type deduced from ``stack::get``. This allows a person to properly check if the type they're getting is what they actually want, and gracefully handle errors when working with the stack if they so choose to. You can define ``SOL_CHECK_ARGUMENTS`` to turn on additional :ref:`safety`, in which ``stack::get`` will default to calling this version of the function with a handler of ``type_panic`` to strongly alert for errors and help you track bugs if you suspect something might be going wrong in your system. + .. code-block:: cpp :caption: function: set_field diff --git a/docs/source/api/table.rst b/docs/source/api/table.rst index 3d8cd598..92e391da 100644 --- a/docs/source/api/table.rst +++ b/docs/source/api/table.rst @@ -121,4 +121,18 @@ Sets the desired function to the specified key value. Note that it also allows f Creates a table, optionally with the specified values pre-set into the table. If ``narr`` or ``nrec`` are 0, then compile-time shenanigans are used to guess the amount of array entries (e.g., integer keys) and the amount of hashable entries (e.g., all other entries). +.. code-block:: cpp + :caption: function: create a table with compile-time defaults assumed + :name: table-create-with + + table create(int narr = 0, int nrec = 0); + template + table create(int narr, int nrec, Key&& key, Value&& value, Args&&... args); + + static table create(lua_State* L, int narr = 0, int nrec = 0); + template + static table create(lua_State* L, int narr, int nrec, Key&& key, Value&& value, Args&&... args); + +Creates a table, optionally with the specified values pre-set into the table. If ``narr`` or ``nrec`` are 0, then compile-time shenanigans are used to guess the amount of array entries (e.g., integer keys) and the amount of hashable entries (e.g., all other entries). + .. _input iterators: http://en.cppreference.com/w/cpp/concept/InputIterator \ No newline at end of file diff --git a/docs/source/features.rst b/docs/source/features.rst index 75024fbd..6e69fe10 100644 --- a/docs/source/features.rst +++ b/docs/source/features.rst @@ -85,7 +85,7 @@ The below feature table checks for the presence of something. It, however, does +---------------------------+-------------+------------+----------+---------+----------+-----------+-----------+----------------+----------+ | inheritance | ~ | ✗ | ✗ | ✔ | ✔ | ✔ | ~ | ~ | ✔ | +---------------------------+-------------+------------+----------+---------+----------+-----------+-----------+----------------+----------+ -| overloading | ~ | ✗ | ✗ | ✗ | ✗ | ✔ | ✗ | ✗ | ✗ | +| overloading | ~ | ✗ | ✗ | ✗ | ✗ | ✔ | ✗ | ✗ | ✔ | +---------------------------+-------------+------------+----------+---------+----------+-----------+-----------+----------------+----------+ | thread | ✔ | ✗ | ✗ | ✗ | ✗ | ✔ | ✔ | ✗ | ✔ | +---------------------------+-------------+------------+----------+---------+----------+-----------+-----------+----------------+----------+ @@ -171,7 +171,6 @@ kaguya - TODO: * SWIG - http://www.swig.org/Doc1.3/Lua.html#Lua_nn2 * SLB3 - https://code.google.com/archive/p/slb/ -* Luwra - https://github.com/vapourismo/luwra .. _Go read the docs.: https://oolua.org/docs/index.html \ No newline at end of file diff --git a/docs/source/index.rst b/docs/source/index.rst index ce4fd412..e3c0845c 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -24,6 +24,7 @@ get going: api/top features benchmarks + safety exceptions rtti licenses diff --git a/docs/source/safety.rst b/docs/source/safety.rst new file mode 100644 index 00000000..33a1ee49 --- /dev/null +++ b/docs/source/safety.rst @@ -0,0 +1,10 @@ +safety +====== + +Sol was designed to be correct and fast, and in the pursuit of both uses the regular ``lua_to{x}`` functions of Lua rather than the checking versions (``lua_check{X}``) functions. The API defaults to safe alternatives if you have a ``#define SOL_CHECK_ARGUMENTS`` before you include Sol, or if you pass the ``SOL_CHECK_ARGUMENTS`` define on the build command for your build system. By default, it is off and remains off unless you define this, even in debug mode. + +``SOL_CHECK_ARGUMENTS`` triggers the following changes: + * ``stack::get`` (used everywhere) defaults to using ``stack::check_get`` and dereferencing the argument. It uses ``type_panic`` as the handler if something goes wrong. + * ``stack::call`` will, if no template boolean is specified, check all of the arguments for a function call. + +Remember that if you want these features, you must explicitly turn them on. \ No newline at end of file diff --git a/sol/compatibility/version.hpp b/sol/compatibility/version.hpp index f3294609..db26f6e8 100644 --- a/sol/compatibility/version.hpp +++ b/sol/compatibility/version.hpp @@ -43,22 +43,48 @@ #endif // Lua Version 502, 501 || luajit, 500 #ifdef _MSC_VER +#ifdef _DEBUG +#ifndef SOL_CHECK_ARGUMENTS +// Do not define by default: let user turn it on +//#define SOL_CHECK_ARGUMENTS +#endif // Check Arguments +#endif // Debug #ifndef _CPPUNWIND +#ifndef SOL_NO_EXCEPTIONS #define SOL_NO_EXCEPTIONS 1 -#endif // No +#endif +#endif // Automatic Exceptions + #ifndef _CPPRTTI +#ifndef SOL_NO_RTTI #define SOL_NO_RTTI 1 +#endif #endif // Automatic RTTI #elif defined(__GNUC__) || defined(__clang__) +#ifndef NDEBUG +#ifndef __OPTIMIZE__ +#ifndef SOL_CHECK_ARGUMENTS +// Do not define by default: let user choose +//#define SOL_CHECK_ARGUMENTS +#endif // Check Arguments +#endif // g++ optimizer flag +#endif // Not Debug + #ifndef __EXCEPTIONS +#ifndef SOL_NO_EXCEPTIONS #define SOL_NO_EXCEPTIONS 1 +#endif #endif // No Exceptions + #ifndef __GXX_RTTI +#ifndef SOL_NO_RTII #define SOL_NO_RTTI 1 +#endif #endif // No RTTI + #endif // vc++ || clang++/g++ #endif // SOL_VERSION_HPP diff --git a/sol/coroutine.hpp b/sol/coroutine.hpp index be92e713..af81c008 100644 --- a/sol/coroutine.hpp +++ b/sol/coroutine.hpp @@ -43,11 +43,7 @@ private: template auto invoke( types, std::index_sequence, std::ptrdiff_t n ) { luacall(n, sizeof...(Ret)); - int stacksize = lua_gettop(lua_state()); - int firstreturn = std::max(1, stacksize - static_cast(sizeof...(Ret)) + 1); - auto r = stack::get>(lua_state(), firstreturn); - lua_pop(lua_state(), static_cast(sizeof...(Ret))); - return r; + return stack::pop>(lua_state()); } template diff --git a/sol/demangle.hpp b/sol/demangle.hpp index 0fc3301f..210e08cd 100644 --- a/sol/demangle.hpp +++ b/sol/demangle.hpp @@ -113,7 +113,7 @@ inline std::string get_type_name() { #endif // No Runtime Type information template -inline std::string demangle() { +inline std::string demangle_once() { #ifndef SOL_NO_RTTI std::string realname = get_type_name(typeid(T)); #else @@ -139,6 +139,12 @@ inline std::string demangle() { return realname; } + +template +inline std::string demangle() { + static const std::string d = demangle_once(); + return d; +} } // detail } // sol diff --git a/sol/function.hpp b/sol/function.hpp index f9fdacde..bedfd173 100644 --- a/sol/function.hpp +++ b/sol/function.hpp @@ -53,11 +53,7 @@ private: template auto invoke( types, std::index_sequence, std::ptrdiff_t n ) const { luacall( n, sizeof...( Ret ) ); - int stacksize = lua_gettop( lua_state( ) ); - int firstreturn = std::max(1, stacksize - static_cast(sizeof...(Ret)) + 1); - auto r = stack::get>( lua_state( ), firstreturn ); - lua_pop(lua_state(), static_cast(sizeof...(Ret))); - return r; + return stack::pop>( lua_state( ) ); } template @@ -129,6 +125,24 @@ struct pusher> { set_fx(std::false_type(), L, fxptr); } + template + static void set(lua_State* L, R (C::*memfxptr)(Args...)) { + // Layout: + // idx 1...n: verbatim data of member function pointer + lua_CFunction freefunc = &function_detail::upvalue_this_member_function::call; + int upvalues = stack::stack_detail::push_as_upvalues(L, memfxptr); + stack::push(L, freefunc, upvalues); + } + + template + static void set(lua_State* L, Sig C::* memfxptr) { + // Layout: + // idx 1...n: verbatim data of member function pointer + lua_CFunction freefunc = &function_detail::upvalue_this_member_function::call; + int upvalues = stack::stack_detail::push_as_upvalues(L, memfxptr); + stack::push(L, freefunc, upvalues); + } + template static void set(lua_State* L, R (C::*memfxptr)(Args...), T&& obj) { typedef meta::Bool, std::reference_wrapper>::value || std::is_pointer::value> is_reference; @@ -148,7 +162,7 @@ struct pusher> { template static void set_isconvertible_fx(std::true_type, types, lua_State* L, Fx&& fx) { - typedef R(* fx_ptr_t)(Args...); + using fx_ptr_t = R(*)(Args...); fx_ptr_t fxptr = detail::unwrap(std::forward(fx)); set(L, fxptr); } @@ -203,7 +217,7 @@ struct pusher> { static void set_fx(lua_State* L, std::unique_ptr luafunc) { function_detail::base_function* target = luafunc.release(); - void* targetdata = reinterpret_cast(target); + void* targetdata = static_cast(target); lua_CFunction freefunc = function_detail::call; stack::push(L, userdata_value(targetdata)); diff --git a/sol/function_types_allocator.hpp b/sol/function_types_allocator.hpp index ab830522..7abd89d8 100644 --- a/sol/function_types_allocator.hpp +++ b/sol/function_types_allocator.hpp @@ -23,6 +23,7 @@ #define SOL_FUNCTION_TYPES_ALLOCATOR_HPP #include "raii.hpp" +#include "stack.hpp" #include "function_types_overload.hpp" namespace sol { diff --git a/sol/function_types_member.hpp b/sol/function_types_member.hpp index fe6b4c5a..3b2b6525 100644 --- a/sol/function_types_member.hpp +++ b/sol/function_types_member.hpp @@ -46,6 +46,37 @@ struct functor_function : public base_function { } }; +template +struct this_member_function : public base_function { + typedef std::remove_pointer_t> function_type; + typedef meta::function_return_t return_type; + typedef meta::function_args_t args_types; + struct functor { + function_type invocation; + + template + functor(Args&&... args): invocation(std::forward(args)...) {} + + template + return_type operator()(lua_State* L, Args&&... args) { + auto& mem = detail::unwrap(stack::get(L, 1)); + return (mem.*invocation)(std::forward(args)...); + } + } fx; + + template + this_member_function(Args&&... args): fx(std::forward(args)...) {} + + int call(lua_State* L) { + return stack::call_into_lua(meta::tuple_types(), args_types(), fx, L, 2, L); + } + + virtual int operator()(lua_State* L) override { + auto f = [&](lua_State* L) -> int { return this->call(L);}; + return detail::trampoline(L, f); + } +}; + template struct member_function : public base_function { typedef std::remove_pointer_t> function_type; diff --git a/sol/function_types_static.hpp b/sol/function_types_static.hpp index 60e62830..2c24cefe 100644 --- a/sol/function_types_static.hpp +++ b/sol/function_types_static.hpp @@ -57,7 +57,9 @@ struct upvalue_member_function { auto objdata = stack::stack_detail::get_as_upvalues(L, memberdata.second); function_type& memfx = memberdata.first; T& item = *objdata.first; - auto fx = [&item, &memfx](auto&&... args) -> typename traits_type::return_type { return (item.*memfx)(std::forward(args)...); }; + auto fx = [&item, &memfx](auto&&... args) -> typename traits_type::return_type { + return (item.*memfx)(std::forward(args)...); + }; return stack::call_into_lua(meta::tuple_types(), typename traits_type::args_type(), fx, L, 1); } @@ -69,6 +71,30 @@ struct upvalue_member_function { return call(L); } }; + +template +struct upvalue_this_member_function { + typedef std::remove_pointer_t> function_type; + typedef meta::function_traits traits_type; + + static int real_call(lua_State* L) { + auto memberdata = stack::stack_detail::get_as_upvalues(L, 1); + function_type& memfx = memberdata.first; + auto fx = [&memfx](lua_State* L, auto&&... args) -> typename traits_type::return_type { + T& item = stack::get(L, 1); + return (item.*memfx)(std::forward(args)...); + }; + return stack::call_into_lua(meta::tuple_types(), typename traits_type::args_type(), fx, L, 2, L); + } + + static int call (lua_State* L) { + return detail::static_trampoline<(&real_call)>(L); + } + + int operator()(lua_State* L) { + return call(L); + } +}; } // function_detail } // sol diff --git a/sol/optional.hpp b/sol/optional.hpp new file mode 100644 index 00000000..ec2e41c0 --- /dev/null +++ b/sol/optional.hpp @@ -0,0 +1,42 @@ +// The MIT License (MIT) + +// Copyright (c) 2013-2016 Rapptz, ThePhD and contributors + +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +#ifndef SOL_OPTIONAL_HPP +#define SOL_OPTIONAL_HPP + +#if __cplusplus > 201402L +#include +#else +#include "Optional/optional.hpp" +#endif // C++ 14 + +namespace sol { + +#if __cplusplus > 201402L +template +using optional = sol::optional; +using nullopt_t = std::nullopt_t; +constexpr nullopt_t nullopt = std::experimental::nullopt; +#else +#endif // C++ 14 +} + +#endif // SOL_OPTIONAL_HPP diff --git a/sol/protected_function.hpp b/sol/protected_function.hpp index 961c7525..aeb8d940 100644 --- a/sol/protected_function.hpp +++ b/sol/protected_function.hpp @@ -68,11 +68,7 @@ private: template auto invoke(types, std::index_sequence, std::ptrdiff_t n, handler& h) const { luacall(n, sizeof...(Ret), h); - int stacksize = lua_gettop(lua_state()); - int firstreturn = std::max(0, stacksize - static_cast(sizeof...(Ret)) + 1); - auto r = stack::get>(lua_state(), firstreturn); - lua_pop(lua_state(), static_cast(sizeof...(Ret))); - return r; + return stack::pop>(lua_state()); } template diff --git a/sol/proxy.hpp b/sol/proxy.hpp index 85e7e6c3..fdab1971 100644 --- a/sol/proxy.hpp +++ b/sol/proxy.hpp @@ -114,6 +114,7 @@ template inline bool operator!=(const proxy& right, T&& left) { return right.template get>() != left; } + namespace stack { template struct pusher> { diff --git a/sol/stack.hpp b/sol/stack.hpp index 50df86b3..fdabed06 100644 --- a/sol/stack.hpp +++ b/sol/stack.hpp @@ -22,868 +22,18 @@ #ifndef SOL_STACK_HPP #define SOL_STACK_HPP -#include "error.hpp" -#include "reference.hpp" -#include "userdata.hpp" -#include "tuple.hpp" -#include "traits.hpp" -#include "usertype_traits.hpp" -#include "inheritance.hpp" -#include "overload.hpp" -#include "raii.hpp" -#include -#include +#include "stack_core.hpp" +#include "stack_check.hpp" +#include "stack_get.hpp" +#include "stack_check_get.hpp" +#include "stack_push.hpp" +#include "stack_pop.hpp" +#include "stack_field.hpp" #include -#include +#include namespace sol { namespace stack { -template -struct field_getter; -template -struct field_setter; -template -struct getter; -template -struct popper; -template -struct pusher; -template::value, typename = void> -struct checker; - -template -inline int push(lua_State* L, T&& t, Args&&... args) { - return pusher>{}.push(L, std::forward(t), std::forward(args)...); -} - -// overload allows to use a pusher of a specific type, but pass in any kind of args -template -inline int push(lua_State* L, Arg&& arg, Args&&... args) { - return pusher>{}.push(L, std::forward(arg), std::forward(args)...); -} - -inline int multi_push(lua_State*) { - // do nothing - return 0; -} - -template -inline int multi_push(lua_State* L, T&& t, Args&&... args) { - int pushcount = push(L, std::forward(t)); - void(sol::detail::swallow{(pushcount += sol::stack::push(L, std::forward(args)), 0)... }); - return pushcount; -} - -template -inline decltype(auto) get(lua_State* L, int index = -1) { - return getter>{}.get(L, index); -} - -template -inline decltype(auto) pop(lua_State* L) { - return popper>{}.pop(L); -} - -template -bool check(lua_State* L, int index, Handler&& handler) { - typedef meta::Unqualified Tu; - checker c; - // VC++ has a bad warning here: shut it up - (void)c; - return c.check(L, index, std::forward(handler)); -} - -template -bool check(lua_State* L, int index = -1) { - auto handler = no_panic; - return check(L, index, handler); -} - -template -void get_field(lua_State* L, Key&& key) { - field_getter, global>{}.get(L, std::forward(key)); -} - -template -void get_field(lua_State* L, Key&& key, int tableindex) { - field_getter, global>{}.get(L, std::forward(key), tableindex); -} - -template -void set_field(lua_State* L, Key&& key, Value&& value) { - field_setter, global>{}.set(L, std::forward(key), std::forward(value)); -} - -template -void set_field(lua_State* L, Key&& key, Value&& value, int tableindex) { - field_setter, global>{}.set(L, std::forward(key), std::forward(value), tableindex); -} - -namespace stack_detail { -const bool default_check_arguments = -#ifdef SOL_CHECK_ARGUMENTS -true; -#else -false; -#endif - -template -inline bool check_metatable(lua_State* L, int index = -2) { - luaL_getmetatable(L, &usertype_traits::metatable[0]); - const type expectedmetatabletype = static_cast(lua_type(L, -1)); - if (expectedmetatabletype != type::nil) { - if (lua_rawequal(L, -1, index) == 1) { - lua_pop(L, 2); - return true; - } - } - lua_pop(L, 1); - return false; -} - -template -inline int push_upvalues(lua_State* L, TCont&& cont) { - int n = 0; - for(auto& c : cont) { - if(releasemem) { - stack::push(L, c.release()); - } - else { - stack::push(L, c.get()); - } - ++n; - } - return n; -} -} // stack_detail - -template -struct getter { - static T& get(lua_State* L, int index = -1) { - return getter{}.get(L, index); - } -}; - -template -struct getter::value>> { - static T get(lua_State* L, int index = -1) { - return static_cast(lua_tonumber(L, index)); - } -}; - -template -struct getter, std::is_signed>::value>> { - static T get(lua_State* L, int index = -1) { - return static_cast(lua_tointeger(L, index)); - } -}; - -template -struct getter, std::is_unsigned>::value>> { - static T get(lua_State* L, int index = -1) { - return static_cast(lua_tointeger(L, index)); - } -}; - -template -struct getter::value>> { - static T get(lua_State* L, int index = -1) { - return T(L, index); - } -}; - -template<> -struct getter { - static userdata_value get(lua_State* L, int index = -1) { - return{ lua_touserdata(L, index) }; - } -}; - -template<> -struct getter { - static light_userdata_value get(lua_State* L, int index = -1) { - return{ lua_touserdata(L, index) }; - } -}; - -template<> -struct getter { - static void* get(lua_State* L, int index = -1) { - return lua_touserdata(L, index); - } -}; -} // stack - -namespace detail { -// This needs to be here, specifically, so that it can use get -using special_destruct_func = void(*)(void*); - -template -inline void special_destruct(void* memory) { - T** pointerpointer = static_cast(memory); - special_destruct_func* dx = static_cast(static_cast(pointerpointer + 1)); - Real* target = static_cast(static_cast(dx + 1)); - target->~Real(); -} - -template -inline int unique_destruct(lua_State* L) { - void* memory = stack::get(L, 1); - T** pointerpointer = static_cast(memory); - special_destruct_func& dx = *static_cast( static_cast( pointerpointer + 1 ) ); - (dx)(memory); - return 0; -} -} // detail - -namespace stack { - -template -struct getter { - static T* get_no_nil(lua_State* L, int index = -1) { - void** pudata = static_cast(lua_touserdata(L, index)); - void* udata = *pudata; - return get_no_nil_from(L, udata, index); - } - - static T* get_no_nil_from(lua_State* L, void* udata, int index = -1) { -#ifndef SOL_NO_EXCEPTIONS - if (luaL_getmetafield(L, index, &detail::base_class_check_key()[0]) != 0) { - void* basecastdata = stack::get(L); - 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 (luaL_getmetafield(L, index, &detail::base_class_cast_key()[0]) != 0) { - void* basecastdata = stack::get(L); - 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 (luaL_getmetafield(L, index, &detail::base_class_cast_key()[0]) != 0) { - void* basecastdata = stack::get(L); - 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, detail::id_for::value); - lua_pop(L, 1); - } -#endif // No Runtime Type Information || Exceptions - T* obj = static_cast(udata); - return obj; - } - - static T* get(lua_State* L, int index = -1) { - type t = type_of(L, index); - if (t == type::nil) - return nullptr; - return get_no_nil(L, index); - } -}; - -template -struct getter> { - static Real& get(lua_State* L, int index = -1) { - T** pref = static_cast(lua_touserdata(L, index)); - detail::special_destruct_func* fx = static_cast(static_cast(pref + 1)); - Real* mem = static_cast(static_cast(fx + 1)); - return *mem; - } -}; - -template -struct getter> { - static T* get(lua_State* L, int index = -1) { - return getter::get_no_nil(L, index); - } -}; - -template -struct getter { - static T& get(lua_State* L, int index = -1) { - return *getter::get_no_nil(L, index); - } -}; - -template -struct getter> { - static std::shared_ptr& get(lua_State* L, int index = -1) { - return getter>>::get(L, index); - } -}; - -template -struct getter> { - static std::unique_ptr& get(lua_State* L, int index = -1) { - return getter>>::get(L, index); - } -}; - -template -struct getter> { - static T& get(lua_State* L, int index = -1) { - return getter{}.get(L, index); - } -}; - -template<> -struct getter { - static type get(lua_State *L, int index){ - return static_cast(lua_type(L, index)); - } -}; - -template<> -struct getter { - static bool get(lua_State* L, int index) { - return lua_toboolean(L, index) != 0; - } -}; - -template<> -struct getter { - static std::string get(lua_State* L, int index = -1) { - std::size_t len; - auto str = lua_tolstring(L, index, &len); - return { str, len }; - } -}; - -template<> -struct getter { - static const char* get(lua_State* L, int index = -1) { - return lua_tostring(L, index); - } -}; - -template<> -struct getter { - static nil_t get(lua_State*, int = -1) { - return nil_t{ }; - } -}; - -template<> -struct getter { - static lua_CFunction get(lua_State* L, int index = -1) { - return lua_tocfunction(L, index); - } -}; - -template<> -struct getter { - static c_closure get(lua_State* L, int index = -1) { - return c_closure(lua_tocfunction(L, index), -1); - } -}; - -template -struct getter> { - template - static decltype(auto) apply(std::index_sequence, lua_State* L, int index = -1) { - index = lua_absindex(L, index); - return std::tuple(L, index + I))...>(stack::get(L, index + I)...); - } - - static decltype(auto) get(lua_State* L, int index = -1) { - return apply(std::index_sequence_for(), L, index); - } -}; - -template -struct getter> { - static decltype(auto) get(lua_State* L, int index = -1) { - index = lua_absindex(L, index); - return std::pair(L, index)), decltype(stack::get(L, index))>(stack::get(L, index), stack::get(L, index + 1)); - } -}; - -template -struct checker { - template - static bool check (lua_State* L, int index, const Handler& handler) { - const type indextype = type_of(L, index); - bool success = expected == indextype; - if (!success) { - // expected type, actual type - handler(L, index, expected, indextype); - } - return success; - } -}; - -template -struct checker { - template - static bool check (lua_State* L, int index, const Handler& handler) { - const type indextype = type_of(L, index); - // Allow nil to be transformed to nullptr - if (indextype == type::nil) { - return true; - } - return checker{}.check(types(), L, indextype, index, handler); - } -}; - -template -struct checker { - template - static bool check (types, lua_State* L, type indextype, int index, const Handler& handler) { - if (indextype != type::userdata) { - handler(L, index, type::userdata, indextype); - return false; - } - if (meta::Or, std::is_same>::value) - return true; - if (lua_getmetatable(L, index) == 0) { - handler(L, index, type::userdata, indextype); - return false; - } - if (stack_detail::check_metatable(L)) - return true; - if (stack_detail::check_metatable(L)) - return true; - if (stack_detail::check_metatable>(L)) - return true; -#ifndef SOL_NO_EXCEPTIONS - lua_getfield(L, -1, &detail::base_class_check_key()[0]); - void* basecastdata = stack::get(L); - detail::throw_cast basecast = (detail::throw_cast)basecastdata; - bool success = detail::catch_check(basecast); -#elif !defined(SOL_NO_RTTI) - lua_getfield(L, -1, &detail::base_class_check_key()[0]); - if (stack::get(L) == type::nil) { - lua_pop(L, 2); - return false; - } - void* basecastdata = stack::get(L); - detail::inheritance_check_function ic = (detail::inheritance_check_function)basecastdata; - bool success = ic(typeid(T)); -#else - // Topkek - lua_getfield(L, -1, &detail::base_class_check_key()[0]); - if (stack::get(L) == type::nil) { - lua_pop(L, 2); - return false; - } - void* basecastdata = stack::get(L); - detail::inheritance_check_function ic = (detail::inheritance_check_function)basecastdata; - bool 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; - } - return true; - } - - template - static bool check (lua_State* L, int index, const Handler& handler) { - const type indextype = type_of(L, index); - return check(types(), L, indextype, index, handler); - } -}; - -template -struct popper { - inline decltype(auto) pop(lua_State* L) { - decltype(auto) r = get(L); - lua_pop(L, 1); - return r; - } -}; - -template -struct popper> { - inline decltype(auto) pop(lua_State* L) { - decltype(auto) r = get>(L, lua_gettop(L) - sizeof...(Args) + 1); - lua_pop(L, static_cast(sizeof...(Args))); - return r; - } -}; - -template -struct popper> { - inline decltype(auto) pop(lua_State* L) { - decltype(auto) r = get>(L, lua_gettop(L) - 2 + 1); - lua_pop(L, 2); - return r; - } -}; - -template -struct pusher { - template - static int push(lua_State* L, Args&&... args) { - // Basically, we store all user-data like this: - // If it's a movable/copyable value (no std::ref(x)), then we store the pointer to the new - // data in the first sizeof(T*) bytes, and then however many bytes it takes to - // do the actual object. Things that are std::ref or plain T* are stored as - // just the sizeof(T*), and nothing else. - T** pointerpointer = static_cast(lua_newuserdata(L, sizeof(T*) + sizeof(T))); - T*& referencereference = *pointerpointer; - T* allocationtarget = reinterpret_cast(pointerpointer + 1); - referencereference = allocationtarget; - std::allocator alloc{}; - alloc.construct(allocationtarget, std::forward(args)...); - luaL_getmetatable(L, &usertype_traits::metatable[0]); - lua_setmetatable(L, -2); - return 1; - } -}; - -template -struct pusher { - static int push(lua_State* L, T* obj) { - if (obj == nullptr) - return stack::push(L, nil); - T** pref = static_cast(lua_newuserdata(L, sizeof(T*))); - *pref = obj; - luaL_getmetatable(L, &usertype_traits::metatable[0]); - lua_setmetatable(L, -2); - return 1; - } -}; - -template -struct pusher> { - template - static int push(lua_State* L, Args&&... args) { - T** pref = static_cast(lua_newuserdata(L, sizeof(T*) + sizeof(detail::special_destruct_func) + sizeof(Real))); - detail::special_destruct_func* fx = static_cast(static_cast(pref + 1)); - Real* mem = static_cast(static_cast(fx + 1)); - *fx = detail::special_destruct; - detail::default_construct::construct(mem, std::forward(args)...); - *pref = std::addressof(detail::deref(*mem)); - if (luaL_newmetatable(L, &usertype_traits>::metatable[0]) == 1) { - set_field(L, "__gc", detail::unique_destruct); - } - lua_setmetatable(L, -2); - return 1; - } -}; - -template -struct pusher::value>> { - template - static int push(lua_State* L, Args&&... args) { - typedef typename is_unique_usertype::metatable_type meta_type; - return stack::push>(L, std::forward(args)...); - } -}; - -template -struct pusher> { - static int push(lua_State* L, std::unique_ptr obj) { - if (obj == nullptr) - return stack::push(L, nil); - return stack::push>>(L, std::move(obj)); - } -}; - -template -struct pusher> { - template - static int push(lua_State* L, S&& s) { - if (s == nullptr) - return stack::push(L, nil); - return stack::push>>(L, std::forward(s)); - } -}; - - -template -struct pusher> { - static int push(lua_State* L, const std::reference_wrapper& t) { - return stack::push(L, std::addressof(detail::deref(t.get()))); - } -}; - -template -struct pusher::value>> { - static int push(lua_State* L, const T& value) { - lua_pushnumber(L, value); - return 1; - } -}; - -template -struct pusher, std::is_signed>::value>> { - static int push(lua_State* L, const T& value) { - lua_pushinteger(L, static_cast(value)); - return 1; - } -}; - -template -struct pusher, std::is_unsigned>::value>> { - static int push(lua_State* L, const T& value) { - typedef std::make_signed_t signed_int; - return stack::push(L, static_cast(value)); - } -}; - -template -struct pusher, meta::Not>, meta::Not>>::value>> { - static int push(lua_State* L, const T& cont) { - lua_createtable(L, static_cast(cont.size()), 0); - int tableindex = lua_gettop(L); - unsigned index = 1; - for(auto&& i : cont) { - set_field(L, index++, i, tableindex); - } - return 1; - } -}; - -template -struct pusher, meta::has_key_value_pair, meta::Not>>::value>> { - static int push(lua_State* L, const T& cont) { - lua_createtable(L, static_cast(cont.size()), 0); - int tableindex = lua_gettop(L); - for(auto&& pair : cont) { - set_field(L, pair.first, pair.second, tableindex); - } - return 1; - } -}; - -template -struct pusher::value>> { - static int push(lua_State*, T& ref) { - return ref.push(); - } - - static int push(lua_State*, T&& ref) { - return ref.push(); - } -}; - -template<> -struct pusher { - static int push(lua_State* L, bool b) { - lua_pushboolean(L, b); - return 1; - } -}; - -template<> -struct pusher { - static int push(lua_State* L, nil_t) { - lua_pushnil(L); - return 1; - } -}; - -template<> -struct pusher> { - static int push(lua_State* L, lua_CFunction func, int n = 0) { - lua_pushcclosure(L, func, n); - return 1; - } -}; - -template<> -struct pusher { - static int push(lua_State* L, lua_CFunction func, int n = 0) { - lua_pushcclosure(L, func, n); - return 1; - } -}; - -template<> -struct pusher { - static int push(lua_State* L, c_closure closure) { - lua_pushcclosure(L, closure.c_function, closure.upvalues); - return 1; - } -}; - -template<> -struct pusher { - static int push(lua_State* L, void* userdata) { - lua_pushlightuserdata(L, userdata); - return 1; - } -}; - -template<> -struct pusher { - static int push(lua_State* L, light_userdata_value userdata) { - lua_pushlightuserdata(L, userdata); - return 1; - } -}; - -template<> -struct pusher { - static int push(lua_State* L, userdata_value data) { - void** ud = static_cast(lua_newuserdata(L, sizeof(void*))); - *ud = data.value; - return 1; - } -}; - -template<> -struct pusher { - static int push(lua_State* L, const char* str) { - lua_pushlstring(L, str, std::char_traits::length(str)); - return 1; - } -}; - -template -struct pusher { - static int push(lua_State* L, const char (&str)[N]) { - lua_pushlstring(L, str, N - 1); - return 1; - } -}; - -template<> -struct pusher { - static int push(lua_State* L, const std::string& str) { - lua_pushlstring(L, str.c_str(), str.size()); - return 1; - } -}; - -template -struct pusher> { - template - static int push(std::index_sequence, lua_State* L, T&& t) { - int pushcount = 0; - (void)detail::swallow{ 0, (pushcount += stack::push(L, - detail::forward_get(t) - ), 0)... }; - return pushcount; - } - - template - static int push(lua_State* L, T&& t) { - return push(std::index_sequence_for(), L, std::forward(t)); - } -}; - -template -struct pusher> { - template - static int push(lua_State* L, T&& t) { - int pushcount = stack::push(L, detail::forward_get<0>(t)); - pushcount += stack::push(L, detail::forward_get<1>(t)); - return pushcount; - } -}; - -template -struct field_getter { - template - void get(lua_State* L, Key&& key, int tableindex = -2) { - push( L, std::forward( key ) ); - lua_gettable( L, tableindex ); - } -}; - -template -struct field_getter, b, C> { - template - void apply(std::index_sequence, lua_State* L, Keys&& keys, int tableindex) { - tableindex = lua_absindex(L, tableindex); - void(detail::swallow{ (get_field(L, detail::forward_get(keys), tableindex), 0)... }); - reference saved(L, -1); - lua_pop(L, static_cast(sizeof...(I) + 1)); - saved.push(); - } - - template - void get(lua_State* L, Keys&& keys, int tableindex = -2) { - apply(std::index_sequence_for(), L, std::forward(keys), tableindex); - } -}; - -template -struct field_getter, b, C> { - template - void apply(lua_State* L, Keys&& keys, int tableindex) { - tableindex = lua_absindex(L, tableindex); - get_field(L, detail::forward_get<0>(keys), tableindex); - get_field(L, detail::forward_get<1>(keys), tableindex + 1); - reference saved(L, -1); - lua_pop(L, static_cast(2 + 1)); - saved.push(); - } -}; - -template -struct field_getter::value>> { - template - void get(lua_State* L, Key&& key, int = -1) { - lua_getglobal(L, &key[0]); - } -}; - -template -struct field_getter::value>> { - template - void get(lua_State* L, Key&& key, int tableindex = -1) { - lua_getfield(L, tableindex, &key[0]); - } -}; - -#if SOL_LUA_VERSION >= 503 -template -struct field_getter::value>> { - template - void get(lua_State* L, Key&& key, int tableindex = -1) { - lua_geti(L, tableindex, static_cast(key)); - } -}; -#endif // Lua 5.3.x - -template -struct field_setter { - template - void set(lua_State* L, Key&& key, Value&& value, int tableindex = -3) { - push(L, std::forward(key)); - push(L, std::forward(value)); - lua_settable(L, tableindex); - } -}; - -template -struct field_setter::value>> { - template - void set(lua_State* L, Key&& key, Value&& value, int = -2) { - push(L, std::forward(value)); - lua_setglobal(L, &key[0]); - } -}; - -template -struct field_setter::value>> { - template - void set(lua_State* L, Key&& key, Value&& value, int tableindex = -2) { - push(L, std::forward(value)); - lua_setfield(L, tableindex, &key[0]); - } -}; - -#if SOL_LUA_VERSION >= 503 -template -struct field_setter::value>> { - template - void set(lua_State* L, Key&& key, Value&& value, int tableindex = -2) { - push(L, std::forward(value)); - lua_seti(L, tableindex, static_cast(key)); - } -}; -#endif // Lua 5.3.x - namespace stack_detail { template inline int push_as_upvalues(lua_State* L, T& item) { @@ -914,39 +64,16 @@ inline std::pair get_as_upvalues(lua_State* L, int index = 1) { return std::pair(*reinterpret_cast(static_cast(voiddata.data())), index); } -template -struct check_types { - template - static bool check(types, std::index_sequence, lua_State* L, int firstargument, Handler&& handler) { - if (!stack::check(L, firstargument + I0, handler)) - return false; - return check(types(), std::index_sequence(), L, firstargument, std::forward(handler)); - } - - template - static bool check(types<>, std::index_sequence<>, lua_State*, int, Handler&&) { - return true; - } -}; - -template <> -struct check_types { - template - static bool check(types, std::index_sequence, lua_State*, int, Handler&&) { - return true; - } -}; - template ::value>> inline R call(types, types ta, std::index_sequence tai, lua_State* L, int start, Fx&& fx, FxArgs&&... args) { check_types{}.check(ta, tai, L, start, type_panic); - return fx(std::forward(args)..., stack::get(L, start + I)...); + return fx(std::forward(args)..., stack_detail::unchecked_get(L, start + I)...); } template inline void call(types, types ta, std::index_sequence tai, lua_State* L, int start, Fx&& fx, FxArgs&&... args) { check_types{}.check(ta, tai, L, start, type_panic); - fx(std::forward(args)..., stack::get(L, start + I)...); + fx(std::forward(args)..., stack_detail::unchecked_get(L, start + I)...); } } // stack_detail diff --git a/sol/stack_check.hpp b/sol/stack_check.hpp new file mode 100644 index 00000000..8a798310 --- /dev/null +++ b/sol/stack_check.hpp @@ -0,0 +1,288 @@ +// The MIT License (MIT) + +// Copyright (c) 2013-2016 Rapptz, ThePhD and contributors + +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +#ifndef SOL_STACK_CHECK_HPP +#define SOL_STACK_CHECK_HPP + +#include "stack_core.hpp" +#include "usertype_traits.hpp" +#include "inheritance.hpp" +#include +#include +#include + +namespace sol { +namespace stack { +namespace stack_detail { +template +inline bool check_metatable(lua_State* L, int index = -2) { + luaL_getmetatable(L, &usertype_traits::metatable[0]); + const type expectedmetatabletype = static_cast(lua_type(L, -1)); + if (expectedmetatabletype != type::nil) { + if (lua_rawequal(L, -1, index) == 1) { + lua_pop(L, 2); + return true; + } + } + lua_pop(L, 1); + return false; +} + +template +struct basic_check { + template + static bool check (lua_State* L, int index, Handler&& handler) { + bool success = check_func(L, index) == 1; + if (!success) { + // expected type, actual type + handler(L, index, expected, type_of(L, index)); + } + return success; + } +}; + +template +struct check_types { + template + static bool check(types, std::index_sequence, lua_State* L, int firstargument, Handler&& handler) { + if (!stack::check(L, firstargument + I0, handler)) + return false; + return check(types(), std::index_sequence(), L, firstargument, std::forward(handler)); + } + + template + static bool check(types<>, std::index_sequence<>, lua_State*, int, Handler&&) { + return true; + } +}; + +template <> +struct check_types { + template + static bool check(types, std::index_sequence, lua_State*, int, Handler&&) { + return true; + } +}; +} // stack_detail + +template +struct checker { + template + static bool check (lua_State* L, int index, Handler&& handler) { + const type indextype = type_of(L, index); + bool success = expected == indextype; + if (!success) { + // expected type, actual type + handler(L, index, expected, indextype); + } + return success; + } +}; + +template +struct checker { + template + static bool check (lua_State*, int, Handler&&) { + return true; + } +}; + +template +struct checker { + template + static bool check (lua_State* L, int index, Handler&& handler) { + bool success = lua_isnoneornil(L, index); + if (!success) { + // expected type, actual type + handler(L, index, expected, type_of(L, index)); + } + return success; + } +}; + +template +struct checker { + template + static bool check (lua_State* L, int index, Handler&& handler) { + bool success = !lua_isnone(L, index); + if (!success) { + // expected type, actual type + handler(L, index, type::none, type_of(L, index)); + } + return success; + } +}; + +template +struct checker { + template + static bool check (lua_State* L, int index, Handler&& handler) { + type t = type_of(L, index); + bool success = t == type::userdata || t == type::lightuserdata; + if (!success) { + // expected type, actual type + handler(L, index, type::lightuserdata, t); + } + return success; + } +}; + +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 { + template + static bool check (lua_State* L, int index, Handler&& handler) { + const type indextype = type_of(L, index); + // Allow nil to be transformed to nullptr + if (indextype == type::nil) { + return true; + } + return checker{}.check(types(), L, indextype, index, std::forward(handler)); + } +}; + +template +struct checker { + template + static bool check (types, lua_State* L, type indextype, int index, Handler&& handler) { + if (indextype != type::userdata) { + handler(L, index, type::userdata, indextype); + return false; + } + if (meta::Or, std::is_same>::value) + return true; + if (lua_getmetatable(L, index) == 0) { + handler(L, index, type::userdata, indextype); + return false; + } + if (stack_detail::check_metatable(L)) + return true; + if (stack_detail::check_metatable(L)) + return true; + if (stack_detail::check_metatable>(L)) + return true; +#ifndef SOL_NO_EXCEPTIONS + lua_getfield(L, -1, &detail::base_class_check_key()[0]); + void* basecastdata = lua_touserdata(L, -1); + detail::throw_cast basecast = (detail::throw_cast)basecastdata; + bool success = detail::catch_check(basecast); +#elif !defined(SOL_NO_RTTI) + lua_getfield(L, -1, &detail::base_class_check_key()[0]); + if (stack::get(L) == type::nil) { + lua_pop(L, 2); + return false; + } + void* basecastdata = lua_touserdata(L, -1); + detail::inheritance_check_function ic = (detail::inheritance_check_function)basecastdata; + bool success = ic(typeid(T)); +#else + // Topkek + lua_getfield(L, -1, &detail::base_class_check_key()[0]); + if (stack::get(L) == type::nil) { + lua_pop(L, 2); + return false; + } + void* basecastdata = lua_touserdata(L, -1); + detail::inheritance_check_function ic = (detail::inheritance_check_function)basecastdata; + bool 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; + } + return true; + } + + template + static bool check (lua_State* L, int index, Handler&& handler) { + const type indextype = type_of(L, index); + return check(types(), L, indextype, index, std::forward(handler)); + } +}; + +template +struct checker, type::userdata, C> { + template + static bool check(lua_State* L, int index, Handler&& handler) { + return checker{}.check(L, index, std::forward(handler)); + } +}; + +template +struct checker, type::userdata, C> { + template + static bool check(lua_State* L, int index, Handler&& handler) { + return checker>, type::userdata, C>{}.check(L, index, std::forward(handler)); + } +}; + +template +struct checker, type::userdata, C> { + template + static bool check(lua_State* L, int index, Handler&& handler) { + return checker>, type::userdata, C>{}.check(L, index, std::forward(handler)); + } +}; + +template +struct checker, type::userdata, C> { + template + static bool check(lua_State* L, int index, Handler&& handler) { + return checker{}.check(L, index, std::forward(handler)); + } +}; + +template +struct checker, type::poly, C> { + template + static bool apply(std::index_sequence is, lua_State* L, int index, Handler&& handler) { + index = index < 0 ? lua_absindex(L, index) - ( sizeof...(I) - 1 ) : index; + return stack_detail::check_types{}.check(types(), is, L, index, handler); + } + + template + static bool check(lua_State* L, int index, Handler&& handler) { + return apply(std::index_sequence_for(), L, index, std::forward(handler)); + } +}; + +template +struct checker, type::poly, C> { + template + static bool check(lua_State* L, int index, Handler&& handler) { + index = index < 0 ? lua_absindex(L, index) - 1 : index; + return stack::check(L, index, handler) && stack::check(L, index + 1, handler); + } +}; +} // stack +} // sol + +#endif // SOL_STACK_CHECK_HPP diff --git a/sol/stack_check_get.hpp b/sol/stack_check_get.hpp new file mode 100644 index 00000000..efc93305 --- /dev/null +++ b/sol/stack_check_get.hpp @@ -0,0 +1,75 @@ +// The MIT License (MIT) + +// Copyright (c) 2013-2016 Rapptz, ThePhD and contributors + +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +#ifndef SOL_STACK_CHECK_GET_HPP +#define SOL_STACK_CHECK_GET_HPP + +#include "stack_core.hpp" +#include "stack_get.hpp" +#include "stack_check.hpp" +#include "optional.hpp" + +namespace sol { +namespace stack { +template +struct check_getter { + typedef stack_detail::strip_t U; + typedef std::conditional_t::value, U, U&> R; + + template + static optional get( lua_State* L, int index, Handler&& handler) { + if (!check(L, index, std::forward(handler))) + return nullopt; + return stack_detail::unchecked_get(L, index); + } +}; + +template +struct check_getter::value && !std::is_same::value>> { + template + static optional get( lua_State* L, int index, Handler&& handler) { + int isnum = 0; + lua_Integer value = lua_tointegerx(L, index, &isnum); + if (isnum == 0) { + handler(L, index, type::number, type_of(L, index)); + return nullopt; + } + return static_cast(value); + } +}; + +template +struct check_getter::value>> { + template + static optional get( lua_State* L, int index, Handler&& handler) { + int isnum = 0; + lua_Number value = lua_tonumberx(L, index, &isnum); + if (isnum == 0) { + handler(L, index, type::number, type_of(L, index)); + return nullopt; + } + return static_cast(value); + } +}; +} // stack +} // sol + +#endif // SOL_STACK_CHECK_GET_HPP diff --git a/sol/stack_core.hpp b/sol/stack_core.hpp new file mode 100644 index 00000000..458fd559 --- /dev/null +++ b/sol/stack_core.hpp @@ -0,0 +1,183 @@ +// The MIT License (MIT) + +// Copyright (c) 2013-2016 Rapptz, ThePhD and contributors + +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +#ifndef SOL_STACK_CORE_HPP +#define SOL_STACK_CORE_HPP + +#include "types.hpp" +#include "reference.hpp" +#include "userdata.hpp" +#include "tuple.hpp" +#include "traits.hpp" + +namespace sol { +namespace detail { +using special_destruct_func = void(*)(void*); + +template +inline void special_destruct(void* memory) { + T** pointerpointer = static_cast(memory); + special_destruct_func* dx = static_cast(static_cast(pointerpointer + 1)); + Real* target = static_cast(static_cast(dx + 1)); + target->~Real(); +} + +template +inline int unique_destruct(lua_State* L) { + void* memory = lua_touserdata(L, 1); + T** pointerpointer = static_cast(memory); + special_destruct_func& dx = *static_cast( static_cast( pointerpointer + 1 ) ); + (dx)(memory); + return 0; +} +} // detail +namespace stack { + +template +struct field_getter; +template +struct field_setter; +template +struct getter; +template +struct popper; +template +struct pusher; +template::value, typename = void> +struct checker; +template +struct check_getter; + +namespace stack_detail { +template +struct strip { + typedef T type; +}; +template +struct strip> { + typedef T& type; +}; +template +struct strip> { + typedef T type; +}; +template +using strip_t = typename strip::type; +const bool default_check_arguments = +#ifdef SOL_CHECK_ARGUMENTS +true; +#else +false; +#endif +template +inline decltype(auto) unchecked_get(lua_State* L, int index = -1) { + return getter>{}.get(L, index); +} +} // stack_detail + +template +inline int push(lua_State* L, T&& t, Args&&... args) { + return pusher>{}.push(L, std::forward(t), std::forward(args)...); +} + +// overload allows to use a pusher of a specific type, but pass in any kind of args +template +inline int push(lua_State* L, Arg&& arg, Args&&... args) { + return pusher>{}.push(L, std::forward(arg), std::forward(args)...); +} + +inline int multi_push(lua_State*) { + // do nothing + return 0; +} + +template +inline int multi_push(lua_State* L, T&& t, Args&&... args) { + int pushcount = push(L, std::forward(t)); + void(sol::detail::swallow{(pushcount += sol::stack::push(L, std::forward(args)), 0)... }); + return pushcount; +} + +template +bool check(lua_State* L, int index, Handler&& handler) { + typedef meta::Unqualified Tu; + checker c; + // VC++ has a bad warning here: shut it up + (void)c; + return c.check(L, index, std::forward(handler)); +} + +template +bool check(lua_State* L, int index = -1) { + auto handler = no_panic; + return check(L, index, handler); +} + +template +inline decltype(auto) check_get(lua_State* L, int index, Handler&& handler) { + return check_getter>{}.get(L, index, std::forward(handler)); +} + +template +inline decltype(auto) check_get(lua_State* L, int index = -1) { + auto handler = no_panic; + return check_get(L, index, handler); +} + +template +inline decltype(auto) get(lua_State* L, int index = -1) { +#ifdef SOL_CHECK_ARGUMENTS + auto op = check_get(L, index, type_panic); + typedef typename meta::Unqualified::value_type U; + return static_cast(*op); +#else + return stack_detail::unchecked_get(L, index); +#endif +} + +template +inline decltype(auto) pop(lua_State* L) { + return popper>{}.pop(L); +} + +template +void get_field(lua_State* L, Key&& key) { + field_getter, global>{}.get(L, std::forward(key)); +} + +template +void get_field(lua_State* L, Key&& key, int tableindex) { + field_getter, global>{}.get(L, std::forward(key), tableindex); +} + +template +void set_field(lua_State* L, Key&& key, Value&& value) { + field_setter, global>{}.set(L, std::forward(key), std::forward(value)); +} + +template +void set_field(lua_State* L, Key&& key, Value&& value, int tableindex) { + field_setter, global>{}.set(L, std::forward(key), std::forward(value), tableindex); +} +} // stack +} // sol + +#endif // SOL_STACK_CORE_HPP diff --git a/sol/stack_field.hpp b/sol/stack_field.hpp new file mode 100644 index 00000000..df5117a4 --- /dev/null +++ b/sol/stack_field.hpp @@ -0,0 +1,138 @@ +// The MIT License (MIT) + +// Copyright (c) 2013-2016 Rapptz, ThePhD and contributors + +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +#ifndef SOL_STACK_FIELD_HPP +#define SOL_STACK_FIELD_HPP + +#include "stack_core.hpp" +#include "stack_push.hpp" +#include "stack_get.hpp" +#include "stack_check_get.hpp" + +namespace sol { +namespace stack { +template +struct field_getter { + template + void get(lua_State* L, Key&& key, int tableindex = -2) { + push( L, std::forward( key ) ); + lua_gettable( L, tableindex ); + } +}; + +template +struct field_getter, b, C> { + template + void apply(std::index_sequence, lua_State* L, Keys&& keys, int tableindex) { + tableindex = lua_absindex(L, tableindex); + void(detail::swallow{ (get_field(L, detail::forward_get(keys), tableindex), 0)... }); + reference saved(L, -1); + lua_pop(L, static_cast(sizeof...(I))); + saved.push(); + } + + template + void get(lua_State* L, Keys&& keys, int tableindex = -2) { + apply(std::index_sequence_for(), L, std::forward(keys), tableindex); + } +}; + +template +struct field_getter, b, C> { + template + void get(lua_State* L, Keys&& keys, int tableindex = -2) { + tableindex = lua_absindex(L, tableindex); + get_field(L, detail::forward_get<0>(keys), tableindex); + get_field(L, detail::forward_get<1>(keys), tableindex + 1); + reference saved(L, -1); + lua_pop(L, static_cast(2)); + saved.push(); + } +}; + +template +struct field_getter::value>> { + template + void get(lua_State* L, Key&& key, int = -1) { + lua_getglobal(L, &key[0]); + } +}; + +template +struct field_getter::value>> { + template + void get(lua_State* L, Key&& key, int tableindex = -1) { + lua_getfield(L, tableindex, &key[0]); + } +}; + +#if SOL_LUA_VERSION >= 503 +template +struct field_getter::value>> { + template + void get(lua_State* L, Key&& key, int tableindex = -1) { + lua_geti(L, tableindex, static_cast(key)); + } +}; +#endif // Lua 5.3.x + +template +struct field_setter { + template + void set(lua_State* L, Key&& key, Value&& value, int tableindex = -3) { + push(L, std::forward(key)); + push(L, std::forward(value)); + lua_settable(L, tableindex); + } +}; + +template +struct field_setter::value>> { + template + void set(lua_State* L, Key&& key, Value&& value, int = -2) { + push(L, std::forward(value)); + lua_setglobal(L, &key[0]); + } +}; + +template +struct field_setter::value>> { + template + void set(lua_State* L, Key&& key, Value&& value, int tableindex = -2) { + push(L, std::forward(value)); + lua_setfield(L, tableindex, &key[0]); + } +}; + +#if SOL_LUA_VERSION >= 503 +template +struct field_setter::value>> { + template + void set(lua_State* L, Key&& key, Value&& value, int tableindex = -2) { + push(L, std::forward(value)); + lua_seti(L, tableindex, static_cast(key)); + } +}; +#endif // Lua 5.3.x +} // stack +} // sol + +#endif // SOL_STACK_FIELD_HPP \ No newline at end of file diff --git a/sol/stack_get.hpp b/sol/stack_get.hpp new file mode 100644 index 00000000..f97dd76d --- /dev/null +++ b/sol/stack_get.hpp @@ -0,0 +1,260 @@ +// The MIT License (MIT) + +// Copyright (c) 2013-2016 Rapptz, ThePhD and contributors + +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +#ifndef SOL_STACK_GET_HPP +#define SOL_STACK_GET_HPP + +#include "stack_core.hpp" +#include "usertype_traits.hpp" +#include "inheritance.hpp" +#include "overload.hpp" +#include "error.hpp" +#include +#include +#include + +namespace sol { +namespace stack { + +template +struct getter { + static T& get(lua_State* L, int index = -1) { + return getter{}.get(L, index); + } +}; + +template +struct getter::value>> { + static T get(lua_State* L, int index = -1) { + return static_cast(lua_tonumber(L, index)); + } +}; + +template +struct getter, std::is_signed>::value>> { + static T get(lua_State* L, int index = -1) { + return static_cast(lua_tointeger(L, index)); + } +}; + +template +struct getter, std::is_unsigned>::value>> { + static T get(lua_State* L, int index = -1) { + return static_cast(lua_tointeger(L, index)); + } +}; + +template +struct getter::value>> { + static T get(lua_State* L, int index = -1) { + return T(L, index); + } +}; + +template<> +struct getter { + static userdata_value get(lua_State* L, int index = -1) { + return userdata_value( lua_touserdata(L, index) ); + } +}; + +template<> +struct getter { + static light_userdata_value get(lua_State* L, int index = -1) { + return light_userdata_value( lua_touserdata(L, index) ); + } +}; + +template<> +struct getter { + static type get(lua_State *L, int index){ + return static_cast(lua_type(L, index)); + } +}; + +template<> +struct getter { + static bool get(lua_State* L, int index) { + return lua_toboolean(L, index) != 0; + } +}; + +template<> +struct getter { + static std::string get(lua_State* L, int index = -1) { + std::size_t len; + auto str = lua_tolstring(L, index, &len); + return { str, len }; + } +}; + +template<> +struct getter { + static const char* get(lua_State* L, int index = -1) { + return lua_tostring(L, index); + } +}; + +template<> +struct getter { + static nil_t get(lua_State*, int = -1) { + return nil_t{ }; + } +}; + +template<> +struct getter { + static lua_CFunction get(lua_State* L, int index = -1) { + return lua_tocfunction(L, index); + } +}; + +template<> +struct getter { + static c_closure get(lua_State* L, int index = -1) { + return c_closure(lua_tocfunction(L, index), -1); + } +}; + +template<> +struct getter { + static void* get(lua_State* L, int index = -1) { + return lua_touserdata(L, index); + } +}; + +template +struct getter { + static T* get_no_nil(lua_State* L, int index = -1) { + void** pudata = static_cast(lua_touserdata(L, index)); + void* udata = *pudata; + return get_no_nil_from(L, udata, index); + } + + static T* get_no_nil_from(lua_State* L, void* udata, int index = -1) { +#ifndef SOL_NO_EXCEPTIONS + if (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 (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 (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, detail::id_for::value); + lua_pop(L, 1); + } +#endif // No Runtime Type Information || Exceptions + T* obj = static_cast(udata); + return obj; + } + + static T* get(lua_State* L, int index = -1) { + type t = type_of(L, index); + if (t == type::nil) + return nullptr; + return get_no_nil(L, index); + } +}; + +template +struct getter> { + static Real& get(lua_State* L, int index = -1) { + T** pref = static_cast(lua_touserdata(L, index)); + detail::special_destruct_func* fx = static_cast(static_cast(pref + 1)); + Real* mem = static_cast(static_cast(fx + 1)); + return *mem; + } +}; + +template +struct getter> { + static T* get(lua_State* L, int index = -1) { + return getter::get_no_nil(L, index); + } +}; + +template +struct getter { + static T& get(lua_State* L, int index = -1) { + return *getter::get_no_nil(L, index); + } +}; + +template +struct getter> { + static std::shared_ptr& get(lua_State* L, int index = -1) { + return getter>>::get(L, index); + } +}; + +template +struct getter> { + static std::unique_ptr& get(lua_State* L, int index = -1) { + return getter>>::get(L, index); + } +}; + +template +struct getter> { + static T& get(lua_State* L, int index = -1) { + return getter{}.get(L, index); + } +}; + +template +struct getter> { + template + static decltype(auto) apply(std::index_sequence, lua_State* L, int index = -1) { + index = index < 0 ? lua_absindex(L, index) - ( sizeof...(I) - 1 ) : index; + return std::tuple(L, index + I))...>(stack::get(L, index + I)...); + } + + static decltype(auto) get(lua_State* L, int index = -1) { + return apply(std::index_sequence_for(), L, index); + } +}; + +template +struct getter> { + static decltype(auto) get(lua_State* L, int index = -1) { + index = index < 0 ? lua_absindex(L, index) - 1 : index; + return std::pair(L, index)), decltype(stack::get(L, index))>(stack::get(L, index), stack::get(L, index + 1)); + } +}; + +} // stack +} // sol + +#endif // SOL_STACK_GET_HPP diff --git a/sol/stack_pop.hpp b/sol/stack_pop.hpp new file mode 100644 index 00000000..9f22d379 --- /dev/null +++ b/sol/stack_pop.hpp @@ -0,0 +1,61 @@ +// The MIT License (MIT) + +// Copyright (c) 2013-2016 Rapptz, ThePhD and contributors + +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +#ifndef SOL_STACK_POP_HPP +#define SOL_STACK_POP_HPP + +#include "stack_core.hpp" +#include "stack_get.hpp" +#include +#include + +namespace sol { +namespace stack { +template +struct popper { + inline static decltype(auto) pop(lua_State* L) { + decltype(auto) r = get(L); + lua_pop(L, 1); + return r; + } +}; + +template +struct popper> { + inline static decltype(auto) pop(lua_State* L) { + decltype(auto) r = get>(L); + lua_pop(L, static_cast(sizeof...(Args))); + return r; + } +}; + +template +struct popper> { + inline static decltype(auto) pop(lua_State* L) { + decltype(auto) r = get>(L); + lua_pop(L, 2); + return r; + } +}; +} // stack +} // sol + +#endif // SOL_STACK_POP_HPP diff --git a/sol/stack_push.hpp b/sol/stack_push.hpp new file mode 100644 index 00000000..111ba73a --- /dev/null +++ b/sol/stack_push.hpp @@ -0,0 +1,297 @@ +// The MIT License (MIT) + +// Copyright (c) 2013-2016 Rapptz, ThePhD and contributors + +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +#ifndef SOL_STACK_PUSH_HPP +#define SOL_STACK_PUSH_HPP + +#include "stack_core.hpp" +#include "raii.hpp" +#include + +namespace sol { +namespace stack { +template +struct pusher { + template + static int push(lua_State* L, Args&&... args) { + // Basically, we store all user-data like this: + // If it's a movable/copyable value (no std::ref(x)), then we store the pointer to the new + // data in the first sizeof(T*) bytes, and then however many bytes it takes to + // do the actual object. Things that are std::ref or plain T* are stored as + // just the sizeof(T*), and nothing else. + T** pointerpointer = static_cast(lua_newuserdata(L, sizeof(T*) + sizeof(T))); + T*& referencereference = *pointerpointer; + T* allocationtarget = reinterpret_cast(pointerpointer + 1); + referencereference = allocationtarget; + std::allocator alloc{}; + alloc.construct(allocationtarget, std::forward(args)...); + luaL_getmetatable(L, &usertype_traits::metatable[0]); + lua_setmetatable(L, -2); + return 1; + } +}; + +template +struct pusher { + static int push(lua_State* L, T* obj) { + if (obj == nullptr) + return stack::push(L, nil); + T** pref = static_cast(lua_newuserdata(L, sizeof(T*))); + *pref = obj; + luaL_getmetatable(L, &usertype_traits::metatable[0]); + lua_setmetatable(L, -2); + return 1; + } +}; + +template +struct pusher> { + template + static int push(lua_State* L, Args&&... args) { + T** pref = static_cast(lua_newuserdata(L, sizeof(T*) + sizeof(detail::special_destruct_func) + sizeof(Real))); + detail::special_destruct_func* fx = static_cast(static_cast(pref + 1)); + Real* mem = static_cast(static_cast(fx + 1)); + *fx = detail::special_destruct; + detail::default_construct::construct(mem, std::forward(args)...); + *pref = std::addressof(detail::deref(*mem)); + if (luaL_newmetatable(L, &usertype_traits>::metatable[0]) == 1) { + set_field(L, "__gc", detail::unique_destruct); + } + lua_setmetatable(L, -2); + return 1; + } +}; + +template +struct pusher::value>> { + template + static int push(lua_State* L, Args&&... args) { + typedef typename is_unique_usertype::metatable_type meta_type; + return stack::push>(L, std::forward(args)...); + } +}; + +template +struct pusher> { + static int push(lua_State* L, std::unique_ptr obj) { + if (obj == nullptr) + return stack::push(L, nil); + return stack::push>>(L, std::move(obj)); + } +}; + +template +struct pusher> { + template + static int push(lua_State* L, S&& s) { + if (s == nullptr) + return stack::push(L, nil); + return stack::push>>(L, std::forward(s)); + } +}; + + +template +struct pusher> { + static int push(lua_State* L, const std::reference_wrapper& t) { + return stack::push(L, std::addressof(detail::deref(t.get()))); + } +}; + +template +struct pusher::value>> { + static int push(lua_State* L, const T& value) { + lua_pushnumber(L, value); + return 1; + } +}; + +template +struct pusher, std::is_signed>::value>> { + static int push(lua_State* L, const T& value) { + lua_pushinteger(L, static_cast(value)); + return 1; + } +}; + +template +struct pusher, std::is_unsigned>::value>> { + static int push(lua_State* L, const T& value) { + typedef std::make_signed_t signed_int; + return stack::push(L, static_cast(value)); + } +}; + +template +struct pusher, meta::Not>, meta::Not>>::value>> { + static int push(lua_State* L, const T& cont) { + lua_createtable(L, static_cast(cont.size()), 0); + int tableindex = lua_gettop(L); + unsigned index = 1; + for(auto&& i : cont) { + set_field(L, index++, i, tableindex); + } + return 1; + } +}; + +template +struct pusher, meta::has_key_value_pair, meta::Not>>::value>> { + static int push(lua_State* L, const T& cont) { + lua_createtable(L, static_cast(cont.size()), 0); + int tableindex = lua_gettop(L); + for(auto&& pair : cont) { + set_field(L, pair.first, pair.second, tableindex); + } + return 1; + } +}; + +template +struct pusher::value>> { + static int push(lua_State*, T& ref) { + return ref.push(); + } + + static int push(lua_State*, T&& ref) { + return ref.push(); + } +}; + +template<> +struct pusher { + static int push(lua_State* L, bool b) { + lua_pushboolean(L, b); + return 1; + } +}; + +template<> +struct pusher { + static int push(lua_State* L, nil_t) { + lua_pushnil(L); + return 1; + } +}; + +template<> +struct pusher> { + static int push(lua_State* L, lua_CFunction func, int n = 0) { + lua_pushcclosure(L, func, n); + return 1; + } +}; + +template<> +struct pusher { + static int push(lua_State* L, lua_CFunction func, int n = 0) { + lua_pushcclosure(L, func, n); + return 1; + } +}; + +template<> +struct pusher { + static int push(lua_State* L, c_closure closure) { + lua_pushcclosure(L, closure.c_function, closure.upvalues); + return 1; + } +}; + +template<> +struct pusher { + static int push(lua_State* L, void* userdata) { + lua_pushlightuserdata(L, userdata); + return 1; + } +}; + +template<> +struct pusher { + static int push(lua_State* L, light_userdata_value userdata) { + lua_pushlightuserdata(L, userdata); + return 1; + } +}; + +template<> +struct pusher { + static int push(lua_State* L, userdata_value data) { + void** ud = static_cast(lua_newuserdata(L, sizeof(void*))); + *ud = data.value; + return 1; + } +}; + +template<> +struct pusher { + static int push(lua_State* L, const char* str) { + lua_pushlstring(L, str, std::char_traits::length(str)); + return 1; + } +}; + +template +struct pusher { + static int push(lua_State* L, const char (&str)[N]) { + lua_pushlstring(L, str, N - 1); + return 1; + } +}; + +template<> +struct pusher { + static int push(lua_State* L, const std::string& str) { + lua_pushlstring(L, str.c_str(), str.size()); + return 1; + } +}; + +template +struct pusher> { + template + static int push(std::index_sequence, lua_State* L, T&& t) { + int pushcount = 0; + (void)detail::swallow{ 0, (pushcount += stack::push(L, + detail::forward_get(t) + ), 0)... }; + return pushcount; + } + + template + static int push(lua_State* L, T&& t) { + return push(std::index_sequence_for(), L, std::forward(t)); + } +}; + +template +struct pusher> { + template + static int push(lua_State* L, T&& t) { + int pushcount = stack::push(L, detail::forward_get<0>(t)); + pushcount += stack::push(L, detail::forward_get<1>(t)); + return pushcount; + } +}; +} // stack +} // sol + +#endif // SOL_STACK_PUSH_HPP diff --git a/sol/state_view.hpp b/sol/state_view.hpp index 1a00cafc..52eee076 100644 --- a/sol/state_view.hpp +++ b/sol/state_view.hpp @@ -255,6 +255,18 @@ public: return *this; } + template + state_view& set_function(Key&& key, R (C::*mem_ptr)(Args...)) { + global.set_function(std::forward(key), mem_ptr); + return *this; + } + + template + state_view& set_function(Key&& key, Sig C::* mem_ptr) { + global.set_function(std::forward(key), mem_ptr); + return *this; + } + template state_view& set_function(Key&& key, R (C::*mem_ptr)(Args...), T&& obj) { global.set_function(std::forward(key), mem_ptr, std::forward(obj)); @@ -292,6 +304,11 @@ public: return create_table(lua_state(), narr, nrec, std::forward(key), std::forward(value), std::forward(args)...); } + template + table create_table_with(Args&&... args) { + return create_table_with(lua_state(), std::forward(args)...); + } + static inline table create_table(lua_State* L, int narr = 0, int nrec = 0) { return global_table::create(L, narr, nrec); } @@ -300,6 +317,11 @@ public: static inline table create_table(lua_State* L, int narr, int nrec, Key&& key, Value&& value, Args&&... args) { return global_table::create(L, narr, nrec, std::forward(key), std::forward(value), std::forward(args)...); } + + template + static inline table create_table_with(lua_State* L, Args&&... args) { + return global_table::create_with(L, std::forward(args)...); + } }; } // sol diff --git a/sol/table_core.hpp b/sol/table_core.hpp index d2974075..9b830d4d 100644 --- a/sol/table_core.hpp +++ b/sol/table_core.hpp @@ -62,13 +62,13 @@ class table_core : public reference { } } - template - auto tuple_get( types, std::index_sequence, Keys&& keys ) const - -> decltype(stack::pop>(nullptr)){ + template + auto tuple_get( types, std::index_sequence, Keys&& keys ) const + -> decltype(stack::pop>(nullptr)){ auto pp = stack::push_pop...>::value>(*this); int tableindex = lua_gettop(lua_state()); void(detail::swallow{ ( stack::get_field(lua_state(), detail::forward_get(keys), tableindex), 0)... }); - return stack::pop>( lua_state() ); + return stack::pop>( lua_state() ); } template @@ -241,6 +241,18 @@ public: return *this; } + template + table_core& set_function( Key&& key, R( C::*mem_ptr )( Args... ) ) { + set_resolved_function( std::forward( key ), mem_ptr ); + return *this; + } + + template + table_core& set_function( Key&& key, Sig C::* mem_ptr ) { + set_resolved_function( std::forward( key ), mem_ptr ); + return *this; + } + template table_core& set_function( Key&& key, Fx&& fx ) { set_fx( types( ), std::forward( key ), std::forward( fx ) ); @@ -271,6 +283,37 @@ private: } public: + static inline table create(lua_State* L, int narr = 0, int nrec = 0) { + lua_createtable(L, narr, nrec); + table result(L); + lua_pop(L, 1); + return result; + } + + template + static inline table create(lua_State* L, int narr, int nrec, Key&& key, Value&& value, Args&&... args) { + lua_createtable(L, narr, nrec); + table result(L); + result.set(std::forward(key), std::forward(value), std::forward(args)...); + lua_pop(L, 1); + return result; + } + + template + static inline table create_with(lua_State* L, Args&&... args) { + static const int narr = static_cast(meta::count_if_2_pack::value); + return create(L, narr, static_cast((sizeof...(Args) / 2) - narr), std::forward(args)...); + } + + table create(int narr = 0, int nrec = 0) { + return create(lua_state(), narr, nrec); + } + + template + table create(int narr, int nrec, Key&& key, Value&& value, Args&&... args) { + return create(lua_state(), narr, nrec, std::forward(key), std::forward(value), std::forward(args)...); + } + template table create(Name&& name, int narr = 0, int nrec = 0) { table x = create(lua_state(), narr, nrec); @@ -285,32 +328,10 @@ public: return x; } - table create(int narr = 0, int nrec = 0) { - return create(lua_state(), narr, nrec); - } - - template - table create(int narr, int nrec, Key&& key, Value&& value, Args&&... args) { - return create(lua_state(), narr, nrec, std::forward(key), std::forward(value), std::forward(args)...); - } - - static inline table create(lua_State* L, int narr = 0, int nrec = 0) { - lua_createtable(L, narr, nrec); - table result(L); - lua_pop(L, 1); - return result; - } - - template - static inline table create(lua_State* L, int narr, int nrec, Key&& key, Value&& value, Args&&... args) { - if (narr == 0) narr = static_cast(meta::count_if_pack::value); - if (nrec == 0) nrec = static_cast(( sizeof...(Args) + 2 ) - narr); - lua_createtable(L, narr, nrec); - table result(L); - result.set(std::forward(key), std::forward(value), std::forward(args)...); - lua_pop(L, 1); - return result; - } + template + table create_with(Args&&... args) { + return create_with(lua_state(), std::forward(args)...); + } }; } // sol diff --git a/sol/traits.hpp b/sol/traits.hpp index bf40c17d..e96a4c1b 100644 --- a/sol/traits.hpp +++ b/sol/traits.hpp @@ -142,12 +142,25 @@ namespace meta_detail { template class Pred, typename... Ts> struct count_if_pack {}; template class Pred, typename T, typename... Ts> - struct count_if_pack : std::conditional_t, count_if_pack(Pred::value), Pred, Ts...>> { }; + struct count_if_pack : std::conditional_t(Pred::value)>, + count_if_pack(Pred::value), Pred, Ts...> + > { }; + template class Pred, typename... Ts> + struct count_if_2_pack {}; + template class Pred, typename T, typename U, typename... Ts> + struct count_if_2_pack : std::conditional_t(Pred::value)>, + count_if_2_pack(Pred::value), Pred, Ts...> + > { }; } // meta_detail template class Pred, typename... Ts> struct count_if_pack : meta_detail::count_if_pack<0, Pred, Ts...> { }; +template class Pred, typename... Ts> +struct count_if_2_pack : meta_detail::count_if_2_pack<0, Pred, Ts...> { }; + template struct return_type { typedef std::tuple type; diff --git a/sol/types.hpp b/sol/types.hpp index 52b45352..271fdf34 100644 --- a/sol/types.hpp +++ b/sol/types.hpp @@ -34,9 +34,9 @@ inline int static_trampoline (lua_State* L) { return f(L); } -template -inline int trampoline(lua_State* L, Fx&& f) { - return f(L); +template +inline int trampoline(lua_State* L, Fx&& f, Args&&... args) { + return f(L, std::forward(args)...); } inline int c_trampoline(lua_State* L, lua_CFunction f) { @@ -48,7 +48,7 @@ inline int static_trampoline (lua_State* L) { try { return f(L); } - catch (const char *s) { // Catch and convert exceptions. + catch (const char *s) { lua_pushstring(L, s); } catch (const std::exception& e) { @@ -60,12 +60,12 @@ inline int static_trampoline (lua_State* L) { return lua_error(L); } -template -inline int trampoline(lua_State* L, Fx&& f) { +template +inline int trampoline(lua_State* L, Fx&& f, Args&&... args) { try { - return f(L); + return f(L, std::forward(args)...); } - catch (const char *s) { // Catch and convert exceptions. + catch (const char *s) { lua_pushstring(L, s); } catch (const std::exception& e) { @@ -232,9 +232,18 @@ 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 {}; @@ -275,7 +284,18 @@ template struct lua_type_of::value>> : std::integral_constant {}; template -struct is_lua_primitive : std::integral_constant>::value || std::is_base_of>::value> { }; +struct is_lua_primitive : std::integral_constant>::value + || std::is_base_of>::value> { }; + +template +struct is_lua_primitive : std::true_type {}; +template <> +struct is_lua_primitive : std::true_type {}; +template <> +struct is_lua_primitive : std::true_type {}; +template +struct is_lua_primitive> : is_lua_primitive {}; template struct is_proxy_primitive : is_lua_primitive { }; diff --git a/sol/usertype.hpp b/sol/usertype.hpp index 5e60ffab..d3404f80 100644 --- a/sol/usertype.hpp +++ b/sol/usertype.hpp @@ -120,6 +120,21 @@ enum class stage { uniquemeta, }; +template +inline int push_upvalues(lua_State* L, TCont&& cont) { + int n = 0; + for(auto& c : cont) { + if(releasemem) { + stack::push(L, c.release()); + } + else { + stack::push(L, c.get()); + } + ++n; + } + return n; +} + template inline void push_metatable(lua_State* L, bool needsindexfunction, std::vector>& funcs, std::vector& functable, std::vector& metafunctable, void* baseclasscheck, void* baseclasscast) { static const auto& gcname = meta_function_names[static_cast(meta_function::garbage_collect)]; @@ -137,7 +152,7 @@ inline void push_metatable(lua_State* L, bool needsindexfunction, std::vector(functable.size())); - int up = stack::stack_detail::push_upvalues(L, funcs); + int up = push_upvalues(L, funcs); functable.push_back({nullptr, nullptr}); luaL_setfuncs(L, functable.data(), up); functable.pop_back(); @@ -192,7 +207,7 @@ inline void set_global_deleter(lua_State* L, lua_CFunction cleanup, Functions&& // even if the user calls collectgarbage(), weirdly enough lua_createtable(L, 0, 0); // global table that sits at toplevel lua_createtable(L, 0, 1); // metatable for the global table - int up = stack::stack_detail::push_upvalues(L, functions); + int up = push_upvalues(L, functions); stack::set_field(L, "__gc", c_closure(cleanup, up)); lua_setmetatable(L, -2); // gctable name by default has ♻ part of it diff --git a/tests.cpp b/tests.cpp index 64942eaa..4635f6fd 100644 --- a/tests.cpp +++ b/tests.cpp @@ -372,13 +372,13 @@ TEST_CASE("simple/call-c++-function", "C++ function is called from lua") { TEST_CASE("simple/call-lambda", "A C++ lambda is exposed to lua and called") { sol::state lua; - int x = 0; + int a = 0; - lua.set_function("foo", [&x] { x = 1; }); + lua.set_function("foo", [&a] { a = 1; }); lua.script("foo()"); - REQUIRE(x == 1); + REQUIRE(a == 1); } TEST_CASE("advanced/get-and-call", "Checks for lambdas returning values after a get operation") { @@ -537,6 +537,17 @@ TEST_CASE("tables/create-local-named", "Check if creating a table is kosher") { REQUIRE((testtable[3] == 4)); } +TEST_CASE("tables/create-with-local", "Check if creating a table is kosher") { + sol::state lua; + lua["testtable"] = lua.create_table_with("Woof", "Bark", 1, 2, 3, 4); + sol::object testobj = lua["testtable"]; + REQUIRE(testobj.is()); + sol::table testtable = testobj.as(); + REQUIRE((testtable["Woof"] == std::string("Bark"))); + REQUIRE((testtable[1] == 2)); + REQUIRE((testtable[3] == 4)); +} + TEST_CASE("tables/functions-variables", "Check if tables and function calls work as intended") { sol::state lua; lua.open_libraries(sol::lib::base, sol::lib::os);