C++17 additions: std::variant, string_views of all types, checker for if we have the right version

added variadic_results, to return a variable number of arguments to Lua
added variadic_results and as_results
added improved function examples (for multiple results and split overloading out)
added tests for variadics
added tests for C++17 utilities
added a forwarding header
added a specific `unsafe_function` header
added and improved documetation pages
This commit is contained in:
ThePhD 2017-07-09 12:00:57 -04:00
parent 077448bfc8
commit e69e7c79fa
48 changed files with 1742 additions and 335 deletions

View File

@ -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

View File

@ -6,7 +6,7 @@ turn an iterable argument into multiple arguments
.. code-block:: cpp
template <typename T>
as_args_t { ... };
struct as_args_t { ... };
template <typename T>
as_args_t<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

View File

@ -0,0 +1,50 @@
as_returns
==========
turn an iterable argument into a multiple-return type
-----------------------------------------------------
.. code-block:: cpp
template <typename T>
struct as_returns_t { ... };
template <typename T>
as_returns_t<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<variadic_results>`
.. code-block:: cpp
:linenos:
:caption: as_returns.c++
#define SOL_CHECK_ARGUMENTS
#include <sol.hpp>
#include <string>
#include <set>
int main () {
sol::state lua;
lua.set_function("f", []() {
std::set<std::string> 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;
}

View File

@ -0,0 +1,11 @@
variadic_results
================
push multiple disparate arguments into lua
------------------------------------------
.. code-block:: cpp
struct variadic_results : std::vector<object> { ... };
template <typename T>
as_args_t<T> as_args( T&& );

View File

@ -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
---------------------
@ -106,3 +110,4 @@ 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
.. _this issue here: https://github.com/ThePhD/sol2/issues/426

View File

@ -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<tutorial/all-the-things>`
* For a full explanation, :doc:`read the tutorial<tutorial/functions>` 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<raw-function-note>`)
* 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<api/variadic_args>`, for handling variable number of arguments at runtime
- :doc:`sol::this_state<api/this_state>`, for getting the current Lua state
- :doc:`sol::this_environment<api/this_environment>`, for potentially retrieving the current Lua environment
* :doc:`Overload function calls on a single name<api/overload>`, 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<api/nested>`, :doc:`sol::as_table<api/nested>`, :doc:`sol::as_args<api/as_args>` and :doc:`sol::as_function<api/as_function>`
* Set environments for Lua functions and scrips with :doc:`sol::environment<api/environment>`

View File

@ -11,7 +11,7 @@ config
Note that you can obtain safety with regards to functions you bind by using the :doc:`protect<api/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\<T><api/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<api/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.

View File

@ -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<sol::function>("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;
}

View File

@ -0,0 +1,81 @@
#define SOL_CHECK_ARGUMENTS
#include <sol.hpp>
#include <tuple>
#include <cassert>
#include <iostream>
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<sol::function>("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<int> 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<int>, a });
values.push_back({ L, sol::in_place_type<bool>, b });
values.push_back({ L, sol::in_place_type<const char*>, "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;
}

42
examples/overloading.cpp Normal file
View File

@ -0,0 +1,42 @@
#define SOL_CHECK_ARGUMENTS
#include <sol.hpp>
#include <cassert>
#include <iostream>
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;
}

View File

@ -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

View File

@ -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 <typename T>
struct to_args_t {
struct as_args_t {
T src;
};
template <typename Source>
auto as_args(Source&& source) {
return to_args_t<Source>{ std::forward<Source>(source) };
return as_args_t<Source>{ std::forward<Source>(source) };
}
namespace stack {
template <typename T>
struct pusher<to_args_t<T>> {
int push(lua_State* L, const to_args_t<T>& e) {
struct pusher<as_args_t<T>> {
int push(lua_State* L, const as_args_t<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

54
sol/as_returns.hpp Normal file
View File

@ -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 <typename T>
struct as_returns_t {
T src;
};
template <typename Source>
auto as_returns(Source&& source) {
return as_returns_t<std::decay_t<Source>>{ std::forward<Source>(source) };
}
namespace stack {
template <typename T>
struct pusher<as_returns_t<T>> {
int push(lua_State* L, const as_returns_t<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

View File

@ -22,11 +22,523 @@
#ifndef SOL_CONTAINER_TRAITS_HPP
#define SOL_CONTAINER_TRAITS_HPP
#include "traits.hpp"
#include "stack.hpp"
#include <unordered_map>
namespace sol {
namespace container_detail {
template <typename T>
struct has_find {
private:
typedef std::array<char, 1> one;
typedef std::array<char, 2> two;
template <typename C> static one test(decltype(std::declval<C>().find(std::declval<std::add_rvalue_reference_t<typename C::value_type>>()))*);
template <typename C> static two test(...);
public:
static const bool value = sizeof(test<T>(0)) == sizeof(char);
};
template <typename T>
struct has_push_back {
private:
typedef std::array<char, 1> one;
typedef std::array<char, 2> two;
template <typename C> static one test(decltype(std::declval<C>().push_back(std::declval<std::add_rvalue_reference_t<typename C::value_type>>()))*);
template <typename C> static two test(...);
public:
static const bool value = sizeof(test<T>(0)) == sizeof(char);
};
template <typename T>
struct has_clear {
private:
typedef std::array<char, 1> one;
typedef std::array<char, 2> two;
template <typename C> static one test(decltype(&C::clear));
template <typename C> static two test(...);
public:
static const bool value = sizeof(test<T>(0)) == sizeof(char);
};
template <typename T>
struct has_insert {
private:
typedef std::array<char, 1> one;
typedef std::array<char, 2> two;
template <typename C> static one test(decltype(std::declval<C>().insert(std::declval<std::add_rvalue_reference_t<typename C::const_iterator>>(), std::declval<std::add_rvalue_reference_t<typename C::value_type>>()))*);
template <typename C> static two test(...);
public:
static const bool value = sizeof(test<T>(0)) == sizeof(char);
};
template <typename X, typename = void>
struct container_traits_default {
private:
typedef std::remove_pointer_t<meta::unwrap_unqualified_t<X>> 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<X>().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<X>().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<X>().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<X>().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<X>().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<X>().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<X>().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<X>().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<X>().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<X>().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<X>().c_str());
return lua_nil;
}
};
template <typename X>
struct container_traits_default<T, std::enable_if_t<
meta::all<is_container<meta::unqualified<X>>, meta::has_value_type<meta::unqualified<X>>, meta::has_iterator<meta::unqualified<X>>>::value
>> {
private:
typedef std::remove_pointer_t<meta::unwrap_unqualified_t<X>> T;
typedef typename T::iterator iterator;
typedef std::conditional_t<is_associative::value, typename T::value_type, std::pair<std::size_t, typename T::value_type>> KV;
typedef typename KV::first_type K;
typedef typename KV::second_type V;
typedef std::remove_reference_t<decltype(*std::declval<I&>())> iterator_return;
typedef typename meta::iterator_tag<I>::type iterator_category;
typedef std::conditional_t<std::is_same<tag_t, std::input_iterator_tag>::value,
V,
std::conditional_t<is_associative::value,
V,
decltype(*std::declval<I&>())
>
> push_type;
static auto& get_src(lua_State* L) {
#ifdef SOL_SAFE_USERTYPE
auto p = stack::check_get<T*>(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<T>(L, 1);
#endif // Safe getting with error
}
public:
static int delegate_call(lua_State* L) {
static std::unordered_map<std::string, lua_CFunction> 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<std::string>(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<K>(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<push_type>(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<K>(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<push_type>(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<K>(L, 2);
if (!maybek) {
return luaL_error(L, "sol: improper key of type %s for %s", lua_typename(L, static_cast<int>(type_of(L, 2))), detail::demangle<T>().c_str());
}
K& k = *maybek;
#else
K k = stack::get<K>(L, 2);
#endif
using std::end;
auto it = detail::find(src, k);
if (it != end(src)) {
auto& v = *it;
v.second = stack::get<V>(L, 3);
}
else {
src.insert(it, { std::move(k), stack::get<V>(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<K>(L, 2);
if (!maybek) {
return luaL_error(L, "sol: improper key of type %s for %s", lua_typename(L, static_cast<int>(type_of(L, 2))), detail::demangle<T>().c_str());
}
K& k = *maybek;
#else
K k = stack::get<K>(L, 2);
#endif
using std::end;
auto it = detail::find(src, k);
if (it != end(src)) {
auto& v = *it;
v.second = stack::get<V>(L, 3);
}
else {
return luaL_error(L, "sol: cannot insert key of type %s to into %s", lua_typename(L, static_cast<int>(type_of(L, 2))), detail::demangle<T>().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<bool, detail::has_insert<T>::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<K>(L, 2);
if (!maybek) {
return luaL_error(L, "sol: improper index of type %s to a %s", lua_typename(L, static_cast<int>(type_of(L, 2))), detail::demangle<T>().c_str());
}
K& k = *maybek;
#else
K k = stack::get<K>(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<T>().c_str());
}
#endif
--k;
if (k == src.size()) {
real_add_call_push(std::integral_constant<bool, detail::has_push_back<T>::value && std::is_copy_constructible<V>::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<T>().c_str());
}
#endif
std::advance(it, k);
*it = stack::get<V>(L, 3);
return 0;
}
static int real_new_index_call(lua_State* L) {
return real_new_index_call_const(meta::neg<meta::any<std::is_const<V>, std::is_const<IR>, meta::neg<std::is_copy_assignable<V>>>>(), meta::all<is_associative, detail::has_insert<T>>(), L);
}
static int real_pairs_next_call_assoc(std::true_type, lua_State* L) {
using std::end;
iter& i = stack::get<user<iter>>(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<push_type>(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<user<iter>>(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<user<iter>>(L, 1);
auto& source = i.source;
auto& it = i.it;
K k = stack::get<K>(L, 2);
if (it == end(source)) {
return 0;
}
int p;
p = stack::push_reference(L, k + 1);
p += stack::stack_detail::push_reference<push_type>(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<user<iter>>(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<V>(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<T>();
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<V>(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<bool, detail::has_insert<T>::value && std::is_copy_constructible<V>::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<bool, detail::has_push_back<T>::value && std::is_copy_constructible<V>::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<T>();
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<bool, (detail::has_push_back<T>::value || detail::has_insert<T>::value) && std::is_copy_constructible<V>::value>(), L);
}
static int real_insert_call_capable(std::false_type, std::false_type, lua_State*L) {
static const std::string& s = detail::demangle<T>();
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<K>(L, 2)), stack::get<V>(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<bool, detail::has_insert<T>::value && std::is_copy_assignable<V>::value>(), is_associative(), L);
}
static int real_clear_call_capable(std::false_type, lua_State* L) {
static const std::string& s = detail::demangle<T>();
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<bool, detail::has_clear<T>::value>(), L);
}
static int real_find_call_capable(std::false_type, std::false_type, lua_State*L) {
static const std::string& s = detail::demangle<T>();
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<V>(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<push_type>(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<bool, detail::has_find<T>::value>(), is_associative(), L);
}
};
} // container_detail
template <typename T>
struct container_traits : container_detail::container_traits_default<T> {};
} // sol
#endif // SOL_CONTAINER_TRAITS_HPP

View File

@ -174,11 +174,8 @@ namespace sol {
return stack::stack_detail::push_reference<push_type>(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);
@ -194,13 +191,9 @@ namespace sol {
std::advance(it, k);
return stack::stack_detail::push_reference<push_type>(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);
}

View File

@ -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

98
sol/forward.hpp Normal file
View File

@ -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 <typename Table, typename Key>
struct proxy;
template<typename T>
class usertype;
template<typename T>
class simple_usertype;
template <bool, typename T>
class basic_table_core;
template <bool b>
using table_core = basic_table_core<b, reference>;
template <bool b>
using stack_table_core = basic_table_core<b, stack_reference>;
template <typename T>
using basic_table = basic_table_core<false, T>;
typedef table_core<false> table;
typedef table_core<true> global_table;
typedef stack_table_core<false> stack_table;
typedef stack_table_core<true> stack_global_table;
template <typename base_t>
struct basic_environment;
using environment = basic_environment<reference>;
using stack_environment = basic_environment<stack_reference>;
template <typename T>
class basic_function;
template <typename T>
class basic_protected_function;
using protected_function = basic_protected_function<reference>;
using stack_protected_function = basic_protected_function<stack_reference>;
using unsafe_function = basic_function<reference>;
using safe_function = basic_protected_function<reference>;
using stack_unsafe_function = basic_function<stack_reference>;
using stack_safe_function = basic_protected_function<stack_reference>;
#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 <typename base_t>
class basic_object;
template <typename base_t>
class basic_userdata;
template <typename base_t>
class basic_lightuserdata;
struct variadic_args;
using object = basic_object<reference>;
using stack_object = basic_object<stack_reference>;
using userdata = basic_userdata<reference>;
using stack_userdata = basic_userdata<stack_reference>;
using lightuserdata = basic_lightuserdata<reference>;
using stack_lightuserdata = basic_lightuserdata<stack_reference>;
class coroutine;
class thread;
struct variadic_args;
struct variadic_results;
struct this_state;
struct this_environment;
template <typename T>
struct light;
template <typename T>
struct user;
template <typename T>
struct as_args_t;
} // sol
#endif SOL_FORWARD_HPP

View File

@ -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 <cstdint>
#include <functional>
#include <memory>
namespace sol {
template <typename base_t>
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<int>(argcount), static_cast<int>(resultcount), 0, nullptr);
}
template<std::size_t... I, typename... Ret>
auto invoke(types<Ret...>, std::index_sequence<I...>, std::ptrdiff_t n) const {
luacall(n, lua_size<std::tuple<Ret...>>::value);
return stack::pop<std::tuple<Ret...>>(base_t::lua_state());
}
template<std::size_t I, typename Ret>
Ret invoke(types<Ret>, std::index_sequence<I>, std::ptrdiff_t n) const {
luacall(n, lua_size<Ret>::value);
return stack::pop<Ret>(base_t::lua_state());
}
template <std::size_t I>
void invoke(types<void>, std::index_sequence<I>, 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<int>(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 <typename T, meta::enable<meta::neg<std::is_same<meta::unqualified_t<T>, basic_function>>, meta::neg<std::is_same<base_t, stack_reference>>, std::is_base_of<base_t, meta::unqualified_t<T>>> = meta::enabler>
basic_function(T&& r) noexcept : base_t(std::forward<T>(r)) {
#ifdef SOL_CHECK_ARGUMENTS
if (!is_function<meta::unqualified_t<T>>::value) {
auto pp = stack::push_pop(*this);
stack::check<basic_function>(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 <typename T, meta::enable<meta::neg<std::is_integral<meta::unqualified_t<T>>>, meta::neg<std::is_same<T, ref_index>>> = 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<basic_function>(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<basic_function>(L, -1, type_panic);
#endif // Safety
}
template<typename... Args>
function_result operator()(Args&&... args) const {
return call<>(std::forward<Args>(args)...);
}
template<typename... Ret, typename... Args>
decltype(auto) operator()(types<Ret...>, Args&&... args) const {
return call<Ret...>(std::forward<Args>(args)...);
}
template<typename... Ret, typename... Args>
decltype(auto) call(Args&&... args) const {
base_t::push();
int pushcount = stack::multi_push_reference(base_t::lua_state(), std::forward<Args>(args)...);
return invoke(types<Ret...>(), std::make_index_sequence<sizeof...(Ret)>(), pushcount);
}
};
namespace stack {
template<typename Signature>
@ -154,6 +70,7 @@ namespace sol {
}
};
} // stack
} // sol
#endif // SOL_FUNCTION_HPP

View File

@ -22,8 +22,23 @@
#ifndef SOL_IN_PLACE_HPP
#define SOL_IN_PLACE_HPP
#include <cstddef>
#include <utility>
namespace sol {
#ifdef SOL_CXX17_FEATURES
using in_place_t = std::in_place_t;
constexpr std::in_place_t in_place{};
template <typename T> using in_place_type_t = std::in_place_type_t<T>;
template <typename T>
constexpr std::in_place_type_t<T> in_place_type{};
template <size_t I> using in_place_index_t = std::in_place_index_t<I>;
template <size_t I>
constexpr in_place_index_t<I> in_place_index{};
#else
namespace detail {
struct in_place_of {};
template <std::size_t I>
@ -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 <typename T>
constexpr inline in_place_tag in_place(detail::in_place_of_t<T>) { return in_place_tag(in_place_tag::init()); }
constexpr inline in_place_tag in_place (detail::in_place_of_t<T>) { return in_place_tag(); }
template <std::size_t I>
constexpr inline in_place_tag in_place(detail::in_place_of_i<I>) { return in_place_tag(in_place_tag::init()); }
constexpr inline in_place_tag in_place (detail::in_place_of_i<I>) { return in_place_tag(); }
template <typename T>
constexpr inline in_place_tag in_place_type (detail::in_place_of_t<T>) { return in_place_tag(); }
template <std::size_t I>
constexpr inline in_place_tag in_place_index (detail::in_place_of_i<I>) { return in_place_tag(); }
using in_place_t = in_place_tag(&)(detail::in_place_of);
template <typename T>
using in_place_type_t = in_place_tag(&)(detail::in_place_of_t<T>);
template <std::size_t I>
using in_place_index_t = in_place_tag(&)(detail::in_place_of_i<I>);
#endif
} // sol

View File

@ -82,7 +82,7 @@ namespace sol {
template <typename T, typename... Args>
basic_object(lua_State* L, in_place_type_t<T>, Args&&... args) noexcept : basic_object(std::integral_constant<bool, !std::is_base_of<stack_reference, base_t>::value>(), L, -stack::push<T>(L, std::forward<Args>(args)...)) {}
template <typename T, typename... Args>
basic_object(lua_State* L, in_place_t, T&& arg, Args&&... args) noexcept : basic_object(L, in_place<T>, std::forward<T>(arg), std::forward<Args>(args)...) {}
basic_object(lua_State* L, in_place_t, T&& arg, Args&&... args) noexcept : basic_object(L, in_place_type<T>, std::forward<T>(arg), std::forward<Args>(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; }

View File

@ -30,6 +30,8 @@
# ifndef SOL_OPTIONAL_IMPLEMENTATION_HPP
# define SOL_OPTIONAL_IMPLEMENTATION_HPP
#include "in_place.hpp"
# include <utility>
# include <type_traits>
# include <initializer_list>

View File

@ -22,6 +22,7 @@
#ifndef SOL_OVERLOAD_HPP
#define SOL_OVERLOAD_HPP
#include "traits.hpp"
#include <utility>
namespace sol {

View File

@ -22,6 +22,10 @@
#ifndef SOL_PROPERTY_HPP
#define SOL_PROPERTY_HPP
#include "types.hpp"
#include <type_traits>
#include <utility>
namespace sol {
struct no_prop { };

View File

@ -25,6 +25,7 @@
#include "reference.hpp"
#include "stack.hpp"
#include "protected_function_result.hpp"
#include "unsafe_function.hpp"
#include <cstdint>
#include <algorithm>

View File

@ -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<std::is_same<meta::unqualified_t<Table>, 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<typename Table, typename Key, typename T>

View File

@ -66,7 +66,7 @@ namespace sol {
}
}
string_detail::string_shim accessor = stack::get<string_detail::string_shim>(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);

View File

@ -28,6 +28,9 @@
#include <memory>
#include <functional>
#include <utility>
#ifdef SOL_CXX17_FEATURES
#include <variant>
#endif // C++17
namespace sol {
namespace stack {
@ -485,6 +488,40 @@ namespace sol {
return stack::check<T>(L, index, no_panic, tracking);
}
};
#ifdef SOL_CXX17_FEATURES
template<typename... Tn, typename C>
struct checker<std::variant<Tn...>, type::poly, C> {
typedef std::variant<Tn...> V;
typedef std::variant_size<V> V_size;
typedef std::integral_constant<bool, V_size::value == 0> V_is_empty;
template <typename Handler>
static bool is_one(std::integral_constant<std::size_t, 0>, 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 <std::size_t I, typename Handler>
static bool is_one(std::integral_constant<std::size_t, I>, lua_State* L, int index, Handler&& handler, record& tracking) {
typedef std::variant_alternative_t<I - 1, V> T;
if (stack::check<T>(L, index, no_panic, tracking)) {
return true;
}
return is_one(std::integral_constant<std::size_t, I - 1>(), L, index, std::forward<Handler>(handler), tracking);
}
template <typename Handler>
static bool check(lua_State* L, int index, Handler&& handler, record& tracking) {
return is_one(std::integral_constant<std::size_t, V_size::value>(), L, index, std::forward<Handler>(handler), tracking);
}
};
#endif // C++17
} // stack
} // sol

View File

@ -26,6 +26,7 @@
#include "stack_get.hpp"
#include "stack_check.hpp"
#include "optional.hpp"
#include <cstdlib>
namespace sol {
namespace stack {
@ -108,6 +109,48 @@ namespace sol {
return check_get<T>(L, index, no_panic, tracking);
}
};
#ifdef SOL_CXX17_FEATURES
template <typename... Tn>
struct check_getter<std::variant<Tn...>> {
typedef std::variant<Tn...> V;
typedef std::variant_size<V> V_size;
typedef std::integral_constant<bool, V_size::value == 0> V_is_empty;
template <typename Handler>
static optional<V> get_empty(std::true_type, lua_State* L, int index, Handler&& handler, record& tracking) {
return nullopt;
}
template <typename Handler>
static optional<V> 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 <typename Handler>
static optional<V> get_one(std::integral_constant<std::size_t, 0>, lua_State* L, int index, Handler&& handler, record& tracking) {
return get_empty(V_is_empty(), L, index, std::forward<Handler>(handler), tracking);
}
template <std::size_t I, typename Handler>
static optional<V> get_one(std::integral_constant<std::size_t, I>, lua_State* L, int index, Handler&& handler, record& tracking) {
typedef std::variant_alternative_t<I - 1, V> T;
if (stack::check<T>(L, index, no_panic, tracking)) {
return V(std::in_place_index<I - 1>, stack::get<T>(L, index));
}
return get_one(std::integral_constant<std::size_t, I - 1>(), L, index, std::forward<Handler>(handler), tracking);
}
template <typename Handler>
static optional<V> get(lua_State* L, int index, Handler&& handler, record& tracking) {
return get_one(std::integral_constant<std::size_t, V_size::value>(), L, index, std::forward<Handler>(handler), tracking);
}
};
#endif // C++17
} // stack
} // sol

View File

@ -30,10 +30,15 @@
#include <memory>
#include <functional>
#include <utility>
#include <cstdlib>
#ifdef SOL_CODECVT_SUPPORT
#include <codecvt>
#include <locale>
#endif
#endif // codecvt header support
#ifdef SOL_CXX17_FEATURES
#include <string_view>
#include <variant>
#endif // C++17
namespace sol {
namespace stack {
@ -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> {
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,7 +281,8 @@ namespace sol {
struct getter<const char*> {
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);
}
};
@ -573,25 +569,25 @@ namespace sol {
}
};
template<typename... Args>
struct getter<std::tuple<Args...>> {
typedef std::tuple<decltype(stack::get<Args>(nullptr, 0))...> R;
template<typename... Tn>
struct getter<std::tuple<Tn...>> {
typedef std::tuple<decltype(stack::get<Tn>(nullptr, 0))...> R;
template <typename... TArgs>
static R apply(std::index_sequence<>, lua_State*, int, record&, TArgs&&... args) {
template <typename... Args>
static R apply(std::index_sequence<>, lua_State*, int, record&, Args&&... args) {
// Fuck you too, VC++
return R{std::forward<TArgs>(args)...};
return R{ std::forward<Args>(args)... };
}
template <std::size_t I, std::size_t... Ix, typename... TArgs>
static R apply(std::index_sequence<I, Ix...>, lua_State* L, int index, record& tracking, TArgs&&... args) {
template <std::size_t I, std::size_t... Ix, typename... Args>
static R apply(std::index_sequence<I, Ix...>, lua_State* L, int index, record& tracking, Args&&... args) {
// Fuck you too, VC++
typedef std::tuple_element_t<I, std::tuple<Args...>> T;
return apply(std::index_sequence<Ix...>(), L, index, tracking, std::forward<TArgs>(args)..., stack::get<T>(L, index + tracking.used, tracking));
typedef std::tuple_element_t<I, std::tuple<Tn...>> T;
return apply(std::index_sequence<Ix...>(), L, index, tracking, std::forward<Args>(args)..., stack::get<T>(L, index + tracking.used, tracking));
}
static R get(lua_State* L, int index, record& tracking) {
return apply(std::make_index_sequence<sizeof...(Args)>(), L, index, tracking);
return apply(std::make_index_sequence<sizeof...(Tn)>(), L, index, tracking);
}
};
@ -601,6 +597,65 @@ namespace sol {
return std::pair<decltype(stack::get<A>(L, index)), decltype(stack::get<B>(L, index))>{stack::get<A>(L, index, tracking), stack::get<B>(L, index + tracking.used, tracking)};
}
};
#ifdef SOL_CXX17_FEATURES
template<>
struct getter<std::string_view> {
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 <typename... Tn>
struct getter<std::variant<Tn...>> {
typedef std::variant<Tn...> V;
typedef std::variant_size<V> V_size;
typedef std::integral_constant<bool, V_size::value == 0> 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<T>(L, index));
}
static V get_one(std::integral_constant<std::size_t, 0>, lua_State* L, int index, record& tracking) {
return get_empty(V_is_empty(), L, index, tracking);
}
template <std::size_t I>
static V get_one(std::integral_constant<std::size_t, I>, lua_State* L, int index, record& tracking) {
typedef std::variant_alternative_t<I - 1, V> T;
if (stack::check<T>(L, index, no_panic, tracking)) {
return V(std::in_place_index<I - 1>, stack::get<T>(L, index));
}
return get_one(std::integral_constant<std::size_t, I - 1>(), L, index, tracking);
}
static V get(lua_State* L, int index, record& tracking) {
return get_one(std::integral_constant<std::size_t, V_size::value>(), L, index, tracking);
}
};
#else
template <>
struct getter<string_detail::string_shim> {
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

View File

@ -42,6 +42,10 @@ namespace sol {
return stack::get<T>(L, stack_index());
}
type get_type() const noexcept {
return type_of(lua_state(), stack_index());
}
int push() const {
return push(L);
}

View File

@ -25,12 +25,17 @@
#include "stack_core.hpp"
#include "raii.hpp"
#include "optional.hpp"
#include "usertype_traits.hpp"
#include <memory>
#include <type_traits>
#ifdef SOL_CODECVT_SUPPORT
#include <codecvt>
#include <locale>
#endif
#ifdef SOL_CXX17_FEATURES
#include <string_view>
#include <variant>
#endif // C++17
namespace sol {
namespace stack {
@ -736,6 +741,63 @@ namespace sol {
return 1;
}
};
#ifdef SOL_CXX17_FEATURES
template <>
struct pusher<std::string_view> {
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<std::wstring_view> {
static int push(lua_State* L, const std::wstring_view& sv) {
return stack::push(L, sv.data(), sv.length());
}
};
template <>
struct pusher<std::u16string_view> {
static int push(lua_State* L, const std::u16string_view& sv) {
return stack::push(L, sv.data(), sv.length());
}
};
template <>
struct pusher<std::u32string_view> {
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 <typename T>
int operator()(T&& value) const {
return stack::push<T>(L, std::forward<T>(value));
}
};
} // stack_detail
template <typename... Tn>
struct pusher<std::variant<Tn...>> {
static int push(lua_State* L, const std::variant<Tn...>& v) {
return std::visit(stack_detail::push_function(L), v);
}
static int push(lua_State* L, std::variant<Tn...>&& v) {
return std::visit(stack_detail::push_function(L), std::move(v));
}
};
#endif
} // stack
} // sol

View File

@ -47,13 +47,13 @@ namespace sol {
optional<string_detail::string_shim> maybetopmsg = stack::check_get<string_detail::string_shim>(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<string_detail::string_shim> maybetraceback = stack::check_get<string_detail::string_shim>(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);
}

View File

@ -22,11 +22,18 @@
#ifndef SOL_STRING_SHIM_HPP
#define SOL_STRING_SHIM_HPP
#include "feature_test.hpp"
#include <cstddef>
#include <string>
#ifdef SOL_CXX17_FEATURES
#include <string_view>
#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
}
}

View File

@ -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 {

View File

@ -28,6 +28,9 @@
#include <memory>
#include <functional>
#include <iterator>
#ifdef SOL_CXX17_FEATURES
#include <string_view>
#endif
namespace sol {
template<std::size_t I>
@ -294,6 +297,24 @@ namespace sol {
static std::false_type test(...);
};
struct has_value_type_impl {
template<typename T, typename U = unqualified_t<T>,
typename V = typename U::value_type>
static std::true_type test(int);
template<typename...>
static std::false_type test(...);
};
struct has_iterator_impl {
template<typename T, typename U = unqualified_t<T>,
typename V = typename U::iterator>
static std::true_type test(int);
template<typename...>
static std::false_type test(...);
};
struct has_key_value_pair_impl {
template<typename T, typename U = unqualified_t<T>,
typename V = typename U::value_type,
@ -339,11 +360,25 @@ namespace sol {
template<typename T>
struct has_mapped_type : decltype(meta_detail::has_mapped_type_impl::test<T>(0)) {};
template<typename T>
struct has_iterator : decltype(meta_detail::has_iterator_impl::test<T>(0)) {};
template<typename T>
struct has_value_type : decltype(meta_detail::has_value_type_impl::test<T>(0)) {};
template <typename T>
struct is_associative : meta::all<has_key_value_pair<T>, has_mapped_type<T>> {};
template <typename T>
using is_string_constructible = any<std::is_same<unqualified_t<T>, const char*>, std::is_same<unqualified_t<T>, char>, std::is_same<unqualified_t<T>, std::string>, std::is_same<unqualified_t<T>, std::initializer_list<char>>>;
using is_string_constructible = any<
std::is_same<unqualified_t<T>, const char*>
, std::is_same<unqualified_t<T>, char>
, std::is_same<unqualified_t<T>, std::string>
, std::is_same<unqualified_t<T>, std::initializer_list<char>>
#ifdef SOL_CXX17_FEATURES
, std::is_same<unqualified_t<T>, std::string_view>
#endif
>;
template <typename T>
struct is_pair : std::false_type {};

View File

@ -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 <array>
#include <string>
#ifdef SOL_CXX17_FEATURES
#include <string_view>
#include <variant>
#endif // C++17
namespace sol {
namespace detail {
@ -650,64 +656,6 @@ namespace sol {
return lua_typename(L, static_cast<int>(t));
}
class reference;
class stack_reference;
template <typename Table, typename Key>
struct proxy;
template<typename T>
class usertype;
template <bool, typename T>
class basic_table_core;
template <bool b>
using table_core = basic_table_core<b, reference>;
template <bool b>
using stack_table_core = basic_table_core<b, stack_reference>;
template <typename T>
using basic_table = basic_table_core<false, T>;
typedef table_core<false> table;
typedef table_core<true> global_table;
typedef stack_table_core<false> stack_table;
typedef stack_table_core<true> stack_global_table;
template <typename base_t>
struct basic_environment;
using environment = basic_environment<reference>;
using stack_environment = basic_environment<stack_reference>;
template <typename T>
class basic_function;
template <typename T>
class basic_protected_function;
using protected_function = basic_protected_function<reference>;
using stack_protected_function = basic_protected_function<stack_reference>;
using unsafe_function = basic_function<reference>;
using safe_function = basic_protected_function<reference>;
using stack_unsafe_function = basic_function<stack_reference>;
using stack_safe_function = basic_protected_function<stack_reference>;
#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 <typename base_t>
class basic_object;
template <typename base_t>
class basic_userdata;
template <typename base_t>
class basic_lightuserdata;
struct variadic_args;
using object = basic_object<reference>;
using stack_object = basic_object<stack_reference>;
using userdata = basic_userdata<reference>;
using stack_userdata = basic_userdata<stack_reference>;
using lightuserdata = basic_lightuserdata<reference>;
using stack_lightuserdata = basic_lightuserdata<stack_reference>;
class coroutine;
class thread;
struct variadic_args;
struct this_state;
struct this_environment;
namespace detail {
template <typename T, typename = void>
struct lua_type_of : std::integral_constant<type, type::userdata> {};
@ -757,9 +705,6 @@ namespace sol {
template <>
struct lua_type_of<const char32_t*> : std::integral_constant<type, type::string> {};
template <>
struct lua_type_of<string_detail::string_shim> : std::integral_constant<type, type::string> {};
template <>
struct lua_type_of<bool> : std::integral_constant<type, type::boolean> {};
@ -874,6 +819,29 @@ namespace sol {
template <typename T>
struct lua_type_of<T, std::enable_if_t<std::is_enum<T>::value>> : std::integral_constant<type, type::number> {};
template <>
struct lua_type_of<meta_function> : std::integral_constant<type, type::string> {};
#ifdef SOL_CXX17_FEATURES
template <>
struct lua_type_of<std::string_view> : std::integral_constant<type, type::string> {};
template <>
struct lua_type_of<std::wstring_view> : std::integral_constant<type, type::string> {};
template <>
struct lua_type_of<std::u16string_view> : std::integral_constant<type, type::string> {};
template <>
struct lua_type_of<std::u32string_view> : std::integral_constant<type, type::string> {};
template <typename... Tn>
struct lua_type_of<std::variant<Tn...>> : std::integral_constant<type, type::poly> {};
#else
template <>
struct lua_type_of<string_detail::string_shim> : std::integral_constant<type, type::string> {};
#endif // C++ 17 (or not) features
template <typename T, typename C = void>
struct is_container : std::false_type {};
@ -889,11 +857,23 @@ namespace sol {
template <>
struct is_container<std::u32string> : std::false_type {};
template <typename T>
struct is_container<T, std::enable_if_t<meta::has_begin_end<meta::unqualified_t<T>>::value>> : std::true_type {};
#ifdef SOL_CXX17_FEATURES
template <>
struct is_container<std::string_view> : std::false_type {};
template <>
struct lua_type_of<meta_function> : std::integral_constant<type, type::string> {};
struct is_container<std::wstring_view> : std::false_type {};
template <>
struct is_container<std::u16string_view> : std::false_type {};
template <>
struct is_container<std::u32string_view> : std::false_type {};
#endif // C++ 17
template <typename T>
struct is_container<T, std::enable_if_t<meta::has_begin_end<meta::unqualified_t<T>>::value>> : std::true_type {};
template <typename C, C v, template <typename...> class V, typename... Args>
struct accumulate : std::integral_constant<C, v> {};

115
sol/unsafe_function.hpp Normal file
View File

@ -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 <cstdint>
namespace sol {
template <typename base_t>
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<int>(argcount), static_cast<int>(resultcount), 0, nullptr);
}
template<std::size_t... I, typename... Ret>
auto invoke(types<Ret...>, std::index_sequence<I...>, std::ptrdiff_t n) const {
luacall(n, lua_size<std::tuple<Ret...>>::value);
return stack::pop<std::tuple<Ret...>>(base_t::lua_state());
}
template<std::size_t I, typename Ret>
Ret invoke(types<Ret>, std::index_sequence<I>, std::ptrdiff_t n) const {
luacall(n, lua_size<Ret>::value);
return stack::pop<Ret>(base_t::lua_state());
}
template <std::size_t I>
void invoke(types<void>, std::index_sequence<I>, 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<int>(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 <typename T, meta::enable<meta::neg<std::is_same<meta::unqualified_t<T>, basic_function>>, meta::neg<std::is_same<base_t, stack_reference>>, std::is_base_of<base_t, meta::unqualified_t<T>>> = meta::enabler>
basic_function(T&& r) noexcept : base_t(std::forward<T>(r)) {
#ifdef SOL_CHECK_ARGUMENTS
if (!is_function<meta::unqualified_t<T>>::value) {
auto pp = stack::push_pop(*this);
stack::check<basic_function>(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 <typename T, meta::enable<meta::neg<std::is_integral<meta::unqualified_t<T>>>, meta::neg<std::is_same<T, ref_index>>> = 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<basic_function>(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<basic_function>(L, -1, type_panic);
#endif // Safety
}
template<typename... Args>
function_result operator()(Args&&... args) const {
return call<>(std::forward<Args>(args)...);
}
template<typename... Ret, typename... Args>
decltype(auto) operator()(types<Ret...>, Args&&... args) const {
return call<Ret...>(std::forward<Args>(args)...);
}
template<typename... Ret, typename... Args>
decltype(auto) call(Args&&... args) const {
base_t::push();
int pushcount = stack::multi_push_reference(base_t::lua_state(), std::forward<Args>(args)...);
return invoke(types<Ret...>(), std::make_index_sequence<sizeof...(Ret)>(), pushcount);
}
};
} // sol
#endif // SOL_UNSAFE_FUNCTION_HPP

View File

@ -82,7 +82,7 @@ namespace sol {
template <typename N, typename F>
void set(N&& n, F&& f) {
auto meta = static_cast<simple_usertype_metatable<T>*>(base_t::registrar_data());
meta->add(state, n, f);
meta->add(state, std::forward<N>(n), std::forward<F>(f));
}
};

View File

@ -31,6 +31,7 @@
#include "inheritance.hpp"
#include "raii.hpp"
#include "deprecate.hpp"
#include "object.hpp"
#include <unordered_map>
#include <cstdio>
@ -250,7 +251,7 @@ namespace sol {
#if 0//def SOL_SAFE_USERTYPE
auto maybeaccessor = stack::get<optional<string_detail::string_shim>>(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<optional<string_detail::string_shim>>(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());
}
}

View File

@ -42,17 +42,27 @@ namespace sol {
stack_proxy sp;
va_iterator() : L(nullptr), index((std::numeric_limits<int>::max)()), stacktop((std::numeric_limits<int>::max)()) {}
va_iterator(const va_iterator<true>& 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<stack_proxy&>(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<int>(idx));
}
@ -213,6 +223,10 @@ namespace sol {
return stack::get<T>(L, index + static_cast<int>(start));
}
type get_type(difference_type start = 0) const noexcept {
return type_of(L, index + static_cast<int>(start));
}
stack_proxy operator[](difference_type start) const {
return stack_proxy(L, index + static_cast<int>(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<std::size_t>(leftover_count()); }
int top() const { return stacktop; }
};

51
sol/variadic_results.hpp Normal file
View File

@ -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 <vector>
namespace sol {
struct variadic_results : public std::vector<object> {
using std::vector<object>::vector;
};
namespace stack {
template <>
struct pusher<variadic_results> {
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

View File

@ -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<int> v{ 1, 2, 3 };
lua.set_function("f", [&]() -> std::vector<int>& {
@ -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<int> v{ 1, 2, 3 };
lua.set_function("f", [&]() -> std::list<int>& {
@ -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<std::string, int> v{ { "a", 1 },{ "b", 2 },{ "c", 3 } };
lua.set_function("f", [&]() -> std::map<std::string, int>& {
@ -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<std::string, int> v{ { "a", 1 },{ "b", 2 },{ "c", 3 } };
lua.set_function("f", [&]() -> std::unordered_map<std::string, int>& {
@ -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<int> v{ 1, 2, 3 };
lua.set_function("f", [&]() -> std::unordered_set<int>& {
@ -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<int> v{ 1, 2, 3 };
lua.set_function("f", [&]() -> std::set<int>& {
@ -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<int, int> 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<int, const int> 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<int> 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<int> 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<int>("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<options> : 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(

View File

@ -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()

View File

@ -18,6 +18,10 @@ namespace sol {
template <>
struct lua_size<two_things> : std::integral_constant<int, 2> {};
// Then, the expected type
template <>
struct lua_type_of<two_things> : std::integral_constant<sol::type, sol::type::poly> {};
// 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

View File

@ -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<int, int, int> triple = std::make_tuple(10, 11, 12);
const static std::tuple<int, float> 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<int, int, int> 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>("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<int>("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<int, float, double, std::string> 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<sol::function>("j").call<int, float, double, std::string>() == 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<int, float, double, std::string> 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<int, float, double, std::string>() == 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);

View File

@ -3,7 +3,7 @@
#include <catch.hpp>
#include <sol.hpp>
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);

69
test_utility.cpp Normal file
View File

@ -0,0 +1,69 @@
#define SOL_CHECK_ARGUMENTS
#include <catch.hpp>
#include <sol.hpp>
#ifdef SOL_CXX17_FEATURES
#include <string_view>
#include <variant>
#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<float, int, std::string> vv) {
int v = std::get<int>(vv);
return v == 2;
});
lua["v"] = std::variant<float, int, std::string>(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<float, int, std::string> vv) {
int v = std::get<int>(vv);
return v == 2;
});
lua["v"] = std::variant<float, int, std::string>(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
}

182
test_variadics.cpp Normal file
View File

@ -0,0 +1,182 @@
#define SOL_CHECK_ARGUMENTS
#include <catch.hpp>
#include <sol.hpp>
#include <deque>
#include <set>
#include <functional>
#include <string>
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<std::string> results{ "arf", "bark", "woof" };
return sol::as_returns(std::move(results));
});
lua.set_function("g", []() {
static const std::deque<int> 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<int>, 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<const char*>, "bark");
vr.push_back({ ts, sol::in_place<const char*>, "woof" });
vr.insert(vr.cend(), { ts, sol::in_place<const char*>, "arf" });
vr.emplace_back(ts, sol::in_place<const char*>, "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");
}
}