diff --git a/docs/source/api/api-top.rst b/docs/source/api/api-top.rst index 22d80dfc..9c3e7cd7 100644 --- a/docs/source/api/api-top.rst +++ b/docs/source/api/api-top.rst @@ -35,7 +35,9 @@ Browse the various function and classes :doc:`Sol<../index>` utilizes to make yo thread optional variadic_args + variadic_results as_args + as_returns overload property var diff --git a/docs/source/api/as_args.rst b/docs/source/api/as_args.rst index 8e323fdf..fdb12f20 100644 --- a/docs/source/api/as_args.rst +++ b/docs/source/api/as_args.rst @@ -6,7 +6,7 @@ turn an iterable argument into multiple arguments .. code-block:: cpp template - as_args_t { ... }; + struct as_args_t { ... }; template as_args_t as_args( T&& ); @@ -15,6 +15,7 @@ turn an iterable argument into multiple arguments ``sol::as_args`` is a function that that takes an iterable and turns it into multiple arguments to a function call. It forwards its arguments, and is meant to be used as shown below: .. code-block:: cpp + :linenos: :caption: as_args.c++ #define SOL_CHECK_ARGUMENTS diff --git a/docs/source/api/as_returns.rst b/docs/source/api/as_returns.rst new file mode 100644 index 00000000..a64ea156 --- /dev/null +++ b/docs/source/api/as_returns.rst @@ -0,0 +1,50 @@ +as_returns +========== +turn an iterable argument into a multiple-return type +----------------------------------------------------- + +.. code-block:: cpp + + template + struct as_returns_t { ... }; + + template + as_returns_t as_returns( T&& ); + + +This allows you to wrap up a source that has ``begin`` and ``end`` iterator-returning functions on it and return it as multiple results into Lua. To have more control over the returns, use :doc:`sol::variadic_results` + + +.. code-block:: cpp + :linenos: + :caption: as_returns.c++ + + #define SOL_CHECK_ARGUMENTS + #include + + #include + #include + + int main () { + sol::state lua; + + lua.set_function("f", []() { + std::set results{ "arf", "bark", "woof" }; + return sol::as_returns(std::move(results)); + }); + + lua.script(R"( + v1, v2, v3 = f() + )"); + }()); + + std::string v1 = lua["v1"]; + std::string v2 = lua["v2"]; + std::string v3 = lua["v3"]; + + assert(v1 == "arf"); + assert(v2 == "bark"); + assert(v3 == "woof"); + + return 0; + } diff --git a/docs/source/api/variadic_results.rst b/docs/source/api/variadic_results.rst new file mode 100644 index 00000000..fb9da9ef --- /dev/null +++ b/docs/source/api/variadic_results.rst @@ -0,0 +1,11 @@ +variadic_results +================ +push multiple disparate arguments into lua +------------------------------------------ + +.. code-block:: cpp + + struct variadic_results : std::vector { ... }; + + template + as_args_t as_args( T&& ); diff --git a/docs/source/compilation.rst b/docs/source/compilation.rst index d0bbf895..cb33dec9 100644 --- a/docs/source/compilation.rst +++ b/docs/source/compilation.rst @@ -50,6 +50,10 @@ Clang 3.4, 3.5 and 3.6 have many bugs we have run into when developing sol2 and We encourage all users to upgrade immediately. If you need old code for some reason, use `sol2 release v2.18.0`_: otherwise, always grab sol2's latest. +feature support +--------------- + +track future compiler and feature support in `this issue here`_. supported Lua version --------------------- @@ -105,4 +109,5 @@ Hopefully, as things progress, we move things forward. .. _sol2 release v2.18.0: https://github.com/ThePhD/sol2/releases/tag/v2.17.5 .. _OrfeasZ in this issue: https://github.com/ThePhD/sol2/issues/329#issuecomment-276824983 -.. _issue describing preliminary steps can be found here: https://github.com/ThePhD/sol2/issues/436#issuecomment-312021508 \ No newline at end of file +.. _issue describing preliminary steps can be found here: https://github.com/ThePhD/sol2/issues/436#issuecomment-312021508 +.. _this issue here: https://github.com/ThePhD/sol2/issues/426 diff --git a/docs/source/functions.rst b/docs/source/functions.rst index d5c7425e..9cc0e815 100644 --- a/docs/source/functions.rst +++ b/docs/source/functions.rst @@ -9,11 +9,13 @@ There are a number of examples dealing with functions and how they can be bound * For a quicker walkthrough that demonstrates almost everything, see `the examples`_ and the :doc:`the quick and dirty tutorial` * For a full explanation, :doc:`read the tutorial` and consult the subjects below * If you have bindings and set-ups that want to leverage the C API without sol2's interference, you can push a raw function, which has certain implications (noted :ref:`below`) +* You return multiple values by returning a `std::tuple` * You can work with **transparent arguments** that provide you with special information, such as - :doc:`sol::variadic_args`, for handling variable number of arguments at runtime - :doc:`sol::this_state`, for getting the current Lua state - :doc:`sol::this_environment`, for potentially retrieving the current Lua environment * :doc:`Overload function calls on a single name`, discriminating by argument number and type (first-come, first-serve overloading) + - Note: because of this feature, automatic number to string conversion from Lua is not permitted * Control serialization of arguments and return types with :doc:`sol::nested`, :doc:`sol::as_table`, :doc:`sol::as_args` and :doc:`sol::as_function` * Set environments for Lua functions and scrips with :doc:`sol::environment` diff --git a/docs/source/safety.rst b/docs/source/safety.rst index 77e3af80..4341401f 100644 --- a/docs/source/safety.rst +++ b/docs/source/safety.rst @@ -11,7 +11,7 @@ config Note that you can obtain safety with regards to functions you bind by using the :doc:`protect` wrapper around function/variable bindings you set into Lua. Additionally, you can have basic boolean checks when using the API by just converting to a :doc:`sol::optional\` when necessary for getting things out of Lua and for function arguments. ``SOL_SAFE_USERTYPE`` triggers the following change: - * If the userdata to a usertype function is nil, will trigger an error instead of letting things go through and letting the system segfault/crash. + * If the userdata to a usertype function is nil, will trigger an error instead of letting things go through and letting the system segfault/crash * Turned on by default with clang++, g++ and VC++ if a basic check for building in debug mode is detected ``SOL_SAFE_FUNCTION`` triggers the following change: @@ -19,8 +19,9 @@ Note that you can obtain safety with regards to functions you bind by using the * Not turned on by default under any detectible compiler settings: you must turn this one on manually ``SOL_CHECK_ARGUMENTS`` triggers the following changes: - * ``sol::stack::get`` (used everywhere) defaults to using ``sol::stack::check_get`` and dereferencing the argument. It uses ``sol::type_panic`` as the handler if something goes wrong. - * ``sol::stack::call`` and its variants will, if no templated boolean is specified, check all of the arguments for a function call. + * ``sol::stack::get`` (used everywhere) defaults to using ``sol::stack::check_get`` and dereferencing the argument. It uses ``sol::type_panic`` as the handler if something goes wrong + * ``lua_tolstring`` conversions are not permitted on numbers: through the API: only actual strings are allowed. This is necessary to allow :doc:`sol::overload` to work properly + * ``sol::stack::call`` and its variants will, if no templated boolean is specified, check all of the arguments for a function call * If ``SOL_SAFE_USERTYPE`` is not defined, it gets defined to turn being on and the effects described above kick in Tests are compiled with this on to ensure everything is going as expected. Remember that if you want these features, you must explicitly turn them on all of them to be sure you are getting them. diff --git a/examples/functions.cpp b/examples/functions.cpp index 8d16d8b0..3b6d7781 100644 --- a/examples/functions.cpp +++ b/examples/functions.cpp @@ -5,7 +5,7 @@ #include inline int my_add(int x, int y) { - return x + y; + return x + y; } struct multiplier { @@ -18,10 +18,6 @@ struct multiplier { } }; -inline std::string make_string( std::string input ) { - return "string: " + input; -} - int main() { std::cout << "=== functions example ===" << std::endl; @@ -67,6 +63,7 @@ inc() // Do something based on this information std::cout << "Yahoo! x is " << x << std::endl; } + // retrieval of a function is done similarly // to other variables, using sol::function sol::function add = lua["my_add"]; @@ -79,34 +76,7 @@ inc() std::cout << "Woo, value is 21!" << std::endl; } - // multi-return functions are supported using - // std::tuple as the interface. - lua.set_function("multi", [] { return std::make_tuple(10, "goodbye"); }); - lua.script("x, y = multi()"); - lua.script("assert(x == 10 and y == 'goodbye')"); - - auto multi = lua.get("multi"); - int first; - std::string second; - sol::tie(first, second) = multi(); - - // use the values - assert(first == 10); - assert(second == "goodbye"); - - // you can even overload functions - // just pass in the different functions - // you want to pack into a single name: - // make SURE they take different types! - - lua.set_function("func", sol::overload([](int x) { return x; }, make_string, my_add)); - - // All these functions are now overloaded through "func" - lua.script(R"( -print(func(1)) -print(func("bark")) -print(func(1,2)) -)"); - std::cout << std::endl; + + return 0; } \ No newline at end of file diff --git a/examples/multi_results.cpp b/examples/multi_results.cpp new file mode 100644 index 00000000..21342148 --- /dev/null +++ b/examples/multi_results.cpp @@ -0,0 +1,81 @@ +#define SOL_CHECK_ARGUMENTS +#include + +#include +#include +#include + +int main() { + std::cout << "=== multi results example ===" << std::endl; + + sol::state lua; + lua.open_libraries(sol::lib::base); + + // multi-return functions are supported using + // std::tuple as the transfer type, + // sol::as_returns for containers, + // and sol::variadic_results for more special things + lua.set_function("multi_tuple", [] { + return std::make_tuple(10, "goodbye"); + }); + lua.script("print('calling multi_tuple')"); + lua.script("print(multi_tuple())"); + lua.script("x, y = multi_tuple()"); + lua.script("assert(x == 10 and y == 'goodbye')"); + + auto multi = lua.get("multi"); + int first; + std::string second; + // tie the values + sol::tie(first, second) = multi(); + + // use the values + assert(first == 10); + assert(second == "goodbye"); + + // sol::as_returns + // works with any iterable, + // but we show off std::vector here + lua.set_function("multi_containers", [] (bool add_extra) { + std::vector values{55, 66}; + if (add_extra) { + values.push_back(77); + } + return sol::as_returns(std::move(values)); + }); + lua.script("print('calling multi_containers')"); + lua.script("print(multi_containers(false))"); + lua.script("a, b, c = multi_containers(true)"); + int a = lua["a"]; + int b = lua["b"]; + int c = lua["c"]; + + assert(a == 55); + assert(b == 66); + assert(c == 77); + + // sol::variadic_results + // you can push objects of different types + // note that sol::this_state is a transparent + // argument: you don't need to pass + // that state through Lua + lua.set_function("multi_vars", [](int a, bool b, sol::this_state L) { + sol::variadic_results values; + values.push_back({ L, sol::in_place_type, a }); + values.push_back({ L, sol::in_place_type, b }); + values.push_back({ L, sol::in_place_type, "awoo" }); + return values; + }); + lua.script("print('calling multi_vars')"); + lua.script("print(multi_vars(2, false))"); + lua.script("t, u, v = multi_vars(42, true)"); + int t = lua["t"]; + bool u = lua["u"]; + std::string v = lua["v"]; + + assert(t == 42); + assert(u); + assert(v == "awoo"); + + return 0; +} \ No newline at end of file diff --git a/examples/overloading.cpp b/examples/overloading.cpp new file mode 100644 index 00000000..1d068557 --- /dev/null +++ b/examples/overloading.cpp @@ -0,0 +1,42 @@ +#define SOL_CHECK_ARGUMENTS +#include + +#include +#include + +inline int my_add(int x, int y) { + return x + y; +} + +inline std::string make_string(std::string input) { + return "string: " + input; +} + +int main() { + std::cout << "=== overloading example ===" << std::endl; + + sol::state lua; + lua.open_libraries(sol::lib::base); + + // you can overload functions + // just pass in the different functions + // you want to pack into a single name: + // make SURE they take different types! + + lua.set_function("func", sol::overload( + [](int x) { return x; }, + make_string, + my_add + )); + + // All these functions are now overloaded through "func" + lua.script(R"( +print(func(1)) +print(func("bark")) +print(func(1,2)) +)"); + + std::cout << std::endl; + + return 0; +} \ No newline at end of file diff --git a/sol.hpp b/sol.hpp index 3acefec9..f428c7f4 100644 --- a/sol.hpp +++ b/sol.hpp @@ -43,8 +43,11 @@ #elif defined _MSC_VER #pragma warning( push ) #pragma warning( disable : 4324 ) // structure was padded due to alignment specifier +#pragma warning( disable : 4503 ) // decorated name horse shit +#pragma warning( disable : 4702 ) // unreachable code #endif // g++ +#include "sol/forward.hpp" #include "sol/state.hpp" #include "sol/object.hpp" #include "sol/function.hpp" @@ -52,6 +55,7 @@ #include "sol/state.hpp" #include "sol/coroutine.hpp" #include "sol/variadic_args.hpp" +#include "sol/variadic_results.hpp" #ifdef __GNUC__ #pragma GCC diagnostic pop diff --git a/sol/as_args.hpp b/sol/as_args.hpp index 123d3a00..977c27e7 100644 --- a/sol/as_args.hpp +++ b/sol/as_args.hpp @@ -19,26 +19,26 @@ // 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_TO_ARGS_HPP -#define SOL_TO_ARGS_HPP +#ifndef SOL_AS_ARGS_HPP +#define SOL_AS_ARGS_HPP #include "stack.hpp" namespace sol { template - struct to_args_t { + struct as_args_t { T src; }; template auto as_args(Source&& source) { - return to_args_t{ std::forward(source) }; + return as_args_t{ std::forward(source) }; } namespace stack { template - struct pusher> { - int push(lua_State* L, const to_args_t& e) { + struct pusher> { + int push(lua_State* L, const as_args_t& e) { int p = 0; for (const auto& i : e.src) { p += stack::push(L, i); @@ -46,7 +46,7 @@ namespace sol { return p; } }; - } + } // stack } // sol -#endif // SOL_TO_ARGS_HPP +#endif // SOL_AS_ARGS_HPP diff --git a/sol/as_returns.hpp b/sol/as_returns.hpp new file mode 100644 index 00000000..9621dddb --- /dev/null +++ b/sol/as_returns.hpp @@ -0,0 +1,54 @@ +// The MIT License (MIT) + +// Copyright (c) 2013-2017 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_AS_RETURNS_HPP +#define SOL_AS_RETURNS_HPP + +#include "traits.hpp" +#include "stack.hpp" + +namespace sol { + template + struct as_returns_t { + T src; + }; + + template + auto as_returns(Source&& source) { + return as_returns_t>{ std::forward(source) }; + } + + namespace stack { + template + struct pusher> { + int push(lua_State* L, const as_returns_t& e) { + auto& src = detail::unwrap(e.src); + int p = 0; + for (const auto& i : src) { + p += stack::push(L, i); + } + return p; + } + }; + } // stack +} // sol + +#endif // SOL_AS_RETURNS_HPP diff --git a/sol/container_traits.hpp b/sol/container_traits.hpp index e5e843a6..eae539c5 100644 --- a/sol/container_traits.hpp +++ b/sol/container_traits.hpp @@ -22,11 +22,523 @@ #ifndef SOL_CONTAINER_TRAITS_HPP #define SOL_CONTAINER_TRAITS_HPP +#include "traits.hpp" #include "stack.hpp" #include namespace sol { + namespace container_detail { + template + struct has_find { + private: + typedef std::array one; + typedef std::array two; + + template static one test(decltype(std::declval().find(std::declval>()))*); + template static two test(...); + + public: + static const bool value = sizeof(test(0)) == sizeof(char); + }; + + template + struct has_push_back { + private: + typedef std::array one; + typedef std::array two; + + template static one test(decltype(std::declval().push_back(std::declval>()))*); + template static two test(...); + + public: + static const bool value = sizeof(test(0)) == sizeof(char); + }; + + template + struct has_clear { + private: + typedef std::array one; + typedef std::array two; + + template static one test(decltype(&C::clear)); + template static two test(...); + + public: + static const bool value = sizeof(test(0)) == sizeof(char); + }; + + template + struct has_insert { + private: + typedef std::array one; + typedef std::array two; + + template static one test(decltype(std::declval().insert(std::declval>(), std::declval>()))*); + template static two test(...); + + public: + static const bool value = sizeof(test(0)) == sizeof(char); + }; + + template + struct container_traits_default { + private: + typedef std::remove_pointer_t> T; + public: + typedef std::false_type is_container; + typedef std::false_type is_associative; + typedef lua_nil_t iterator; + typedef lua_nil_t value_type; + + static lua_nil_t get(lua_State* L, T* self, stack_object key) { + (void)self; + (void)key; + luaL_error(L, "sol: cannot call 'get(key)' on type '%s' because it is not recognized as a container", detail::demangle().c_str()); + return lua_nil; + } + + static lua_nil_t index_get(lua_State* L, T* self, stack_object key) { + (void)self; + (void)key; + luaL_error(L, "sol: cannot call 'container[key]' on type '%s' because it is not recognized as a container", detail::demangle().c_str()); + return lua_nil; + } + + static lua_nil_t index_set(lua_State* L, T* self, stack_object key, stack_object value) { + (void)self; + (void)key; + (void)value; + luaL_error(L, "sol: cannot call 'container[key] = value' on type '%s' because it is not recognized as a container", detail::demangle().c_str()); + return lua_nil; + } + + static lua_nil_t add(lua_State* L, T* self, stack_object key, stack_object value) { + (void)self; + (void)key; + (void)value; + luaL_error(L, "sol: cannot call '%s' on type '%s' because it is not recognized as a container", value.valid() ? "add(key, value)" : "add(value)", detail::demangle().c_str()); + return lua_nil; + } + + static lua_nil_t insert(lua_State* L, T* self, stack_object where, stack_object value) { + (void)self; + (void)where; + (void)value; + luaL_error(L, "sol: cannot call 'container[key] = value' on type '%s' because it is not recognized as a container", detail::demangle().c_str()); + return lua_nil; + } + + static lua_nil_t find(lua_State* L, T* self, stack_object key) { + (void)self; + (void)key; + luaL_error(L, "sol: cannot call 'container[key] = value' on type '%s' because it is not recognized as a container", detail::demangle().c_str()); + return lua_nil; + } + + static lua_nil_t begin(lua_State* L, T* self) { + (void)self; + luaL_error(L, "sol: cannot call 'begin' on type '%s' because it is not recognized as a container", detail::demangle().c_str()); + return lua_nil; + } + + static lua_nil_t end(lua_State* L, T* self) { + (void)self; + luaL_error(L, "sol: cannot call 'end' on type '%s' because it is not recognized as a container", detail::demangle().c_str()); + return lua_nil; + } + + static lua_nil_t size(lua_State* L, T* self) { + (void)self; + luaL_error(L, "sol: cannot call 'end' on type '%s' because it is not recognized as a container", detail::demangle().c_str()); + return lua_nil; + } + + static lua_nil_t clear(lua_State* L, T* self) { + (void)self; + luaL_error(L, "sol: cannot call 'clear' on type '%s' because it is not recognized as a container", detail::demangle().c_str()); + return lua_nil; + } + + static lua_nil_t erase(lua_State* L, T* self, stack_object key) { + (void)self; + (void)key; + luaL_error(L, "sol: cannot call 'erase' on type '%s' because it is not recognized as a container", detail::demangle().c_str()); + return lua_nil; + } + }; + template + struct container_traits_default>, meta::has_value_type>, meta::has_iterator>>::value + >> { + private: + typedef std::remove_pointer_t> T; + typedef typename T::iterator iterator; + typedef std::conditional_t> KV; + typedef typename KV::first_type K; + typedef typename KV::second_type V; + typedef std::remove_reference_t())> iterator_return; + typedef typename meta::iterator_tag::type iterator_category; + typedef std::conditional_t::value, + V, + std::conditional_t()) + > + > push_type; + + static auto& get_src(lua_State* L) { +#ifdef SOL_SAFE_USERTYPE + auto p = stack::check_get(L, 1); + if (!p || p.value() == nullptr) { + luaL_error(L, "sol: 'self' argument is not the proper type (pass 'self' as first argument with ':' or call on proper type)"); + } + return *p.value(); +#else + return stack::get(L, 1); +#endif // Safe getting with error + } + + public: + + static int delegate_call(lua_State* L) { + static std::unordered_map calls{ + { "add", &real_add_call }, + { "insert", &real_insert_call }, + { "clear", &real_clear_call }, + { "find", &real_find_call }, + { "get", &real_get_call } + }; + auto maybename = stack::check_get(L, 2); + if (maybename) { + auto& name = *maybename; + auto it = calls.find(name); + if (it != calls.cend()) { + return stack::push(L, it->second); + } + } + return stack::push(L, lua_nil); + } + + static int real_index_call_associative(std::true_type, lua_State* L) { + auto k = stack::check_get(L, 2); + if (k) { + auto& src = get_src(L); + using std::end; + auto it = detail::find(src, *k); + if (it != end(src)) { + auto& v = *it; + return stack::stack_detail::push_reference(L, v.second); + } + } + else { + return delegate_call(L); + } + return stack::push(L, lua_nil); + } + + static int real_index_call_associative(std::false_type, lua_State* L) { + auto& src = get_src(L); + auto maybek = stack::check_get(L, 2); + if (maybek) { + using std::begin; + auto it = begin(src); + K k = *maybek; + if (k > src.size() || k < 1) { + return stack::push(L, lua_nil); + } + --k; + std::advance(it, k); + return stack::stack_detail::push_reference(L, *it); + } + else { + return delegate_call(L); + } + + return stack::push(L, lua_nil); + } + + static int real_index_call(lua_State* L) { + return real_index_call_associative(is_associative(), L); + } + + static int real_get_call(lua_State* L) { + return real_index_call_associative(is_associative(), L); + } + + static int real_new_index_call_const(std::false_type, std::false_type, lua_State* L) { + return luaL_error(L, "sol: cannot write to a const value type or an immutable iterator (e.g., std::set)"); + } + + static int real_new_index_call_const(std::false_type, std::true_type, lua_State* L) { + return luaL_error(L, "sol: cannot write to a const value type or an immutable iterator (e.g., std::set)"); + } + + static int real_new_index_call_fixed(std::true_type, lua_State* L) { + auto& src = get_src(L); +#ifdef SOL_CHECK_ARGUMENTS + auto maybek = stack::check_get(L, 2); + if (!maybek) { + return luaL_error(L, "sol: improper key of type %s for %s", lua_typename(L, static_cast(type_of(L, 2))), detail::demangle().c_str()); + } + K& k = *maybek; +#else + K k = stack::get(L, 2); +#endif + using std::end; + auto it = detail::find(src, k); + if (it != end(src)) { + auto& v = *it; + v.second = stack::get(L, 3); + } + else { + src.insert(it, { std::move(k), stack::get(L, 3) }); + } + return 0; + } + + static int real_new_index_call_fixed(std::false_type, lua_State* L) { + auto& src = get_src(L); +#ifdef SOL_CHECK_ARGUMENTS + auto maybek = stack::check_get(L, 2); + if (!maybek) { + return luaL_error(L, "sol: improper key of type %s for %s", lua_typename(L, static_cast(type_of(L, 2))), detail::demangle().c_str()); + } + K& k = *maybek; +#else + K k = stack::get(L, 2); +#endif + using std::end; + auto it = detail::find(src, k); + if (it != end(src)) { + auto& v = *it; + v.second = stack::get(L, 3); + } + else { + return luaL_error(L, "sol: cannot insert key of type %s to into %s", lua_typename(L, static_cast(type_of(L, 2))), detail::demangle().c_str()); + } + return 0; + } + + static int real_new_index_call_const(std::true_type, std::true_type, lua_State* L) { + return real_new_index_call_fixed(std::integral_constant::value>(), L); + } + + static int real_new_index_call_const(std::true_type, std::false_type, lua_State* L) { + auto& src = get_src(L); +#ifdef SOL_CHECK_ARGUMENTS + auto maybek = stack::check_get(L, 2); + if (!maybek) { + return luaL_error(L, "sol: improper index of type %s to a %s", lua_typename(L, static_cast(type_of(L, 2))), detail::demangle().c_str()); + } + K& k = *maybek; +#else + K k = stack::get(L, 2); +#endif + using std::begin; + auto it = begin(src); +#ifdef SOL_CHECK_ARGUMENTS + if (k < 1) { + return luaL_error(L, "sol: out of bounds index to a %s", detail::demangle().c_str()); + } +#endif + --k; + if (k == src.size()) { + real_add_call_push(std::integral_constant::value && std::is_copy_constructible::value>(), L, src, 1); + return 0; + } +#ifdef SOL_CHECK_ARGUMENTS + if (k > src.size()) { + return luaL_error(L, "sol: out of bounds index to a %s", detail::demangle().c_str()); + } +#endif + std::advance(it, k); + *it = stack::get(L, 3); + return 0; + } + + static int real_new_index_call(lua_State* L) { + return real_new_index_call_const(meta::neg, std::is_const, meta::neg>>>(), meta::all>(), L); + } + + static int real_pairs_next_call_assoc(std::true_type, lua_State* L) { + using std::end; + iter& i = stack::get>(L, 1); + auto& source = i.source; + auto& it = i.it; + if (it == end(source)) { + return 0; + } + int p; + p = stack::push_reference(L, it->first); + p += stack::stack_detail::push_reference(L, it->second); + std::advance(it, 1); + return p; + } + + static int real_pairs_call_assoc(std::true_type, lua_State* L) { + auto& src = get_src(L); + using std::begin; + stack::push(L, pairs_next_call); + stack::push>(L, src, begin(src)); + stack::push(L, 1); + return 3; + } + + static int real_pairs_next_call_assoc(std::false_type, lua_State* L) { + using std::end; + iter& i = stack::get>(L, 1); + auto& source = i.source; + auto& it = i.it; + K k = stack::get(L, 2); + if (it == end(source)) { + return 0; + } + int p; + p = stack::push_reference(L, k + 1); + p += stack::stack_detail::push_reference(L, *it); + std::advance(it, 1); + return p; + } + + static int real_pairs_call_assoc(std::false_type, lua_State* L) { + auto& src = get_src(L); + using std::begin; + stack::push(L, pairs_next_call); + stack::push>(L, src, begin(src)); + stack::push(L, 0); + return 3; + } + + static int real_pairs_next_call(lua_State* L) { + return real_pairs_next_call_assoc(is_associative(), L); + } + + static int real_pairs_call(lua_State* L) { + return real_pairs_call_assoc(is_associative(), L); + } + + static int real_length_call(lua_State*L) { + auto& src = get_src(L); + return stack::push(L, src.size()); + } + + static int real_add_call_insert(std::true_type, lua_State*L, T& src, int boost = 0) { + using std::end; + src.insert(end(src), stack::get(L, 2 + boost)); + return 0; + } + + static int real_add_call_insert(std::false_type, lua_State*L, T&, int = 0) { + static const std::string& s = detail::demangle(); + return luaL_error(L, "sol: cannot call insert on type %s", s.c_str()); + } + + static int real_add_call_push(std::true_type, lua_State*L, T& src, int boost = 0) { + src.push_back(stack::get(L, 2 + boost)); + return 0; + } + + static int real_add_call_push(std::false_type, lua_State*L, T& src, int boost = 0) { + return real_add_call_insert(std::integral_constant::value && std::is_copy_constructible::value>(), L, src, boost); + } + + static int real_add_call_associative(std::true_type, lua_State* L) { + return real_insert_call(L); + } + + static int real_add_call_associative(std::false_type, lua_State* L) { + auto& src = get_src(L); + return real_add_call_push(std::integral_constant::value && std::is_copy_constructible::value>(), L, src); + } + + static int real_add_call_capable(std::true_type, lua_State* L) { + return real_add_call_associative(is_associative(), L); + } + + static int real_add_call_capable(std::false_type, lua_State* L) { + static const std::string& s = detail::demangle(); + return luaL_error(L, "sol: cannot call add on type %s", s.c_str()); + } + + static int real_add_call(lua_State* L) { + return real_add_call_capable(std::integral_constant::value || detail::has_insert::value) && std::is_copy_constructible::value>(), L); + } + + static int real_insert_call_capable(std::false_type, std::false_type, lua_State*L) { + static const std::string& s = detail::demangle(); + return luaL_error(L, "sol: cannot call insert on type %s", s.c_str()); + } + + static int real_insert_call_capable(std::false_type, std::true_type, lua_State*L) { + return real_insert_call_capable(std::false_type(), std::false_type(), L); + } + + static int real_insert_call_capable(std::true_type, std::false_type, lua_State* L) { + using std::begin; + auto& src = get_src(L); + src.insert(std::next(begin(src), stack::get(L, 2)), stack::get(L, 3)); + return 0; + } + + static int real_insert_call_capable(std::true_type, std::true_type, lua_State* L) { + return real_new_index_call(L); + } + + static int real_insert_call(lua_State*L) { + return real_insert_call_capable(std::integral_constant::value && std::is_copy_assignable::value>(), is_associative(), L); + } + + static int real_clear_call_capable(std::false_type, lua_State* L) { + static const std::string& s = detail::demangle(); + return luaL_error(L, "sol: cannot call clear on type %s", s.c_str()); + } + + static int real_clear_call_capable(std::true_type, lua_State* L) { + auto& src = get_src(L); + src.clear(); + return 0; + } + + static int real_clear_call(lua_State*L) { + return real_clear_call_capable(std::integral_constant::value>(), L); + } + + static int real_find_call_capable(std::false_type, std::false_type, lua_State*L) { + static const std::string& s = detail::demangle(); + return luaL_error(L, "sol: cannot call find on type %s", s.c_str()); + } + + static int real_find_call_capable(std::false_type, std::true_type, lua_State*L) { + return real_index_call(L); + } + + static int real_find_call_capable(std::true_type, std::false_type, lua_State* L) { + auto k = stack::check_get(L, 2); + if (k) { + auto& src = get_src(L); + auto it = src.find(*k); + if (it != src.end()) { + auto& v = *it; + return stack::stack_detail::push_reference(L, v); + } + } + return stack::push(L, lua_nil); + } + + static int real_find_call_capable(std::true_type, std::true_type, lua_State* L) { + return real_index_call(L); + } + + static int real_find_call(lua_State*L) { + return real_find_call_capable(std::integral_constant::value>(), is_associative(), L); + } + }; + } // container_detail + + template + struct container_traits : container_detail::container_traits_default {}; + } // sol #endif // SOL_CONTAINER_TRAITS_HPP diff --git a/sol/container_usertype_metatable.hpp b/sol/container_usertype_metatable.hpp index 967cbd7e..30b747cf 100644 --- a/sol/container_usertype_metatable.hpp +++ b/sol/container_usertype_metatable.hpp @@ -174,10 +174,7 @@ namespace sol { return stack::stack_detail::push_reference(L, v.second); } } - else { - return delegate_call(L); - } - return stack::push(L, lua_nil); + return delegate_call(L); } static int real_index_call_associative(std::false_type, lua_State* L) { @@ -194,11 +191,7 @@ namespace sol { std::advance(it, k); return stack::stack_detail::push_reference(L, *it); } - else { - return delegate_call(L); - } - - return stack::push(L, lua_nil); + return delegate_call(L); } static int real_index_call(lua_State* L) { diff --git a/sol/feature_test.hpp b/sol/feature_test.hpp index 5048a24b..2cbd3cb3 100644 --- a/sol/feature_test.hpp +++ b/sol/feature_test.hpp @@ -22,6 +22,12 @@ #ifndef SOL_FEATURE_TEST_HPP #define SOL_FEATURE_TEST_HPP +#if (defined(__cplusplus) && __cplusplus == 201703L) || (defined(_MSC_VER) && _MSC_VER > 1900 && (defined(_HAS_CXX17) && _HAS_CXX17 == 1) || (_MSVC_LANG > 201402)) +#ifndef SOL_CXX17_FEATURES +#define SOL_CXX17_FEATURES 1 +#endif // C++17 features macro +#endif // C++17 features check + #if defined(__cpp_noexcept_function_type) #ifndef SOL_NOEXCEPT_FUNCTION_TYPE #define SOL_NOEXCEPT_FUNCTION_TYPE 1 diff --git a/sol/forward.hpp b/sol/forward.hpp new file mode 100644 index 00000000..b3b82b79 --- /dev/null +++ b/sol/forward.hpp @@ -0,0 +1,98 @@ +// The MIT License (MIT) + +// Copyright (c) 2013-2017 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_FORWARD_HPP +#define SOL_FORWARD_HPP + +#include "feature_test.hpp" + +namespace sol { + + class reference; + class stack_reference; + template + struct proxy; + template + class usertype; + template + class simple_usertype; + template + class basic_table_core; + template + using table_core = basic_table_core; + template + using stack_table_core = basic_table_core; + template + using basic_table = basic_table_core; + typedef table_core table; + typedef table_core global_table; + typedef stack_table_core stack_table; + typedef stack_table_core stack_global_table; + template + struct basic_environment; + using environment = basic_environment; + using stack_environment = basic_environment; + template + class basic_function; + template + class basic_protected_function; + using protected_function = basic_protected_function; + using stack_protected_function = basic_protected_function; + using unsafe_function = basic_function; + using safe_function = basic_protected_function; + using stack_unsafe_function = basic_function; + using stack_safe_function = basic_protected_function; +#ifdef SOL_SAFE_FUNCTIONS + using function = protected_function; + using stack_function = stack_protected_function; +#else + using function = unsafe_function; + using stack_function = stack_unsafe_function; +#endif + template + class basic_object; + template + class basic_userdata; + template + class basic_lightuserdata; + struct variadic_args; + using object = basic_object; + using stack_object = basic_object; + using userdata = basic_userdata; + using stack_userdata = basic_userdata; + using lightuserdata = basic_lightuserdata; + using stack_lightuserdata = basic_lightuserdata; + class coroutine; + class thread; + struct variadic_args; + struct variadic_results; + struct this_state; + struct this_environment; + template + struct light; + template + struct user; + template + struct as_args_t; + +} // sol + +#endif SOL_FORWARD_HPP diff --git a/sol/function.hpp b/sol/function.hpp index 4d660187..f73a61f7 100644 --- a/sol/function.hpp +++ b/sol/function.hpp @@ -22,96 +22,12 @@ #ifndef SOL_FUNCTION_HPP #define SOL_FUNCTION_HPP -#include "reference.hpp" +#include "unsafe_function.hpp" +#include "protected_function.hpp" #include "stack.hpp" -#include "function_result.hpp" -#include "function_types.hpp" -#include #include -#include namespace sol { - template - class basic_function : public base_t { - private: - void luacall(std::ptrdiff_t argcount, std::ptrdiff_t resultcount) const { - lua_callk(base_t::lua_state(), static_cast(argcount), static_cast(resultcount), 0, nullptr); - } - - template - auto invoke(types, std::index_sequence, std::ptrdiff_t n) const { - luacall(n, lua_size>::value); - return stack::pop>(base_t::lua_state()); - } - - template - Ret invoke(types, std::index_sequence, std::ptrdiff_t n) const { - luacall(n, lua_size::value); - return stack::pop(base_t::lua_state()); - } - - template - void invoke(types, std::index_sequence, std::ptrdiff_t n) const { - luacall(n, 0); - } - - function_result invoke(types<>, std::index_sequence<>, std::ptrdiff_t n) const { - int stacksize = lua_gettop(base_t::lua_state()); - int firstreturn = (std::max)(1, stacksize - static_cast(n)); - luacall(n, LUA_MULTRET); - int poststacksize = lua_gettop(base_t::lua_state()); - int returncount = poststacksize - (firstreturn - 1); - return function_result(base_t::lua_state(), firstreturn, returncount); - } - - public: - basic_function() = default; - template , basic_function>>, meta::neg>, std::is_base_of>> = meta::enabler> - basic_function(T&& r) noexcept : base_t(std::forward(r)) { -#ifdef SOL_CHECK_ARGUMENTS - if (!is_function>::value) { - auto pp = stack::push_pop(*this); - stack::check(base_t::lua_state(), -1, type_panic); - } -#endif // Safety - } - basic_function(const basic_function&) = default; - basic_function& operator=(const basic_function&) = default; - basic_function(basic_function&&) = default; - basic_function& operator=(basic_function&&) = default; - basic_function(const stack_reference& r) : basic_function(r.lua_state(), r.stack_index()) {} - basic_function(stack_reference&& r) : basic_function(r.lua_state(), r.stack_index()) {} - template >>, meta::neg>> = meta::enabler> - basic_function(lua_State* L, T&& r) : basic_function(L, sol::ref_index(r.registry_index())) {} - basic_function(lua_State* L, int index = -1) : base_t(L, index) { -#ifdef SOL_CHECK_ARGUMENTS - stack::check(L, index, type_panic); -#endif // Safety - } - basic_function(lua_State* L, ref_index index) : base_t(L, index) { -#ifdef SOL_CHECK_ARGUMENTS - auto pp = stack::push_pop(*this); - stack::check(L, -1, type_panic); -#endif // Safety - } - - template - function_result operator()(Args&&... args) const { - return call<>(std::forward(args)...); - } - - template - decltype(auto) operator()(types, Args&&... args) const { - return call(std::forward(args)...); - } - - template - decltype(auto) call(Args&&... args) const { - base_t::push(); - int pushcount = stack::multi_push_reference(base_t::lua_state(), std::forward(args)...); - return invoke(types(), std::make_index_sequence(), pushcount); - } - }; namespace stack { template @@ -154,6 +70,7 @@ namespace sol { } }; } // stack + } // sol #endif // SOL_FUNCTION_HPP diff --git a/sol/in_place.hpp b/sol/in_place.hpp index 02b3677b..1f7af9eb 100644 --- a/sol/in_place.hpp +++ b/sol/in_place.hpp @@ -22,8 +22,23 @@ #ifndef SOL_IN_PLACE_HPP #define SOL_IN_PLACE_HPP +#include +#include + namespace sol { +#ifdef SOL_CXX17_FEATURES + using in_place_t = std::in_place_t; + constexpr std::in_place_t in_place{}; + + template using in_place_type_t = std::in_place_type_t; + template + constexpr std::in_place_type_t in_place_type{}; + + template using in_place_index_t = std::in_place_index_t; + template + constexpr in_place_index_t in_place_index{}; +#else namespace detail { struct in_place_of {}; template @@ -32,18 +47,25 @@ namespace sol { struct in_place_of_t {}; } // detail - struct in_place_tag { struct init {}; constexpr in_place_tag(init) {} in_place_tag() = delete; }; - constexpr inline in_place_tag in_place(detail::in_place_of) { return in_place_tag(in_place_tag::init()); } + struct in_place_tag { constexpr in_place_tag() = default; }; + + constexpr inline in_place_tag in_place (detail::in_place_of) { return in_place_tag(); } template - constexpr inline in_place_tag in_place(detail::in_place_of_t) { return in_place_tag(in_place_tag::init()); } + constexpr inline in_place_tag in_place (detail::in_place_of_t) { return in_place_tag(); } template - constexpr inline in_place_tag in_place(detail::in_place_of_i) { return in_place_tag(in_place_tag::init()); } + constexpr inline in_place_tag in_place (detail::in_place_of_i) { return in_place_tag(); } + + template + constexpr inline in_place_tag in_place_type (detail::in_place_of_t) { return in_place_tag(); } + template + constexpr inline in_place_tag in_place_index (detail::in_place_of_i) { return in_place_tag(); } using in_place_t = in_place_tag(&)(detail::in_place_of); template using in_place_type_t = in_place_tag(&)(detail::in_place_of_t); template using in_place_index_t = in_place_tag(&)(detail::in_place_of_i); +#endif } // sol diff --git a/sol/object.hpp b/sol/object.hpp index d40759eb..f520ab9c 100644 --- a/sol/object.hpp +++ b/sol/object.hpp @@ -82,7 +82,7 @@ namespace sol { template basic_object(lua_State* L, in_place_type_t, Args&&... args) noexcept : basic_object(std::integral_constant::value>(), L, -stack::push(L, std::forward(args)...)) {} template - basic_object(lua_State* L, in_place_t, T&& arg, Args&&... args) noexcept : basic_object(L, in_place, std::forward(arg), std::forward(args)...) {} + basic_object(lua_State* L, in_place_t, T&& arg, Args&&... args) noexcept : basic_object(L, in_place_type, std::forward(arg), std::forward(args)...) {} basic_object& operator=(const basic_object&) = default; basic_object& operator=(basic_object&&) = default; basic_object& operator=(const base_type& b) { base_t::operator=(b); return *this; } diff --git a/sol/optional_implementation.hpp b/sol/optional_implementation.hpp index e731ac78..8e9dfc40 100644 --- a/sol/optional_implementation.hpp +++ b/sol/optional_implementation.hpp @@ -30,6 +30,8 @@ # ifndef SOL_OPTIONAL_IMPLEMENTATION_HPP # define SOL_OPTIONAL_IMPLEMENTATION_HPP +#include "in_place.hpp" + # include # include # include diff --git a/sol/overload.hpp b/sol/overload.hpp index 68ee1e45..6befc426 100644 --- a/sol/overload.hpp +++ b/sol/overload.hpp @@ -22,6 +22,7 @@ #ifndef SOL_OVERLOAD_HPP #define SOL_OVERLOAD_HPP +#include "traits.hpp" #include namespace sol { diff --git a/sol/property.hpp b/sol/property.hpp index 06c5a667..84b75133 100644 --- a/sol/property.hpp +++ b/sol/property.hpp @@ -22,6 +22,10 @@ #ifndef SOL_PROPERTY_HPP #define SOL_PROPERTY_HPP +#include "types.hpp" +#include +#include + namespace sol { struct no_prop { }; diff --git a/sol/protected_function.hpp b/sol/protected_function.hpp index e3d63008..da9d5c6e 100644 --- a/sol/protected_function.hpp +++ b/sol/protected_function.hpp @@ -25,6 +25,7 @@ #include "reference.hpp" #include "stack.hpp" #include "protected_function_result.hpp" +#include "unsafe_function.hpp" #include #include diff --git a/sol/proxy.hpp b/sol/proxy.hpp index b206bd71..5b1af665 100644 --- a/sol/proxy.hpp +++ b/sol/proxy.hpp @@ -23,7 +23,6 @@ #define SOL_PROXY_HPP #include "traits.hpp" -#include "object.hpp" #include "function.hpp" #include "protected_function.hpp" #include "proxy_base.hpp" @@ -119,6 +118,17 @@ namespace sol { lua_pop(tbl.lua_state(), p.levels); return p; } + + type get_type() const { + type t = type::none; + auto pp = stack::push_pop(tbl); + auto p = stack::probe_get_field, global_table>::value>(tbl.lua_state(), key, lua_gettop(tbl.lua_state())); + if (p) { + t = type_of(tbl.lua_state(), -1); + } + lua_pop(tbl.lua_state(), p.levels); + return t; + } }; template diff --git a/sol/simple_usertype_metatable.hpp b/sol/simple_usertype_metatable.hpp index 8ba64c5c..20315d3a 100644 --- a/sol/simple_usertype_metatable.hpp +++ b/sol/simple_usertype_metatable.hpp @@ -66,7 +66,7 @@ namespace sol { } } string_detail::string_shim accessor = stack::get(L, keyidx); - std::string accessorkey = accessor.c_str(); + std::string accessorkey = accessor.data(); auto vit = variables.find(accessorkey); if (vit != variables.cend()) { auto& varwrap = *(vit->second); diff --git a/sol/stack_check.hpp b/sol/stack_check.hpp index 570adbb6..d8874b93 100644 --- a/sol/stack_check.hpp +++ b/sol/stack_check.hpp @@ -28,6 +28,9 @@ #include #include #include +#ifdef SOL_CXX17_FEATURES +#include +#endif // C++17 namespace sol { namespace stack { @@ -485,6 +488,40 @@ namespace sol { return stack::check(L, index, no_panic, tracking); } }; + +#ifdef SOL_CXX17_FEATURES + template + struct checker, type::poly, C> { + typedef std::variant V; + typedef std::variant_size V_size; + typedef std::integral_constant V_is_empty; + + template + static bool is_one(std::integral_constant, lua_State* L, int index, Handler&& handler, record& tracking) { + if (V_is_empty::value && lua_isnone(L, index)) { + return true; + } + tracking.use(1); + handler(L, index, type::poly, type_of(L, index)); + return false; + } + + template + static bool is_one(std::integral_constant, lua_State* L, int index, Handler&& handler, record& tracking) { + typedef std::variant_alternative_t T; + if (stack::check(L, index, no_panic, tracking)) { + return true; + } + return is_one(std::integral_constant(), L, index, std::forward(handler), tracking); + } + + template + static bool check(lua_State* L, int index, Handler&& handler, record& tracking) { + return is_one(std::integral_constant(), L, index, std::forward(handler), tracking); + } + }; +#endif // C++17 + } // stack } // sol diff --git a/sol/stack_check_get.hpp b/sol/stack_check_get.hpp index 643b0234..a3f01923 100644 --- a/sol/stack_check_get.hpp +++ b/sol/stack_check_get.hpp @@ -26,6 +26,7 @@ #include "stack_get.hpp" #include "stack_check.hpp" #include "optional.hpp" +#include namespace sol { namespace stack { @@ -108,6 +109,48 @@ namespace sol { return check_get(L, index, no_panic, tracking); } }; + +#ifdef SOL_CXX17_FEATURES + template + struct check_getter> { + typedef std::variant V; + typedef std::variant_size V_size; + typedef std::integral_constant V_is_empty; + + template + static optional get_empty(std::true_type, lua_State* L, int index, Handler&& handler, record& tracking) { + return nullopt; + } + + template + static optional get_empty(std::false_type, lua_State* L, int index, Handler&& handler, record& tracking) { + typedef std::variant_alternative_t<0, V> T; + // This should never be reached... + // please check your code and understand what you did to bring yourself here + handler(L, index, type::poly, type_of(L, index)); + return nullopt; + } + + template + static optional get_one(std::integral_constant, lua_State* L, int index, Handler&& handler, record& tracking) { + return get_empty(V_is_empty(), L, index, std::forward(handler), tracking); + } + + template + static optional get_one(std::integral_constant, lua_State* L, int index, Handler&& handler, record& tracking) { + typedef std::variant_alternative_t T; + if (stack::check(L, index, no_panic, tracking)) { + return V(std::in_place_index, stack::get(L, index)); + } + return get_one(std::integral_constant(), L, index, std::forward(handler), tracking); + } + + template + static optional get(lua_State* L, int index, Handler&& handler, record& tracking) { + return get_one(std::integral_constant(), L, index, std::forward(handler), tracking); + } + }; +#endif // C++17 } // stack } // sol diff --git a/sol/stack_get.hpp b/sol/stack_get.hpp index e0394d9e..bfe7b1ab 100644 --- a/sol/stack_get.hpp +++ b/sol/stack_get.hpp @@ -30,10 +30,15 @@ #include #include #include +#include #ifdef SOL_CODECVT_SUPPORT #include #include -#endif +#endif // codecvt header support +#ifdef SOL_CXX17_FEATURES +#include +#include +#endif // C++17 namespace sol { namespace stack { @@ -143,7 +148,7 @@ namespace sol { typedef typename P::second_type V; return get(types(), L, index, tracking); } - + template static T get(types, lua_State* L, int relindex, record& tracking) { tracking.use(1); @@ -173,7 +178,7 @@ namespace sol { return g.get(L, index, tracking); } }; - + template struct getter, std::enable_if_t, meta::neg>>>::value>> { static T get(lua_State* L, int index, record& tracking) { @@ -268,17 +273,7 @@ namespace sol { tracking.use(1); std::size_t len; auto str = lua_tolstring(L, index, &len); - return std::string( str, len ); - } - }; - - template <> - struct getter { - string_detail::string_shim get(lua_State* L, int index, record& tracking) { - tracking.use(1); - size_t len; - const char* p = lua_tolstring(L, index, &len); - return string_detail::string_shim(p, len); + return std::string(str, len); } }; @@ -286,10 +281,11 @@ namespace sol { struct getter { static const char* get(lua_State* L, int index, record& tracking) { tracking.use(1); - return lua_tostring(L, index); + size_t sz; + return lua_tolstring(L, index, &sz); } }; - + template<> struct getter { static char get(lua_State* L, int index, record& tracking) { @@ -317,7 +313,7 @@ namespace sol { // https://sourceforge.net/p/mingw-w64/bugs/538/ // http://chat.stackoverflow.com/transcript/message/32271369#32271369 for (auto& c : r) { - uint8_t* b = reinterpret_cast(&c); + uint8_t* b = reinterpret_cast(&c); std::swap(b[0], b[1]); } #endif @@ -498,7 +494,7 @@ namespace sol { T* obj = static_cast(udata); return obj; } - + static T& get(lua_State* L, int index, record& tracking) { return *get_no_lua_nil(L, index, tracking); } @@ -573,25 +569,25 @@ namespace sol { } }; - template - struct getter> { - typedef std::tuple(nullptr, 0))...> R; - - template - static R apply(std::index_sequence<>, lua_State*, int, record&, TArgs&&... args) { + template + struct getter> { + typedef std::tuple(nullptr, 0))...> R; + + template + static R apply(std::index_sequence<>, lua_State*, int, record&, Args&&... args) { // Fuck you too, VC++ - return R{std::forward(args)...}; + return R{ std::forward(args)... }; } - - template - static R apply(std::index_sequence, lua_State* L, int index, record& tracking, TArgs&&... args) { + + template + static R apply(std::index_sequence, lua_State* L, int index, record& tracking, Args&&... args) { // Fuck you too, VC++ - typedef std::tuple_element_t> T; - return apply(std::index_sequence(), L, index, tracking, std::forward(args)..., stack::get(L, index + tracking.used, tracking)); + typedef std::tuple_element_t> T; + return apply(std::index_sequence(), L, index, tracking, std::forward(args)..., stack::get(L, index + tracking.used, tracking)); } static R get(lua_State* L, int index, record& tracking) { - return apply(std::make_index_sequence(), L, index, tracking); + return apply(std::make_index_sequence(), L, index, tracking); } }; @@ -601,6 +597,65 @@ namespace sol { return std::pair(L, index)), decltype(stack::get(L, index))>{stack::get(L, index, tracking), stack::get(L, index + tracking.used, tracking)}; } }; + +#ifdef SOL_CXX17_FEATURES + template<> + struct getter { + static std::string_view get(lua_State* L, int index, record& tracking) { + tracking.use(1); + size_t sz; + const char* str = lua_tolstring(L, index, &sz); + return std::string_view(str, sz); + } + }; + + template + struct getter> { + typedef std::variant V; + typedef std::variant_size V_size; + typedef std::integral_constant V_is_empty; + + static V get_empty(std::true_type, lua_State* L, int index, record& tracking) { + return V(); + } + + static V get_empty(std::false_type, lua_State* L, int index, record& tracking) { + typedef std::variant_alternative_t<0, V> T; + // This should never be reached... + // please check your code and understand what you did to bring yourself here + std::abort(); + return V(std::in_place_index<0>, stack::get(L, index)); + } + + static V get_one(std::integral_constant, lua_State* L, int index, record& tracking) { + return get_empty(V_is_empty(), L, index, tracking); + } + + template + static V get_one(std::integral_constant, lua_State* L, int index, record& tracking) { + typedef std::variant_alternative_t T; + if (stack::check(L, index, no_panic, tracking)) { + return V(std::in_place_index, stack::get(L, index)); + } + return get_one(std::integral_constant(), L, index, tracking); + } + + static V get(lua_State* L, int index, record& tracking) { + return get_one(std::integral_constant(), L, index, tracking); + } + }; +#else + template <> + struct getter { + string_detail::string_shim get(lua_State* L, int index, record& tracking) { + tracking.use(1); + size_t len; + const char* p = lua_tolstring(L, index, &len); + return string_detail::string_shim(p, len); + } + }; +#endif // C++17-wave + } // stack } // sol diff --git a/sol/stack_proxy.hpp b/sol/stack_proxy.hpp index 022fdc71..498d8def 100644 --- a/sol/stack_proxy.hpp +++ b/sol/stack_proxy.hpp @@ -42,6 +42,10 @@ namespace sol { return stack::get(L, stack_index()); } + type get_type() const noexcept { + return type_of(lua_state(), stack_index()); + } + int push() const { return push(L); } diff --git a/sol/stack_push.hpp b/sol/stack_push.hpp index a7d690d3..3c0294d5 100644 --- a/sol/stack_push.hpp +++ b/sol/stack_push.hpp @@ -25,12 +25,17 @@ #include "stack_core.hpp" #include "raii.hpp" #include "optional.hpp" +#include "usertype_traits.hpp" #include #include #ifdef SOL_CODECVT_SUPPORT #include #include #endif +#ifdef SOL_CXX17_FEATURES +#include +#include +#endif // C++17 namespace sol { namespace stack { @@ -736,6 +741,63 @@ namespace sol { return 1; } }; + +#ifdef SOL_CXX17_FEATURES + template <> + struct pusher { + static int push(lua_State* L, const std::string_view& sv) { + return stack::push(L, sv.data(), sv.length()); + } + }; +#ifdef SOL_CODECVT_SUPPORT + template <> + struct pusher { + static int push(lua_State* L, const std::wstring_view& sv) { + return stack::push(L, sv.data(), sv.length()); + } + }; + + template <> + struct pusher { + static int push(lua_State* L, const std::u16string_view& sv) { + return stack::push(L, sv.data(), sv.length()); + } + }; + + template <> + struct pusher { + static int push(lua_State* L, const std::u32string_view& sv) { + return stack::push(L, sv.data(), sv.length()); + } + }; +#endif // codecvt header support + + namespace stack_detail { + + struct push_function { + lua_State* L; + + push_function(lua_State* L) : L(L) {} + + template + int operator()(T&& value) const { + return stack::push(L, std::forward(value)); + } + }; + + } // stack_detail + + template + struct pusher> { + static int push(lua_State* L, const std::variant& v) { + return std::visit(stack_detail::push_function(L), v); + } + + static int push(lua_State* L, std::variant&& v) { + return std::visit(stack_detail::push_function(L), std::move(v)); + } + }; +#endif } // stack } // sol diff --git a/sol/state.hpp b/sol/state.hpp index 6df37854..5f439d29 100644 --- a/sol/state.hpp +++ b/sol/state.hpp @@ -47,13 +47,13 @@ namespace sol { optional maybetopmsg = stack::check_get(L, 1); if (maybetopmsg) { const string_detail::string_shim& topmsg = maybetopmsg.value(); - msg.assign(topmsg.c_str(), topmsg.size()); + msg.assign(topmsg.data(), topmsg.size()); } luaL_traceback(L, L, msg.c_str(), 1); optional maybetraceback = stack::check_get(L, -1); if (maybetraceback) { const string_detail::string_shim& traceback = maybetraceback.value(); - msg.assign(traceback.c_str(), traceback.size()); + msg.assign(traceback.data(), traceback.size()); } return stack::push(L, msg); } diff --git a/sol/string_shim.hpp b/sol/string_shim.hpp index c4451583..ed0a41be 100644 --- a/sol/string_shim.hpp +++ b/sol/string_shim.hpp @@ -22,11 +22,18 @@ #ifndef SOL_STRING_SHIM_HPP #define SOL_STRING_SHIM_HPP +#include "feature_test.hpp" #include #include +#ifdef SOL_CXX17_FEATURES +#include +#endif // C++17 features namespace sol { namespace string_detail { +#ifdef SOL_CXX17_FEATURES + typedef std::string_view string_shim; +#else struct string_shim { std::size_t s; const char* p; @@ -82,6 +89,7 @@ namespace sol { return !(*this == r); } }; +#endif // C++17 } } diff --git a/sol/table_core.hpp b/sol/table_core.hpp index 19e0cff6..508ec3bf 100644 --- a/sol/table_core.hpp +++ b/sol/table_core.hpp @@ -27,6 +27,8 @@ #include "function_types.hpp" #include "usertype.hpp" #include "table_iterator.hpp" +#include "types.hpp" +#include "object_base.hpp" namespace sol { namespace detail { diff --git a/sol/traits.hpp b/sol/traits.hpp index 422ac4a4..6b2d58bd 100644 --- a/sol/traits.hpp +++ b/sol/traits.hpp @@ -28,6 +28,9 @@ #include #include #include +#ifdef SOL_CXX17_FEATURES +#include +#endif namespace sol { template @@ -294,6 +297,24 @@ namespace sol { static std::false_type test(...); }; + struct has_value_type_impl { + template, + typename V = typename U::value_type> + static std::true_type test(int); + + template + static std::false_type test(...); + }; + + struct has_iterator_impl { + template, + typename V = typename U::iterator> + static std::true_type test(int); + + template + static std::false_type test(...); + }; + struct has_key_value_pair_impl { template, typename V = typename U::value_type, @@ -339,11 +360,25 @@ namespace sol { template struct has_mapped_type : decltype(meta_detail::has_mapped_type_impl::test(0)) {}; + template + struct has_iterator : decltype(meta_detail::has_iterator_impl::test(0)) {}; + + template + struct has_value_type : decltype(meta_detail::has_value_type_impl::test(0)) {}; + template struct is_associative : meta::all, has_mapped_type> {}; template - using is_string_constructible = any, const char*>, std::is_same, char>, std::is_same, std::string>, std::is_same, std::initializer_list>>; + using is_string_constructible = any< + std::is_same, const char*> + , std::is_same, char> + , std::is_same, std::string> + , std::is_same, std::initializer_list> +#ifdef SOL_CXX17_FEATURES + , std::is_same, std::string_view> +#endif + >; template struct is_pair : std::false_type {}; diff --git a/sol/types.hpp b/sol/types.hpp index 139092a3..88d0f5df 100644 --- a/sol/types.hpp +++ b/sol/types.hpp @@ -22,12 +22,18 @@ #ifndef SOL_TYPES_HPP #define SOL_TYPES_HPP +#include "error.hpp" #include "optional.hpp" #include "compatibility.hpp" +#include "forward.hpp" #include "traits.hpp" #include "string_shim.hpp" #include #include +#ifdef SOL_CXX17_FEATURES +#include +#include +#endif // C++17 namespace sol { namespace detail { @@ -650,64 +656,6 @@ namespace sol { return lua_typename(L, static_cast(t)); } - class reference; - class stack_reference; - template - struct proxy; - template - class usertype; - template - class basic_table_core; - template - using table_core = basic_table_core; - template - using stack_table_core = basic_table_core; - template - using basic_table = basic_table_core; - typedef table_core table; - typedef table_core global_table; - typedef stack_table_core stack_table; - typedef stack_table_core stack_global_table; - template - struct basic_environment; - using environment = basic_environment; - using stack_environment = basic_environment; - template - class basic_function; - template - class basic_protected_function; - using protected_function = basic_protected_function; - using stack_protected_function = basic_protected_function; - using unsafe_function = basic_function; - using safe_function = basic_protected_function; - using stack_unsafe_function = basic_function; - using stack_safe_function = basic_protected_function; -#ifdef SOL_SAFE_FUNCTIONS - using function = protected_function; - using stack_function = stack_protected_function; -#else - using function = unsafe_function; - using stack_function = stack_unsafe_function; -#endif - template - class basic_object; - template - class basic_userdata; - template - class basic_lightuserdata; - struct variadic_args; - using object = basic_object; - using stack_object = basic_object; - using userdata = basic_userdata; - using stack_userdata = basic_userdata; - using lightuserdata = basic_lightuserdata; - using stack_lightuserdata = basic_lightuserdata; - class coroutine; - class thread; - struct variadic_args; - struct this_state; - struct this_environment; - namespace detail { template struct lua_type_of : std::integral_constant {}; @@ -757,9 +705,6 @@ namespace sol { template <> struct lua_type_of : std::integral_constant {}; - template <> - struct lua_type_of : std::integral_constant {}; - template <> struct lua_type_of : std::integral_constant {}; @@ -874,6 +819,29 @@ namespace sol { template struct lua_type_of::value>> : std::integral_constant {}; + template <> + struct lua_type_of : std::integral_constant {}; + +#ifdef SOL_CXX17_FEATURES + 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 {}; +#else + template <> + struct lua_type_of : std::integral_constant {}; +#endif // C++ 17 (or not) features + template struct is_container : std::false_type {}; @@ -889,11 +857,23 @@ namespace sol { template <> struct is_container : std::false_type {}; - template - struct is_container>::value>> : std::true_type {}; +#ifdef SOL_CXX17_FEATURES + template <> + struct is_container : std::false_type {}; template <> - struct lua_type_of : std::integral_constant {}; + struct is_container : std::false_type {}; + + template <> + struct is_container : std::false_type {}; + + template <> + struct is_container : std::false_type {}; + +#endif // C++ 17 + + template + struct is_container>::value>> : std::true_type {}; template class V, typename... Args> struct accumulate : std::integral_constant {}; diff --git a/sol/unsafe_function.hpp b/sol/unsafe_function.hpp new file mode 100644 index 00000000..27d7d8b1 --- /dev/null +++ b/sol/unsafe_function.hpp @@ -0,0 +1,115 @@ +// The MIT License (MIT) + +// Copyright (c) 2013-2017 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_UNSAFE_FUNCTION_HPP +#define SOL_UNSAFE_FUNCTION_HPP + +#include "reference.hpp" +#include "stack.hpp" +#include "function_result.hpp" +#include "function_types.hpp" +#include + +namespace sol { + template + class basic_function : public base_t { + private: + void luacall(std::ptrdiff_t argcount, std::ptrdiff_t resultcount) const { + lua_callk(base_t::lua_state(), static_cast(argcount), static_cast(resultcount), 0, nullptr); + } + + template + auto invoke(types, std::index_sequence, std::ptrdiff_t n) const { + luacall(n, lua_size>::value); + return stack::pop>(base_t::lua_state()); + } + + template + Ret invoke(types, std::index_sequence, std::ptrdiff_t n) const { + luacall(n, lua_size::value); + return stack::pop(base_t::lua_state()); + } + + template + void invoke(types, std::index_sequence, std::ptrdiff_t n) const { + luacall(n, 0); + } + + function_result invoke(types<>, std::index_sequence<>, std::ptrdiff_t n) const { + int stacksize = lua_gettop(base_t::lua_state()); + int firstreturn = (std::max)(1, stacksize - static_cast(n)); + luacall(n, LUA_MULTRET); + int poststacksize = lua_gettop(base_t::lua_state()); + int returncount = poststacksize - (firstreturn - 1); + return function_result(base_t::lua_state(), firstreturn, returncount); + } + + public: + basic_function() = default; + template , basic_function>>, meta::neg>, std::is_base_of>> = meta::enabler> + basic_function(T&& r) noexcept : base_t(std::forward(r)) { +#ifdef SOL_CHECK_ARGUMENTS + if (!is_function>::value) { + auto pp = stack::push_pop(*this); + stack::check(base_t::lua_state(), -1, type_panic); + } +#endif // Safety + } + basic_function(const basic_function&) = default; + basic_function& operator=(const basic_function&) = default; + basic_function(basic_function&&) = default; + basic_function& operator=(basic_function&&) = default; + basic_function(const stack_reference& r) : basic_function(r.lua_state(), r.stack_index()) {} + basic_function(stack_reference&& r) : basic_function(r.lua_state(), r.stack_index()) {} + template >>, meta::neg>> = meta::enabler> + basic_function(lua_State* L, T&& r) : basic_function(L, sol::ref_index(r.registry_index())) {} + basic_function(lua_State* L, int index = -1) : base_t(L, index) { +#ifdef SOL_CHECK_ARGUMENTS + stack::check(L, index, type_panic); +#endif // Safety + } + basic_function(lua_State* L, ref_index index) : base_t(L, index) { +#ifdef SOL_CHECK_ARGUMENTS + auto pp = stack::push_pop(*this); + stack::check(L, -1, type_panic); +#endif // Safety + } + + template + function_result operator()(Args&&... args) const { + return call<>(std::forward(args)...); + } + + template + decltype(auto) operator()(types, Args&&... args) const { + return call(std::forward(args)...); + } + + template + decltype(auto) call(Args&&... args) const { + base_t::push(); + int pushcount = stack::multi_push_reference(base_t::lua_state(), std::forward(args)...); + return invoke(types(), std::make_index_sequence(), pushcount); + } + }; +} // sol + +#endif // SOL_UNSAFE_FUNCTION_HPP diff --git a/sol/usertype.hpp b/sol/usertype.hpp index b0475e11..b7c37df5 100644 --- a/sol/usertype.hpp +++ b/sol/usertype.hpp @@ -82,7 +82,7 @@ namespace sol { template void set(N&& n, F&& f) { auto meta = static_cast*>(base_t::registrar_data()); - meta->add(state, n, f); + meta->add(state, std::forward(n), std::forward(f)); } }; diff --git a/sol/usertype_metatable.hpp b/sol/usertype_metatable.hpp index 86c4526b..720ebfe3 100644 --- a/sol/usertype_metatable.hpp +++ b/sol/usertype_metatable.hpp @@ -31,6 +31,7 @@ #include "inheritance.hpp" #include "raii.hpp" #include "deprecate.hpp" +#include "object.hpp" #include #include @@ -250,7 +251,7 @@ namespace sol { #if 0//def SOL_SAFE_USERTYPE auto maybeaccessor = stack::get>(L, is_index ? -1 : -2); string_detail::string_shim accessor = maybeaccessor.value_or(string_detail::string_shim("(unknown)")); - return luaL_error(L, "sol: attempt to index (get) nil value \"%s\" on userdata (bad (misspelled?) key name or does not exist)", accessor.c_str()); + return luaL_error(L, "sol: attempt to index (get) nil value \"%s\" on userdata (bad (misspelled?) key name or does not exist)", accessor.data()); #else if (is_toplevel(L)) { if (lua_getmetatable(L, 1) == 1) { @@ -266,7 +267,7 @@ namespace sol { else { auto maybeaccessor = stack::get>(L, is_index ? -1 : -2); string_detail::string_shim accessor = maybeaccessor.value_or(string_detail::string_shim("(unknown)")); - return luaL_error(L, "sol: attempt to index (set) nil value \"%s\" on userdata (bad (misspelled?) key name or does not exist)", accessor.c_str()); + return luaL_error(L, "sol: attempt to index (set) nil value \"%s\" on userdata (bad (misspelled?) key name or does not exist)", accessor.data()); } } diff --git a/sol/variadic_args.hpp b/sol/variadic_args.hpp index cafa180b..0cb7c47f 100644 --- a/sol/variadic_args.hpp +++ b/sol/variadic_args.hpp @@ -42,17 +42,27 @@ namespace sol { stack_proxy sp; va_iterator() : L(nullptr), index((std::numeric_limits::max)()), stacktop((std::numeric_limits::max)()) {} + va_iterator(const va_iterator& r) : L(r.L), index(r.index), stacktop(r.stacktop) {} va_iterator(lua_State* luastate, int idx, int topidx) : L(luastate), index(idx), stacktop(topidx), sp(luastate, idx) {} reference operator*() { return stack_proxy(L, index); } + reference operator*() const { + return stack_proxy(L, index); + } + pointer operator->() { sp = stack_proxy(L, index); return &sp; } + pointer operator->() const { + const_cast(sp) = stack_proxy(L, index); + return &sp; + } + va_iterator& operator++ () { ++index; return *this; @@ -95,7 +105,7 @@ namespace sol { return r; } - reference operator[](difference_type idx) { + reference operator[](difference_type idx) const { return stack_proxy(L, index + static_cast(idx)); } @@ -213,6 +223,10 @@ namespace sol { return stack::get(L, index + static_cast(start)); } + type get_type(difference_type start = 0) const noexcept { + return type_of(L, index + static_cast(start)); + } + stack_proxy operator[](difference_type start) const { return stack_proxy(L, index + static_cast(start)); } @@ -220,6 +234,7 @@ namespace sol { lua_State* lua_state() const { return L; }; int stack_index() const { return index; }; int leftover_count() const { return stacktop - (index - 1); } + std::size_t size() const { return static_cast(leftover_count()); } int top() const { return stacktop; } }; diff --git a/sol/variadic_results.hpp b/sol/variadic_results.hpp new file mode 100644 index 00000000..7fe3a3d3 --- /dev/null +++ b/sol/variadic_results.hpp @@ -0,0 +1,51 @@ +// The MIT License (MIT) + +// Copyright (c) 2013-2017 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_VARIADIC_RESULTS_HPP +#define SOL_VARIADIC_RESULTS_HPP + +#include "stack.hpp" +#include "object.hpp" +#include "as_returns.hpp" +#include + +namespace sol { + + struct variadic_results : public std::vector { + using std::vector::vector; + }; + + namespace stack { + template <> + struct pusher { + int push(lua_State* L, const variadic_results& e) { + int p = 0; + for (const auto& i : e) { + p += stack::push(L, i); + } + return p; + } + }; + } // stack + +} // sol + +#endif SOL_VARIADIC_RESULTS_HPP diff --git a/test_containers.cpp b/test_containers.cpp index 1b70d84b..159c6c2f 100644 --- a/test_containers.cpp +++ b/test_containers.cpp @@ -47,7 +47,7 @@ TEST_CASE("containers/returns", "make sure that even references to vectors are b REQUIRE(matching); } -TEST_CASE("containers/vector_roundtrip", "make sure vectors can be round-tripped") { +TEST_CASE("containers/vector roundtrip", "make sure vectors can be round-tripped") { sol::state lua; std::vector v{ 1, 2, 3 }; lua.set_function("f", [&]() -> std::vector& { @@ -59,7 +59,7 @@ TEST_CASE("containers/vector_roundtrip", "make sure vectors can be round-tripped REQUIRE(areequal); } -TEST_CASE("containers/list_roundtrip", "make sure lists can be round-tripped") { +TEST_CASE("containers/list roundtrip", "make sure lists can be round-tripped") { sol::state lua; std::list v{ 1, 2, 3 }; lua.set_function("f", [&]() -> std::list& { @@ -71,7 +71,7 @@ TEST_CASE("containers/list_roundtrip", "make sure lists can be round-tripped") { REQUIRE(areequal); } -TEST_CASE("containers/map_roundtrip", "make sure maps can be round-tripped") { +TEST_CASE("containers/map roundtrip", "make sure maps can be round-tripped") { sol::state lua; std::map v{ { "a", 1 },{ "b", 2 },{ "c", 3 } }; lua.set_function("f", [&]() -> std::map& { @@ -83,7 +83,7 @@ TEST_CASE("containers/map_roundtrip", "make sure maps can be round-tripped") { REQUIRE(areequal); } -TEST_CASE("containers/unordered_map_roundtrip", "make sure unordered_maps can be round-tripped") { +TEST_CASE("containers/unordered_map roundtrip", "make sure unordered_maps can be round-tripped") { sol::state lua; std::unordered_map v{ { "a", 1 },{ "b", 2 },{ "c", 3 } }; lua.set_function("f", [&]() -> std::unordered_map& { @@ -95,7 +95,7 @@ TEST_CASE("containers/unordered_map_roundtrip", "make sure unordered_maps can be REQUIRE(areequal); } -TEST_CASE("containers/unordered_set_roundtrip", "make sure unordered_sets can be round-tripped") { +TEST_CASE("containers/unordered_set roundtrip", "make sure unordered_sets can be round-tripped") { sol::state lua; std::unordered_set v{ 1, 2, 3 }; lua.set_function("f", [&]() -> std::unordered_set& { @@ -107,7 +107,7 @@ TEST_CASE("containers/unordered_set_roundtrip", "make sure unordered_sets can be REQUIRE(areequal); } -TEST_CASE("containers/set_roundtrip", "make sure sets can be round-tripped") { +TEST_CASE("containers/set roundtrip", "make sure sets can be round-tripped") { sol::state lua; std::set v{ 1, 2, 3 }; lua.set_function("f", [&]() -> std::set& { @@ -119,7 +119,7 @@ TEST_CASE("containers/set_roundtrip", "make sure sets can be round-tripped") { REQUIRE(areequal); } -TEST_CASE("containers/custom-usertype", "make sure container usertype metatables can be overridden") { +TEST_CASE("containers/custom usertype", "make sure container usertype metatables can be overridden") { typedef std::unordered_map bark; sol::state lua; @@ -141,7 +141,7 @@ TEST_CASE("containers/custom-usertype", "make sure container usertype metatables REQUIRE_NOTHROW(lua.script("a:something()")); } -TEST_CASE("containers/const-serialization-kvp", "make sure const keys / values are respected") { +TEST_CASE("containers/const serialization kvp", "make sure const keys / values are respected") { typedef std::map bark; sol::state lua; @@ -153,7 +153,7 @@ TEST_CASE("containers/const-serialization-kvp", "make sure const keys / values a REQUIRE_NOTHROW(lua.script("assert(a[24] == 50)")); } -TEST_CASE("containers/basic-serialization", "make sure containers are turned into proper userdata and have basic hooks established") { +TEST_CASE("containers/basic serialization", "make sure containers are turned into proper userdata and have basic hooks established") { typedef std::vector woof; sol::state lua; lua.open_libraries(); @@ -189,7 +189,7 @@ TEST_CASE("containers/const-serialization", "make sure containers are turned int } #endif // Fuck you, glibc -TEST_CASE("containers/table-serialization", "ensure types can be serialized as tables still") { +TEST_CASE("containers/table serialization", "ensure types can be serialized as tables still") { typedef std::vector woof; sol::state lua; lua.open_libraries(); @@ -212,7 +212,7 @@ TEST_CASE("containers/table-serialization", "ensure types can be serialized as t ); } -TEST_CASE("containers/const-correctness", "usertype metatable names should reasonably ignore const attributes") { +TEST_CASE("containers/const correctness", "usertype metatable names should reasonably ignore const attributes") { struct Vec { int x, y, z; }; @@ -247,7 +247,7 @@ end }()); } -TEST_CASE("containers/arbitrary-creation", "userdata and tables should be usable from standard containers") { +TEST_CASE("containers/arbitrary creation", "userdata and tables should be usable from standard containers") { sol::state lua; lua.open_libraries(sol::lib::base); lua.set_function("test_one", test_table_return_one); @@ -286,7 +286,7 @@ TEST_CASE("containers/arbitrary-creation", "userdata and tables should be usable REQUIRE(d.get("four") == 4); } -TEST_CASE("containers/extra-functions", "make sure the manipulation functions are present and usable and working across various container types") { +TEST_CASE("containers/extra functions", "make sure the manipulation functions are present and usable and working across various container types") { sol::state lua; lua.open_libraries(); @@ -386,7 +386,7 @@ c_arr[-1] = 7 } } -TEST_CASE("containers/usertype-transparency", "Make sure containers pass their arguments through transparently and push the results as references, not new values") { +TEST_CASE("containers/usertype transparency", "Make sure containers pass their arguments through transparently and push the results as references, not new values") { class A { public: int a; @@ -458,7 +458,7 @@ namespace sol { struct is_container : std::false_type {}; } -TEST_CASE("containers/is-container", "make sure the is_container trait behaves properly") { +TEST_CASE("containers/is container", "make sure the is_container trait behaves properly") { sol::state lua; lua.open_libraries(); @@ -553,7 +553,7 @@ TEST_CASE("containers/to_args", "Test that the to_args abstractions works") { } -TEST_CASE("containers/ipairs-test", "ensure that abstractions roundtrip properly") { +TEST_CASE("containers/ipairs test", "ensure that abstractions roundtrip properly") { struct thing { int x = 20; }; @@ -585,7 +585,7 @@ end } } -TEST_CASE("containers/append-idiom", "ensure the append-idiom works as intended") { +TEST_CASE("containers/append idiom", "ensure the append-idiom works as intended") { sol::state lua; lua.open_libraries(sol::lib::base); lua.script( diff --git a/test_coroutines.cpp b/test_coroutines.cpp index 1581dc3d..a3276bcd 100644 --- a/test_coroutines.cpp +++ b/test_coroutines.cpp @@ -32,7 +32,7 @@ end REQUIRE(counter == 30); } -TEST_CASE("threading/new-thread-coroutines", "ensure calling a coroutine works when the work is put on a different thread") { +TEST_CASE("threading/new thread coroutines", "ensure calling a coroutine works when the work is put on a different thread") { const auto& script = R"(counter = 20 function loop() diff --git a/test_customizations.cpp b/test_customizations.cpp index dc15a920..9f398e54 100644 --- a/test_customizations.cpp +++ b/test_customizations.cpp @@ -18,6 +18,10 @@ namespace sol { template <> struct lua_size : std::integral_constant {}; + // Then, the expected type + template <> + struct lua_type_of : std::integral_constant {}; + // Now, specialize various stack structures namespace stack { @@ -60,7 +64,7 @@ namespace sol { } } -TEST_CASE("customization/split-struct", "using the newly documented customization points to handle different kinds of classes") { +TEST_CASE("customization/split struct", "using the newly documented customization points to handle different kinds of classes") { sol::state lua; // Create a pass-through style of function diff --git a/test_functions.cpp b/test_functions.cpp index eca8d244..2f502620 100644 --- a/test_functions.cpp +++ b/test_functions.cpp @@ -103,7 +103,7 @@ static int raw_noexcept_function(lua_State* L) noexcept { return sol::stack::push(L, 0x63); } -TEST_CASE("functions/tuple-returns", "Make sure tuple returns are ordered properly") { +TEST_CASE("functions/tuple returns", "Make sure tuple returns are ordered properly") { sol::state lua; lua.script("function f() return '3', 4 end"); @@ -114,7 +114,7 @@ TEST_CASE("functions/tuple-returns", "Make sure tuple returns are ordered proper REQUIRE(v == 4); } -TEST_CASE("functions/overload-resolution", "Check if overloaded function resolution templates compile/work") { +TEST_CASE("functions/overload resolution", "Check if overloaded function resolution templates compile/work") { sol::state lua; lua.open_libraries(sol::lib::base); @@ -143,7 +143,7 @@ TEST_CASE("functions/overload-resolution", "Check if overloaded function resolut REQUIRE_NOTHROW(lua.script("print(overloaded(1, 2, 3))")); } -TEST_CASE("functions/return-order-and-multi-get", "Check if return order is in the same reading order specified in Lua") { +TEST_CASE("functions/return order and multi get", "Check if return order is in the same reading order specified in Lua") { const static std::tuple triple = std::make_tuple(10, 11, 12); const static std::tuple paired = std::make_tuple(10, 10.f); sol::state lua; @@ -165,7 +165,7 @@ TEST_CASE("functions/return-order-and-multi-get", "Check if return order is in t REQUIRE(tcpp2 == paired); } -TEST_CASE("functions/deducing-return-order-and-multi-get", "Check if return order is in the same reading order specified in Lua, with regular deducing calls") { +TEST_CASE("functions/deducing return order and multi get", "Check if return order is in the same reading order specified in Lua, with regular deducing calls") { const static std::tuple triple = std::make_tuple(10, 11, 12); sol::state lua; lua.set_function("f_string", []() { return "this is a string!"; }); @@ -192,7 +192,7 @@ TEST_CASE("functions/deducing-return-order-and-multi-get", "Check if return orde REQUIRE(tluaget == triple); } -TEST_CASE("functions/optional-values", "check if optionals can be passed in to be nil or otherwise") { +TEST_CASE("functions/optional values", "check if optionals can be passed in to be nil or otherwise") { struct thing { int v; }; @@ -213,7 +213,7 @@ end )"); REQUIRE(v->v == 29); } -TEST_CASE("functions/pair-and-tuple-and-proxy-tests", "Check if sol::reference and sol::proxy can be passed to functions as arguments") { +TEST_CASE("functions/pair and tuple and proxy tests", "Check if sol::reference and sol::proxy can be passed to functions as arguments") { sol::state lua; lua.new_usertype("A", "bark", &A::bark); @@ -253,7 +253,7 @@ nested = { variables = { no = { problem = 10 } } } )"); REQUIRE(abc == abcdesired); } -TEST_CASE("functions/sol::function-to-std::function", "check if conversion to std::function works properly and calls with correct arguments") { +TEST_CASE("functions/sol::function to std::function", "check if conversion to std::function works properly and calls with correct arguments") { sol::state lua; lua.open_libraries(sol::lib::base); @@ -272,7 +272,7 @@ TEST_CASE("functions/sol::function-to-std::function", "check if conversion to st )); } -TEST_CASE("functions/returning-functions-from-C++-and-gettin-in-lua", "check to see if returning a functor and getting a functor from lua is possible") { +TEST_CASE("functions/returning functions from C++", "check to see if returning a functor and getting a functor from lua is possible") { sol::state lua; lua.open_libraries(sol::lib::base); @@ -283,7 +283,7 @@ TEST_CASE("functions/returning-functions-from-C++-and-gettin-in-lua", "check to "takefn(afx)\n"); } -TEST_CASE("functions/function_result-protected_function_result", "Function result should be the beefy return type for sol::function that allows for error checking and error handlers") { +TEST_CASE("functions/function_result and protected_function_result", "Function result should be the beefy return type for sol::function that allows for error checking and error handlers") { sol::state lua; lua.open_libraries(sol::lib::base, sol::lib::debug); static const char unhandlederrormessage[] = "true error message"; @@ -392,7 +392,7 @@ TEST_CASE("functions/function_result-protected_function_result", "Function resul } } -TEST_CASE("functions/destructor-tests", "Show that proper copies / destruction happens") { +TEST_CASE("functions/destructor tests", "Show that proper copies / destruction happens") { static int created = 0; static int destroyed = 0; static void* last_call = nullptr; @@ -471,7 +471,7 @@ TEST_CASE("functions/destructor-tests", "Show that proper copies / destruction h } -TEST_CASE("functions/all-kinds", "Register all kinds of functions, make sure they all compile and work") { +TEST_CASE("functions/all kinds", "Register all kinds of functions, make sure they all compile and work") { sol::state lua; struct test_1 { @@ -661,7 +661,7 @@ N = n(1, 2, 3) REQUIRE_THROWS(lua.script("v(nested, inner)")); } -TEST_CASE("simple/call-with-parameters", "Lua function is called with a few parameters from C++") { +TEST_CASE("simple/call with parameters", "Lua function is called with a few parameters from C++") { sol::state lua; REQUIRE_NOTHROW(lua.script("function my_add(i, j, k) return i + j + k end")); @@ -675,7 +675,7 @@ TEST_CASE("simple/call-with-parameters", "Lua function is called with a few para REQUIRE_THROWS(a = f(1, 2, "arf")); } -TEST_CASE("simple/call-c++-function", "C++ function is called from lua") { +TEST_CASE("simple/call c++ function", "C++ function is called from lua") { sol::state lua; lua.set_function("plop_xyz", sep::plop_xyz); @@ -684,7 +684,7 @@ TEST_CASE("simple/call-c++-function", "C++ function is called from lua") { REQUIRE(lua.get("x") == 11); } -TEST_CASE("simple/call-lambda", "A C++ lambda is exposed to lua and called") { +TEST_CASE("simple/call lambda", "A C++ lambda is exposed to lua and called") { sol::state lua; int a = 0; @@ -696,7 +696,7 @@ TEST_CASE("simple/call-lambda", "A C++ lambda is exposed to lua and called") { REQUIRE(a == 1); } -TEST_CASE("advanced/get-and-call", "Checks for lambdas returning values after a get operation") { +TEST_CASE("advanced/get and call", "Checks for lambdas returning values after a get operation") { const static std::string lol = "lol", str = "str"; const static std::tuple heh_tuple = std::make_tuple(1, 6.28f, 3.14, std::string("heh")); sol::state lua; @@ -731,7 +731,7 @@ TEST_CASE("advanced/get-and-call", "Checks for lambdas returning values after a REQUIRE((lua.get("j").call() == heh_tuple)); } -TEST_CASE("advanced/operator[]-call", "Checks for lambdas returning values using operator[]") { +TEST_CASE("advanced/operator[] call", "Checks for lambdas returning values using operator[]") { const static std::string lol = "lol", str = "str"; const static std::tuple heh_tuple = std::make_tuple(1, 6.28f, 3.14, std::string("heh")); sol::state lua; @@ -766,7 +766,7 @@ TEST_CASE("advanced/operator[]-call", "Checks for lambdas returning values using REQUIRE((lua["j"].call() == heh_tuple)); } -TEST_CASE("advanced/call-lambdas", "A C++ lambda is exposed to lua and called") { +TEST_CASE("advanced/call lambdas", "A C++ lambda is exposed to lua and called") { sol::state lua; int x = 0; @@ -779,7 +779,7 @@ TEST_CASE("advanced/call-lambdas", "A C++ lambda is exposed to lua and called") REQUIRE(x == 9); } -TEST_CASE("advanced/call-referenced_obj", "A C++ object is passed by pointer/reference_wrapper to lua and invoked") { +TEST_CASE("advanced/call referenced obj", "A C++ object is passed by pointer/reference_wrapper to lua and invoked") { sol::state lua; int x = 0; @@ -817,51 +817,6 @@ end)"); REQUIRE(c == 3); } -TEST_CASE("functions/variadic_args", "Check to see we can receive multiple arguments through a variadic") { - struct structure { - int x; - bool b; - }; - - sol::state lua; - lua.open_libraries(sol::lib::base); - lua.set_function("v", [](sol::this_state, sol::variadic_args va) -> structure { - int r = 0; - for (auto v : va) { - int value = v; - r += value; - } - return{ r, r > 200 }; - }); - - lua.script("x = v(25, 25)"); - lua.script("x2 = v(25, 25, 100, 50, 250, 150)"); - lua.script("x3 = v(1, 2, 3, 4, 5, 6)"); - - structure& lx = lua["x"]; - structure& lx2 = lua["x2"]; - structure& lx3 = lua["x3"]; - REQUIRE(lx.x == 50); - REQUIRE(lx2.x == 600); - REQUIRE(lx3.x == 21); - REQUIRE_FALSE(lx.b); - REQUIRE(lx2.b); - REQUIRE_FALSE(lx3.b); -} - -TEST_CASE("functions/required_and_variadic_args", "Check if a certain number of arguments can still be required even when using variadic_args") { - sol::state lua; - lua.set_function("v", - [](sol::this_state, sol::variadic_args, int, int) { - } - ); - REQUIRE_NOTHROW(lua.script("v(20, 25, 30)")); - REQUIRE_NOTHROW(lua.script("v(20, 25)")); -#ifndef SOL_LUAJIT - REQUIRE_THROWS(lua.script("v(20)")); -#endif // LuaJIT has problems with exceptions, as fucking usual -} - TEST_CASE("functions/overloading", "Check if overloading works properly for regular set function syntax") { sol::state lua; lua.open_libraries(sol::lib::base); diff --git a/test_storage.cpp b/test_storage.cpp index f2b73aff..00773a35 100644 --- a/test_storage.cpp +++ b/test_storage.cpp @@ -3,7 +3,7 @@ #include #include -TEST_CASE("storage/registry=construction", "ensure entries from the registry can be retrieved") { +TEST_CASE("storage/registry construction", "ensure entries from the registry can be retrieved") { const auto& script = R"( function f() return 2 @@ -23,7 +23,7 @@ end REQUIRE(isequal); } -TEST_CASE("storage/main-thread", "ensure round-tripping and pulling out thread data even on 5.1 with a backup works") { +TEST_CASE("storage/main thread", "ensure round-tripping and pulling out thread data even on 5.1 with a backup works") { sol::state lua; { sol::stack_guard g(lua); diff --git a/test_utility.cpp b/test_utility.cpp new file mode 100644 index 00000000..adf363ef --- /dev/null +++ b/test_utility.cpp @@ -0,0 +1,69 @@ +#define SOL_CHECK_ARGUMENTS + +#include +#include + +#ifdef SOL_CXX17_FEATURES +#include +#include +#endif + + +TEST_CASE("utility/variant", "test that variant can be round-tripped") { +#ifdef SOL_CXX17_FEATURES + SECTION("okay") { + sol::state lua; + lua.open_libraries(sol::lib::base); + + lua.set_function("f", [](int v) { + return v == 2; + }); + lua.set_function("g", [](std::variant vv) { + int v = std::get(vv); + return v == 2; + }); + lua["v"] = std::variant(2); + REQUIRE_NOTHROW([&]() { + lua.script("assert(f(v))"); + lua.script("assert(g(v))"); + }()); + } + SECTION("throws") { + sol::state lua; + lua.open_libraries(sol::lib::base); + + lua.set_function("f", [](int v) { + return v == 2; + }); + lua.set_function("g", [](std::variant vv) { + int v = std::get(vv); + return v == 2; + }); + lua["v"] = std::variant(std::string("bark")); + REQUIRE_THROWS([&]() { + lua.script("assert(f(v))"); + lua.script("assert(g(v))"); + }()); + } +#else + REQUIRE(true); +#endif // C++17 +} + +TEST_CASE("utility/string_view", "test that string_view can be taken as an argument") { +#ifdef SOL_CXX17_FEATURES + sol::state lua; + lua.open_libraries(sol::lib::base); + + lua.set_function("f", [](std::string_view v) { + return v == "bark!"; + }); + lua["v"] = "bark!"; + + REQUIRE_NOTHROW([&]() { + lua.script("assert(f(v))"); + }()); +#else + REQUIRE(true); +#endif // C++17 +} \ No newline at end of file diff --git a/test_variadics.cpp b/test_variadics.cpp new file mode 100644 index 00000000..127b707a --- /dev/null +++ b/test_variadics.cpp @@ -0,0 +1,182 @@ +#define SOL_CHECK_ARGUMENTS + +#include +#include + +#include +#include +#include +#include + +TEST_CASE("variadics/variadic_args", "Check to see we can receive multiple arguments through a variadic") { + struct structure { + int x; + bool b; + }; + + sol::state lua; + lua.open_libraries(sol::lib::base); + lua.set_function("v", [](sol::this_state, sol::variadic_args va) -> structure { + int r = 0; + for (auto v : va) { + int value = v; + r += value; + } + return{ r, r > 200 }; + }); + + lua.script("x = v(25, 25)"); + lua.script("x2 = v(25, 25, 100, 50, 250, 150)"); + lua.script("x3 = v(1, 2, 3, 4, 5, 6)"); + + structure& lx = lua["x"]; + structure& lx2 = lua["x2"]; + structure& lx3 = lua["x3"]; + REQUIRE(lx.x == 50); + REQUIRE(lx2.x == 600); + REQUIRE(lx3.x == 21); + REQUIRE_FALSE(lx.b); + REQUIRE(lx2.b); + REQUIRE_FALSE(lx3.b); +} + +TEST_CASE("variadics/required with variadic_args", "Check if a certain number of arguments can still be required even when using variadic_args") { + sol::state lua; + lua.set_function("v", + [](sol::this_state, sol::variadic_args, int, int) { + } + ); + REQUIRE_NOTHROW(lua.script("v(20, 25, 30)")); + REQUIRE_NOTHROW(lua.script("v(20, 25)")); +#ifndef SOL_LUAJIT + REQUIRE_THROWS(lua.script("v(20)")); +#endif // LuaJIT has problems with exceptions, as fucking usual +} + +TEST_CASE("variadics/variadic_args get type", "Make sure we can inspect types proper from variadic_args") { + sol::state lua; + + lua.set_function("f", [](sol::variadic_args va) { + sol::type types[] = { + sol::type::number, + sol::type::string, + sol::type::boolean + }; + bool working = true; + auto b = va.begin(); + for (std::size_t i = 0; i < va.size(); ++i, ++b) { + sol::type t1 = va.get_type(i); + sol::type t2 = b->get_type(); + working &= types[i] == t1; + working &= types[i] == t2; + } + REQUIRE(working); + }); + + lua.script("f(1, 'bark', true)"); + lua.script("f(2, 'wuf', false)"); +} + +TEST_CASE("variadics/variadic_results", "returning a variable amount of arguments from C++") { + SECTION("as_returns - containers") { + sol::state lua; + + lua.set_function("f", []() { + std::set results{ "arf", "bark", "woof" }; + return sol::as_returns(std::move(results)); + }); + lua.set_function("g", []() { + static const std::deque results{ 25, 82 }; + return sol::as_returns(std::ref(results)); + }); + + REQUIRE_NOTHROW([&]() { + lua.script(R"( + v1, v2, v3 = f() + v4, v5 = g() +)"); + }()); + + std::string v1 = lua["v1"]; + std::string v2 = lua["v2"]; + std::string v3 = lua["v3"]; + int v4 = lua["v4"]; + int v5 = lua["v5"]; + + REQUIRE(v1 == "arf"); + REQUIRE(v2 == "bark"); + REQUIRE(v3 == "woof"); + REQUIRE(v4 == 25); + REQUIRE(v5 == 82); + } + SECTION("variadic_results - variadic_args") { + sol::state lua; + + lua.set_function("f", [](sol::variadic_args args) { + return sol::variadic_results(args.cbegin(), args.cend()); + }); + + REQUIRE_NOTHROW([&]() { + lua.script(R"( + v1, v2, v3 = f(1, 'bark', true) + v4, v5 = f(25, 82) +)"); + }()); + + int v1 = lua["v1"]; + std::string v2 = lua["v2"]; + bool v3 = lua["v3"]; + int v4 = lua["v4"]; + int v5 = lua["v5"]; + + REQUIRE(v1 == 1); + REQUIRE(v2 == "bark"); + REQUIRE(v3); + REQUIRE(v4 == 25); + REQUIRE(v5 == 82); + } + SECTION("variadic_results") { + sol::state lua; + + lua.set_function("f", [](sol::this_state ts, bool maybe) { + if (maybe) { + sol::variadic_results vr; + vr.emplace_back(ts, sol::in_place, 1); + vr.push_back({ ts, sol::in_place, 2 }); + vr.insert(vr.cend(), { ts, sol::in_place, 3 }); + return vr; + } + else { + sol::variadic_results vr; + vr.emplace_back(ts, sol::in_place, "bark"); + vr.push_back({ ts, sol::in_place, "woof" }); + vr.insert(vr.cend(), { ts, sol::in_place, "arf" }); + vr.emplace_back(ts, sol::in_place, "borf"); + return vr; + } + }); + + REQUIRE_NOTHROW([&]() { + lua.script(R"( + v1, v2, v3 = f(true) + v4, v5, v6, v7 = f(false) +)"); + }()); + + int v1 = lua["v1"]; + int v2 = lua["v2"]; + int v3 = lua["v3"]; + std::string v4 = lua["v4"]; + std::string v5 = lua["v5"]; + std::string v6 = lua["v6"]; + std::string v7 = lua["v7"]; + + REQUIRE(v1 == 1); + REQUIRE(v2 == 2); + REQUIRE(v3 == 3); + REQUIRE(v4 == "bark"); + REQUIRE(v5 == "woof"); + REQUIRE(v6 == "arf"); + REQUIRE(v7 == "borf"); + } +}