mirror of
https://github.com/ThePhD/sol2.git
synced 2024-03-22 13:10:44 +08:00
Several improvements to tunnneling for the library, with included tests
Some stack size tests are also included to prevent stack overflow as well.
This commit is contained in:
parent
17c8f8e04d
commit
1293213775
|
@ -50,7 +50,7 @@ args = parser.parse_args()
|
|||
# general variables
|
||||
include = [ '.', os.path.join('Catch', 'include')]
|
||||
depends = []
|
||||
cxxflags = [ '-Wall', '-Wextra', '-pedantic', '-pedantic-errors', '-std=c++14', '-Wno-unused-variable' ]
|
||||
cxxflags = [ '-Wall', '-Wextra', '-pedantic', '-pedantic-errors', '-std=c++14' ]
|
||||
ldflags = []
|
||||
script_dir = os.path.dirname(os.path.realpath(sys.argv[0]))
|
||||
sol_dir = os.path.join(script_dir, 'sol')
|
||||
|
@ -67,8 +67,8 @@ if args.debug:
|
|||
else:
|
||||
cxxflags.extend(['-DNDEBUG', '-O3'])
|
||||
|
||||
if args.cxx == 'clang++':
|
||||
cxxflags.extend(['-Wno-unused-value', '-Wno-constexpr-not-const'])
|
||||
#if args.cxx == 'clang++':
|
||||
# cxxflags.extend(['-Wno-unused-value', '-Wno-constexpr-not-const'])
|
||||
|
||||
if args.lua_dir:
|
||||
include.extend([os.path.join(args.lua_dir, 'include')])
|
||||
|
|
|
@ -67,7 +67,7 @@ private:
|
|||
luacall(n, LUA_MULTRET);
|
||||
int poststacksize = lua_gettop( lua_state( ) );
|
||||
int returncount = poststacksize - (firstreturn - 1);
|
||||
return function_result( lua_state( ), firstreturn, returncount, returncount, call_error::ok );
|
||||
return function_result( lua_state( ), firstreturn, returncount );
|
||||
}
|
||||
|
||||
public:
|
||||
|
@ -156,7 +156,7 @@ private:
|
|||
luacall(n, 0, h);
|
||||
}
|
||||
|
||||
function_result invoke(types<>, std::index_sequence<>, std::ptrdiff_t n, handler& h) const {
|
||||
protected_function_result invoke(types<>, std::index_sequence<>, std::ptrdiff_t n, handler& h) const {
|
||||
bool handlerpushed = error_handler.valid();
|
||||
int stacksize = lua_gettop(lua_state());
|
||||
int firstreturn = std::max(1, stacksize - static_cast<int>(n) - 1);
|
||||
|
@ -173,12 +173,12 @@ private:
|
|||
h.stackindex = 0;
|
||||
stack::push(lua_state(), error.what());
|
||||
firstreturn = lua_gettop(lua_state());
|
||||
return function_result(lua_state(), firstreturn, 0, 1, call_error::runtime);
|
||||
return protected_function_result(lua_state(), firstreturn, 0, 1, call_error::runtime);
|
||||
}
|
||||
catch (...) {
|
||||
throw;
|
||||
}
|
||||
return function_result(lua_state(), firstreturn + ( handlerpushed ? 0 : 1 ), returncount, returncount, code);
|
||||
return protected_function_result(lua_state(), firstreturn + ( handlerpushed ? 0 : 1 ), returncount, returncount, code);
|
||||
}
|
||||
|
||||
public:
|
||||
|
@ -194,7 +194,7 @@ public:
|
|||
protected_function& operator=( protected_function&& ) = default;
|
||||
|
||||
template<typename... Args>
|
||||
function_result operator()(Args&&... args) const {
|
||||
protected_function_result operator()(Args&&... args) const {
|
||||
return call<>(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
|
@ -297,7 +297,7 @@ struct pusher<function_sig<Sigs...>> {
|
|||
void* userobjdata = static_cast<void*>(userptr);
|
||||
lua_CFunction freefunc = &static_member_function<std::decay_t<decltype(*userptr)>, uFx>::call;
|
||||
|
||||
int upvalues = stack::detail::push_as_upvalues(L, memfxptr);
|
||||
int upvalues = stack::stack_detail::push_as_upvalues(L, memfxptr);
|
||||
upvalues += stack::push(L, userobjdata);
|
||||
|
||||
stack::push(L, freefunc, upvalues);
|
||||
|
@ -308,7 +308,7 @@ struct pusher<function_sig<Sigs...>> {
|
|||
std::decay_t<Fx> target(std::forward<Fx>(fx));
|
||||
lua_CFunction freefunc = &static_function<Fx>::call;
|
||||
|
||||
int upvalues = stack::detail::push_as_upvalues(L, target);
|
||||
int upvalues = stack::stack_detail::push_as_upvalues(L, target);
|
||||
stack::push(L, freefunc, upvalues);
|
||||
}
|
||||
|
||||
|
@ -328,7 +328,7 @@ struct pusher<function_sig<Sigs...>> {
|
|||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
stack::detail::push_userdata<void*>(L, metatablename, userdata);
|
||||
stack::stack_detail::push_userdata<void*>(L, metatablename, userdata);
|
||||
stack::push(L, freefunc, 1);
|
||||
}
|
||||
|
||||
|
|
|
@ -33,17 +33,61 @@ private:
|
|||
lua_State* L;
|
||||
int index;
|
||||
int returncount;
|
||||
int popcount;
|
||||
call_error error;
|
||||
|
||||
public:
|
||||
function_result() = default;
|
||||
function_result(lua_State* L, int index = -1, int returncount = 0, int popcount = 0, call_error error = call_error::ok): L(L), index(index), returncount(returncount), popcount(popcount), error(error) {
|
||||
function_result(lua_State* L, int index = -1, int returncount = 0): L(L), index(index), returncount(returncount) {
|
||||
|
||||
}
|
||||
function_result(const function_result&) = default;
|
||||
function_result& operator=(const function_result&) = default;
|
||||
function_result(function_result&& o) : L(o.L), index(o.index), returncount(o.returncount), error(o.error) {
|
||||
function_result(function_result&& o) : L(o.L), index(o.index), returncount(o.returncount) {
|
||||
// Must be manual, otherwise destructor will screw us
|
||||
// return count being 0 is enough to keep things clean
|
||||
// but will be thorough
|
||||
o.L = nullptr;
|
||||
o.index = 0;
|
||||
o.returncount = 0;
|
||||
}
|
||||
function_result& operator=(function_result&& o) {
|
||||
L = o.L;
|
||||
index = o.index;
|
||||
returncount = o.returncount;
|
||||
// Must be manual, otherwise destructor will screw us
|
||||
// return count being 0 is enough to keep things clean
|
||||
// but will be thorough
|
||||
o.L = nullptr;
|
||||
o.index = 0;
|
||||
o.returncount = 0;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T get() const {
|
||||
return stack::get<T>(L, index);
|
||||
}
|
||||
|
||||
~function_result() {
|
||||
lua_pop(L, returncount);
|
||||
}
|
||||
};
|
||||
|
||||
struct protected_function_result : public proxy_base<function_result> {
|
||||
private:
|
||||
lua_State* L;
|
||||
int index;
|
||||
int returncount;
|
||||
int popcount;
|
||||
call_error error;
|
||||
|
||||
public:
|
||||
protected_function_result() = default;
|
||||
protected_function_result(lua_State* L, int index = -1, int returncount = 0, int popcount = 0, call_error error = call_error::ok): L(L), index(index), returncount(returncount), popcount(popcount), error(error) {
|
||||
|
||||
}
|
||||
protected_function_result(const protected_function_result&) = default;
|
||||
protected_function_result& operator=(const protected_function_result&) = default;
|
||||
protected_function_result(protected_function_result&& o) : L(o.L), index(o.index), returncount(o.returncount), popcount(o.popcount), error(o.error) {
|
||||
// Must be manual, otherwise destructor will screw us
|
||||
// return count being 0 is enough to keep things clean
|
||||
// but will be thorough
|
||||
|
@ -53,10 +97,11 @@ public:
|
|||
o.popcount = 0;
|
||||
o.error = call_error::runtime;
|
||||
}
|
||||
function_result& operator=(function_result&& o) {
|
||||
protected_function_result& operator=(protected_function_result&& o) {
|
||||
L = o.L;
|
||||
index = o.index;
|
||||
returncount = o.returncount;
|
||||
popcount = o.popcount;
|
||||
error = o.error;
|
||||
// Must be manual, otherwise destructor will screw us
|
||||
// return count being 0 is enough to keep things clean
|
||||
|
@ -78,7 +123,7 @@ public:
|
|||
return stack::get<T>(L, index);
|
||||
}
|
||||
|
||||
~function_result() {
|
||||
~protected_function_result() {
|
||||
stack::remove(L, index, popcount);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -31,7 +31,7 @@ struct static_function {
|
|||
typedef function_traits<function_type> traits_type;
|
||||
|
||||
static int call(lua_State* L) {
|
||||
auto udata = stack::detail::get_as_upvalues<function_type*>(L);
|
||||
auto udata = stack::stack_detail::get_as_upvalues<function_type*>(L);
|
||||
function_type* fx = udata.first;
|
||||
int r = stack::typed_call(tuple_types<typename traits_type::return_type>(), typename traits_type::args_type(), fx, L);
|
||||
return r;
|
||||
|
@ -48,8 +48,8 @@ struct static_member_function {
|
|||
typedef function_traits<function_type> traits_type;
|
||||
|
||||
static int call(lua_State* L) {
|
||||
auto memberdata = stack::detail::get_as_upvalues<function_type>(L, 1);
|
||||
auto objdata = stack::detail::get_as_upvalues<T*>(L, memberdata.second);
|
||||
auto memberdata = stack::stack_detail::get_as_upvalues<function_type>(L, 1);
|
||||
auto objdata = stack::stack_detail::get_as_upvalues<T*>(L, memberdata.second);
|
||||
function_type& memfx = memberdata.first;
|
||||
T& item = *objdata.first;
|
||||
auto fx = [&item, &memfx](auto&&... args) -> typename traits_type::return_type { return (item.*memfx)(std::forward<decltype(args)>(args)...); };
|
||||
|
|
|
@ -31,8 +31,19 @@ namespace sol {
|
|||
template<typename Table, typename Key>
|
||||
struct proxy : public proxy_base<proxy<Table, Key>> {
|
||||
private:
|
||||
typedef If<is_specialization_of<Key, std::tuple>, Key, std::tuple<If<std::is_array<Unqualified<Key>>, Key&, Unqualified<Key>>>> key_type;
|
||||
Table tbl;
|
||||
If<std::is_array<Unqualified<Key>>, Key&, Unqualified<Key>> key;
|
||||
key_type key;
|
||||
|
||||
template<typename T, std::size_t... I>
|
||||
decltype(auto) tuple_get(std::index_sequence<I...>) const {
|
||||
return tbl.template traverse_get<T>( std::get<I>(key)... );
|
||||
}
|
||||
|
||||
template<std::size_t... I, typename T>
|
||||
void tuple_set(std::index_sequence<I...>, T&& value) const {
|
||||
tbl.traverse_set( std::get<I>(key)..., std::forward<T>(value) );
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
|
@ -41,7 +52,7 @@ public:
|
|||
|
||||
template<typename T>
|
||||
proxy& set(T&& item) {
|
||||
tbl.set(key, std::forward<T>(item));
|
||||
tuple_set( std::make_index_sequence<std::tuple_size<Unqualified<key_type>>::value>(), std::forward<T>(item) );
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
@ -53,24 +64,23 @@ public:
|
|||
|
||||
template<typename U, EnableIf<Function<Unqualified<U>>> = 0>
|
||||
proxy& operator=(U&& other) {
|
||||
tbl.set_function(key, std::forward<U>(other));
|
||||
return *this;
|
||||
return set_function(std::forward<U>(other));
|
||||
}
|
||||
|
||||
template<typename U, DisableIf<Function<Unqualified<U>>> = 0>
|
||||
proxy& operator=(U&& other) {
|
||||
tbl.set(key, std::forward<U>(other));
|
||||
return *this;
|
||||
return set(std::forward<U>(other));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
decltype(auto) get() const {
|
||||
return tbl.template get<T>( key );
|
||||
return tuple_get<T>( std::make_index_sequence<std::tuple_size<Unqualified<key_type>>::value>() );
|
||||
}
|
||||
|
||||
template <typename K>
|
||||
decltype(auto) operator[](K&& key) const {
|
||||
return get<table>()[std::forward<K>(key)];
|
||||
decltype(auto) operator[](K&& k) const {
|
||||
auto keys = tuplefy(key, std::forward<K>(k));
|
||||
return proxy<Table, decltype(keys)>(tbl, std::move(keys));
|
||||
}
|
||||
|
||||
template<typename... Ret, typename... Args>
|
||||
|
|
|
@ -34,7 +34,7 @@ struct push_pop {
|
|||
};
|
||||
template <typename T>
|
||||
struct push_pop<true, T> {
|
||||
push_pop (T x) {}
|
||||
push_pop (T) {}
|
||||
~push_pop() {}
|
||||
};
|
||||
template <bool top_level = false, typename T>
|
||||
|
|
149
sol/stack.hpp
149
sol/stack.hpp
|
@ -35,6 +35,10 @@
|
|||
|
||||
namespace sol {
|
||||
namespace stack {
|
||||
template<typename T, bool global = false, typename = void>
|
||||
struct field_getter;
|
||||
template<typename T, bool global = false, typename = void>
|
||||
struct field_setter;
|
||||
template<typename T, typename = void>
|
||||
struct getter;
|
||||
template<typename T, typename = void>
|
||||
|
@ -63,8 +67,7 @@ inline int push_args(lua_State*) {
|
|||
template<typename T, typename... Args>
|
||||
inline int push_args(lua_State* L, T&& t, Args&&... args) {
|
||||
int pushcount = push(L, std::forward<T>(t));
|
||||
using swallow = char[];
|
||||
void(swallow{'\0', (pushcount += sol::stack::push(L, std::forward<Args>(args)), '\0')... });
|
||||
void(sol::detail::swallow{(pushcount += sol::stack::push(L, std::forward<Args>(args)), 0)... });
|
||||
return pushcount;
|
||||
}
|
||||
|
||||
|
@ -93,7 +96,27 @@ bool check(lua_State* L, int index) {
|
|||
return check<T>(L, index, handler);
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
template <bool global = false, typename Key>
|
||||
void get_field(lua_State* L, Key&& key) {
|
||||
field_getter<Unqualified<Key>, global>{}.get(L, std::forward<Key>(key));
|
||||
}
|
||||
|
||||
template <bool global = false, typename Key>
|
||||
void get_field(lua_State* L, Key&& key, int tableindex) {
|
||||
field_getter<Unqualified<Key>, global>{}.get(L, std::forward<Key>(key), tableindex);
|
||||
}
|
||||
|
||||
template <bool global = false, typename Key, typename Value>
|
||||
void set_field(lua_State* L, Key&& key, Value&& value) {
|
||||
field_setter<Unqualified<Key>, global>{}.set(L, std::forward<Key>(key), std::forward<Value>(value));
|
||||
}
|
||||
|
||||
template <bool global = false, typename Key, typename Value>
|
||||
void set_field(lua_State* L, Key&& key, Value&& value, int tableindex) {
|
||||
field_setter<Unqualified<Key>, global>{}.set(L, std::forward<Key>(key), std::forward<Value>(value), tableindex);
|
||||
}
|
||||
|
||||
namespace stack_detail {
|
||||
const bool default_check_arguments =
|
||||
#ifdef SOL_CHECK_ARGUMENTS
|
||||
true;
|
||||
|
@ -136,9 +159,8 @@ struct userdata_pusher<T*> {
|
|||
|
||||
template<typename T, std::size_t... I>
|
||||
inline int push_tuple(std::index_sequence<I...>, lua_State* L, T&& tuplen) {
|
||||
using swallow = char[1 + sizeof...(I)];
|
||||
int pushcount = 0;
|
||||
swallow {'\0', (pushcount += sol::stack::push(L, std::get<I>(tuplen)), '\0')... };
|
||||
detail::swallow{(pushcount += sol::stack::push(L, std::get<I>(tuplen)), '\0')... };
|
||||
return pushcount;
|
||||
}
|
||||
|
||||
|
@ -179,7 +201,7 @@ template<typename T, typename Key, typename... Args, EnableIf<std::is_pointer<T>
|
|||
inline int push_userdata(lua_State* L, Key&& metatablekey, Args&&... args) {
|
||||
return push_userdata_pointer<T>(L, std::forward<Key>(metatablekey), std::forward<Args>(args)...);
|
||||
}
|
||||
} // detail
|
||||
} // stack_detail
|
||||
|
||||
template <typename T, type expected, typename>
|
||||
struct checker {
|
||||
|
@ -391,8 +413,8 @@ struct popper {
|
|||
template <typename... Args>
|
||||
struct popper<std::tuple<Args...>> {
|
||||
inline decltype(auto) pop(lua_State* L) {
|
||||
decltype(auto) r = get<std::tuple<Args...>>(L, lua_gettop(L) - sizeof...(Args));
|
||||
lua_pop(L, sizeof...(Args));
|
||||
decltype(auto) r = get<std::tuple<Args...>>(L, lua_gettop(L) - sizeof...(Args) + 1);
|
||||
lua_pop(L, static_cast<int>(sizeof...(Args)));
|
||||
return r;
|
||||
}
|
||||
};
|
||||
|
@ -400,11 +422,11 @@ struct popper<std::tuple<Args...>> {
|
|||
template<typename T, typename>
|
||||
struct pusher {
|
||||
static int push(lua_State* L, T& t) {
|
||||
return detail::push_userdata<T>(L, usertype_traits<T>::metatable, t);
|
||||
return stack_detail::push_userdata<T>(L, usertype_traits<T>::metatable, t);
|
||||
}
|
||||
|
||||
static int push(lua_State* L, T&& t) {
|
||||
return detail::push_userdata<T>(L, usertype_traits<T>::metatable, std::move(t));
|
||||
return stack_detail::push_userdata<T>(L, usertype_traits<T>::metatable, std::move(t));
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -472,7 +494,7 @@ struct pusher<T, std::enable_if_t<std::is_base_of<reference, T>::value>> {
|
|||
template<typename T>
|
||||
struct pusher<T*> {
|
||||
static int push(lua_State* L, T* obj) {
|
||||
return detail::push_userdata<T*>(L, usertype_traits<T*>::metatable, obj);
|
||||
return stack_detail::push_userdata<T*>(L, usertype_traits<T*>::metatable, obj);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -568,11 +590,81 @@ template<typename... Args>
|
|||
struct pusher<std::tuple<Args...>> {
|
||||
template <typename Tuple>
|
||||
static int push(lua_State* L, Tuple&& tuplen) {
|
||||
return detail::push_tuple(std::index_sequence_for<Args...>(), L, std::forward<Tuple>(tuplen));
|
||||
return stack_detail::push_tuple(std::index_sequence_for<Args...>(), L, std::forward<Tuple>(tuplen));
|
||||
}
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
template <typename T, bool, typename>
|
||||
struct field_getter {
|
||||
template <typename Key>
|
||||
void get(lua_State* L, Key&& key, int tableindex = -2) {
|
||||
push( L, std::forward<Key>( key ) );
|
||||
lua_gettable( L, tableindex );
|
||||
}
|
||||
};
|
||||
|
||||
template <typename... Args, bool b, typename C>
|
||||
struct field_getter<std::tuple<Args...>, b, C> {
|
||||
template <std::size_t I0, std::size_t... I, typename Key>
|
||||
void apply(std::index_sequence<I0, I...>, lua_State* L, Key&& key, int tableindex) {
|
||||
get_field<b>(L, std::get<I0>(key), tableindex);
|
||||
detail::swallow{ (get_field(L, std::get<I>(key)), 0)... };
|
||||
reference saved(L, -1);
|
||||
lua_pop(L, static_cast<int>(sizeof...(I) + 1));
|
||||
saved.push();
|
||||
}
|
||||
|
||||
template <typename Key>
|
||||
void get(lua_State* L, Key&& key, int tableindex = -2) {
|
||||
apply(std::index_sequence_for<Args...>(), L, std::forward<Key>(key), tableindex);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct field_getter<T, true, std::enable_if_t<is_c_str<T>::value>> {
|
||||
template <typename Key>
|
||||
void get(lua_State* L, Key&& key, int = -1) {
|
||||
lua_getglobal(L, &key[0]);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct field_getter<T, false, std::enable_if_t<is_c_str<T>::value>> {
|
||||
template <typename Key>
|
||||
void get(lua_State* L, Key&& key, int tableindex = -1) {
|
||||
lua_getfield(L, tableindex, &key[0]);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, bool, typename>
|
||||
struct field_setter {
|
||||
template <typename Key, typename Value>
|
||||
void set(lua_State* L, Key&& key, Value&& value, int tableindex = -3) {
|
||||
push(L, std::forward<Key>(key));
|
||||
push(L, std::forward<Value>(value));
|
||||
lua_settable(L, tableindex);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct field_setter<T, true, std::enable_if_t<is_c_str<T>::value>> {
|
||||
template <typename Key, typename Value>
|
||||
void set(lua_State* L, Key&& key, Value&& value, int = -2) {
|
||||
push(L, std::forward<Value>(value));
|
||||
lua_setglobal(L, &key[0]);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct field_setter<T, false, std::enable_if_t<is_c_str<T>::value>> {
|
||||
template <typename Key, typename Value>
|
||||
void set(lua_State* L, Key&& key, Value&& value, int tableindex = -2) {
|
||||
push(L, std::forward<Value>(value));
|
||||
lua_setfield(L, tableindex, &key[0]);
|
||||
}
|
||||
};
|
||||
|
||||
namespace stack_detail {
|
||||
template<typename T>
|
||||
inline int push_as_upvalues(lua_State* L, T& item) {
|
||||
typedef std::decay_t<T> TValue;
|
||||
|
@ -608,10 +700,7 @@ struct check_arguments {
|
|||
static bool check(types<Arg0, Args...>, std::index_sequence<I0, I...>, lua_State* L, int firstargument) {
|
||||
bool checks = true;
|
||||
stack::check<Arg0>(L, firstargument + I0);
|
||||
using swallow = int[sizeof...(Args)+2];
|
||||
(void)swallow {
|
||||
0, (checks &= stack::check<Args>(L, firstargument + I))..., 0
|
||||
};
|
||||
(void)detail::swallow{(checks &= stack::check<Args>(L, firstargument + I))...};
|
||||
return checks;
|
||||
}
|
||||
|
||||
|
@ -628,28 +717,28 @@ struct check_arguments<false> {
|
|||
}
|
||||
};
|
||||
|
||||
template <bool checkargs = detail::default_check_arguments, std::size_t... I, typename R, typename... Args, typename Fx, typename... FxArgs, typename = std::enable_if_t<!std::is_void<R>::value>>
|
||||
template <bool checkargs = default_check_arguments, std::size_t... I, typename R, typename... Args, typename Fx, typename... FxArgs, typename = std::enable_if_t<!std::is_void<R>::value>>
|
||||
inline R call(types<R>, types<Args...> ta, std::index_sequence<I...> tai, lua_State* L, int start, Fx&& fx, FxArgs&&... args) {
|
||||
const int stacksize = lua_gettop(L);
|
||||
const int firstargument = static_cast<int>(start + stacksize - std::max(sizeof...(Args)-1, static_cast<std::size_t>(0)));
|
||||
|
||||
detail::check_arguments<checkargs>{}.check(ta, tai, L, firstargument);
|
||||
check_arguments<checkargs>{}.check(ta, tai, L, firstargument);
|
||||
|
||||
return fx(std::forward<FxArgs>(args)..., stack::get<Args>(L, firstargument + I)...);
|
||||
}
|
||||
|
||||
template <bool checkargs = detail::default_check_arguments, std::size_t... I, typename... Args, typename Fx, typename... FxArgs>
|
||||
template <bool checkargs = default_check_arguments, std::size_t... I, typename... Args, typename Fx, typename... FxArgs>
|
||||
inline void call(types<void>, types<Args...> ta, std::index_sequence<I...> tai, lua_State* L, int start, Fx&& fx, FxArgs&&... args) {
|
||||
const int stacksize = lua_gettop(L);
|
||||
const int firstargument = static_cast<int>(start + stacksize - std::max(sizeof...(Args)-1, static_cast<std::size_t>(0)));
|
||||
|
||||
bool checks = detail::check_arguments<checkargs>{}.check(ta, tai, L, firstargument);
|
||||
bool checks = check_arguments<checkargs>{}.check(ta, tai, L, firstargument);
|
||||
if ( !checks )
|
||||
throw error("Arguments not of the proper types for this function call");
|
||||
|
||||
fx(std::forward<FxArgs>(args)..., stack::get<Args>(L, firstargument + I)...);
|
||||
}
|
||||
} // detail
|
||||
} // stack_detail
|
||||
|
||||
inline void remove( lua_State* L, int index, int count ) {
|
||||
if ( count < 1 )
|
||||
|
@ -673,29 +762,29 @@ inline void remove( lua_State* L, int index, int count ) {
|
|||
}
|
||||
}
|
||||
|
||||
template <bool check_args = detail::default_check_arguments, typename R, typename... Args, typename Fx, typename... FxArgs, typename = std::enable_if_t<!std::is_void<R>::value>>
|
||||
template <bool check_args = stack_detail::default_check_arguments, typename R, typename... Args, typename Fx, typename... FxArgs, typename = std::enable_if_t<!std::is_void<R>::value>>
|
||||
inline R call(types<R> tr, types<Args...> ta, lua_State* L, int start, Fx&& fx, FxArgs&&... args) {
|
||||
typedef typename types<Args...>::indices args_indices;
|
||||
return detail::call<check_args>(tr, ta, args_indices(), L, start, std::forward<Fx>(fx), std::forward<FxArgs>(args)...);
|
||||
return stack_detail::call<check_args>(tr, ta, args_indices(), L, start, std::forward<Fx>(fx), std::forward<FxArgs>(args)...);
|
||||
}
|
||||
|
||||
template <bool check_args = detail::default_check_arguments, typename R, typename... Args, typename Fx, typename... FxArgs, typename = std::enable_if_t<!std::is_void<R>::value>>
|
||||
template <bool check_args = stack_detail::default_check_arguments, typename R, typename... Args, typename Fx, typename... FxArgs, typename = std::enable_if_t<!std::is_void<R>::value>>
|
||||
inline R call(types<R> tr, types<Args...> ta, lua_State* L, Fx&& fx, FxArgs&&... args) {
|
||||
return call<check_args>(tr, ta, L, 0, std::forward<Fx>(fx), std::forward<FxArgs>(args)...);
|
||||
}
|
||||
|
||||
template <bool check_args = detail::default_check_arguments, typename... Args, typename Fx, typename... FxArgs>
|
||||
template <bool check_args = stack_detail::default_check_arguments, typename... Args, typename Fx, typename... FxArgs>
|
||||
inline void call(types<void> tr, types<Args...> ta, lua_State* L, int start, Fx&& fx, FxArgs&&... args) {
|
||||
typedef typename types<Args...>::indices args_indices;
|
||||
detail::call<check_args>(tr, ta, args_indices(), L, start, std::forward<Fx>(fx), std::forward<FxArgs>(args)...);
|
||||
stack_detail::call<check_args>(tr, ta, args_indices(), L, start, std::forward<Fx>(fx), std::forward<FxArgs>(args)...);
|
||||
}
|
||||
|
||||
template <bool check_args = detail::default_check_arguments, typename... Args, typename Fx, typename... FxArgs>
|
||||
template <bool check_args = stack_detail::default_check_arguments, typename... Args, typename Fx, typename... FxArgs>
|
||||
inline void call(types<void> tr, types<Args...> ta, lua_State* L, Fx&& fx, FxArgs&&... args) {
|
||||
call<check_args>(tr, ta, L, 0, std::forward<Fx>(fx), std::forward<FxArgs>(args)...);
|
||||
}
|
||||
|
||||
template<bool check_args = detail::default_check_arguments, typename... Args, typename Fx>
|
||||
template<bool check_args = stack_detail::default_check_arguments, typename... Args, typename Fx>
|
||||
inline int typed_call(types<void> tr, types<Args...> ta, Fx&& fx, lua_State* L, int start = 0) {
|
||||
call<check_args>(tr, ta, L, start, fx);
|
||||
int nargs = static_cast<int>(sizeof...(Args));
|
||||
|
@ -703,7 +792,7 @@ inline int typed_call(types<void> tr, types<Args...> ta, Fx&& fx, lua_State* L,
|
|||
return 0;
|
||||
}
|
||||
|
||||
template<bool check_args = detail::default_check_arguments, typename... Ret, typename... Args, typename Fx>
|
||||
template<bool check_args = stack_detail::default_check_arguments, typename... Ret, typename... Args, typename Fx>
|
||||
inline int typed_call(types<Ret...>, types<Args...> ta, Fx&& fx, lua_State* L, int start = 0) {
|
||||
decltype(auto) r = call<check_args>(types<ReturnType<Ret...>>(), ta, L, start, fx);
|
||||
int nargs = static_cast<int>(sizeof...(Args));
|
||||
|
|
|
@ -145,8 +145,7 @@ public:
|
|||
}
|
||||
|
||||
template<typename... Args, typename... Keys>
|
||||
auto get(Keys&&... keys) const
|
||||
-> decltype(globals.get<Args...>(std::forward<Keys>(keys)...)) {
|
||||
decltype(auto) get(Keys&&... keys) const {
|
||||
return globals.get<Args...>(std::forward<Keys>(keys)...);
|
||||
}
|
||||
|
||||
|
@ -156,24 +155,15 @@ public:
|
|||
return *this;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
SOL_DEPRECATED state_view& set_userdata(usertype<T>& user) {
|
||||
return set_usertype(user);
|
||||
template<typename T, typename... Keys>
|
||||
decltype(auto) traverse_get(Keys&&... keys) const {
|
||||
return globals.traverse_get<T>(std::forward<Keys>(keys)...);
|
||||
}
|
||||
|
||||
template<typename Key, typename T>
|
||||
SOL_DEPRECATED state_view& set_userdata(Key&& key, usertype<T>& user) {
|
||||
return set_usertype(std::forward<Key>(key), user);
|
||||
}
|
||||
|
||||
template<typename Class, typename... CTor, typename... Args>
|
||||
SOL_DEPRECATED state_view& new_userdata(const std::string& name, Args&&... args) {
|
||||
return new_usertype<Class>(name, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename Class, typename... CArgs, typename... Args>
|
||||
SOL_DEPRECATED state_view& new_userdata(const std::string& name, constructors<CArgs...> ctor, Args&&... args) {
|
||||
return new_usertype(name, std::move(ctor), std::forward<Args>(args)...);
|
||||
template<typename... Args>
|
||||
state_view& traverse_set(Args&&... args) {
|
||||
globals.traverse_set(std::forward<Args>(args)...);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
|
|
|
@ -37,65 +37,52 @@ class table_core : public reference {
|
|||
friend class state;
|
||||
friend class state_view;
|
||||
|
||||
template<typename T, typename Key, EnableIf<Bool<top_level>, is_c_str<Key>> = 0>
|
||||
decltype(auto) single_get( Key&& key ) const {
|
||||
lua_getglobal(lua_state( ), &key[0]);
|
||||
return stack::pop<T>(lua_state());
|
||||
template <typename... Args>
|
||||
using is_global = And<Bool<top_level>, is_c_str<Args>...>;
|
||||
|
||||
template<typename... Ret, std::size_t... I, typename Keys>
|
||||
auto tuple_get( types<Ret...>, std::index_sequence<I...>, Keys&& keys ) const
|
||||
-> decltype(stack::pop<std::tuple<Ret...>>(nullptr)){
|
||||
auto pp = stack::push_popper<is_global<decltype(std::get<I>(keys))...>::value>(*this);
|
||||
int tableindex = lua_gettop(lua_state());
|
||||
detail::swallow{ ( stack::get_field<top_level>(lua_state(), std::get<I>(keys), tableindex), 0)... };
|
||||
return stack::pop<std::tuple<Ret...>>( lua_state() );
|
||||
}
|
||||
|
||||
template<typename T, typename Key, EnableIf<Not<Bool<top_level>>, is_c_str<Key>> = 0>
|
||||
decltype(auto) single_get( Key&& key ) const {
|
||||
auto pp = stack::push_popper(*this);
|
||||
lua_getfield( lua_state( ), -1, &key[0] );
|
||||
return stack::pop<T>( lua_state( ) );
|
||||
}
|
||||
|
||||
template<typename T, typename Key, EnableIf<Not<is_c_str<Key>>> = 0>
|
||||
decltype(auto) single_get( Key&& key ) const {
|
||||
auto pp = stack::push_popper(*this);
|
||||
stack::push( lua_state( ), std::forward<Key>( key ) );
|
||||
lua_gettable( lua_state( ), -2 );
|
||||
return stack::pop<T>( lua_state( ) );
|
||||
}
|
||||
|
||||
template<typename Key, typename Value, EnableIf<Bool<top_level>, is_c_str<Key>> = 0>
|
||||
void single_set( Key&& key, Value&& value ) {
|
||||
stack::push( lua_state( ), std::forward<Value>( value ) );
|
||||
lua_setglobal( lua_state( ), &key[0] );
|
||||
}
|
||||
|
||||
template<typename Key, typename Value, EnableIf<Not<Bool<top_level>>, is_c_str<Key>> = 0>
|
||||
void single_set(Key&& key, Value&& value) {
|
||||
push();
|
||||
stack::push(lua_state(), std::forward<Value>(value));
|
||||
lua_setfield(lua_state(), -2, &key[0]);
|
||||
pop();
|
||||
}
|
||||
|
||||
template<typename Key, typename Value, EnableIf<Not<is_c_str<Key>>> = 0>
|
||||
void single_set(Key&& key, Value&& value) {
|
||||
push();
|
||||
stack::push(lua_state(), std::forward<Key>(key));
|
||||
stack::push(lua_state(), std::forward<Value>(value));
|
||||
lua_settable(lua_state(), -3);
|
||||
pop();
|
||||
}
|
||||
|
||||
template<typename Keys, typename... Ret, std::size_t... I>
|
||||
std::tuple<decltype(stack::get<Ret>(nullptr, 0))...> tuple_get( types<Ret...>, std::index_sequence<I...>, Keys&& keys ) const {
|
||||
typedef std::tuple<decltype(single_get<Ret>(0))...> tup;
|
||||
return tup( single_get<Ret>( std::get<I>( keys ) )... );
|
||||
}
|
||||
|
||||
template<typename Keys, typename Ret, std::size_t I>
|
||||
template<typename Ret, std::size_t I, typename Keys>
|
||||
decltype(auto) tuple_get( types<Ret>, std::index_sequence<I>, Keys&& keys ) const {
|
||||
return single_get<Ret>( std::get<I>( keys ) );
|
||||
auto pp = stack::push_popper<is_global<decltype(std::get<I>(keys))>::value>(*this);
|
||||
stack::get_field<top_level>( lua_state( ), std::get<I>( keys ) );
|
||||
return stack::pop<Ret>( lua_state( ) );
|
||||
}
|
||||
|
||||
template<typename Pairs, std::size_t... I>
|
||||
void tuple_set( std::index_sequence<I...>, Pairs&& pairs ) {
|
||||
using swallow = int[];
|
||||
swallow{ 0, ( single_set(std::get<I * 2>(pairs), std::get<I * 2 + 1>(pairs)) , 0)..., 0 };
|
||||
auto pp = stack::push_popper<is_global<decltype(std::get<I * 2>(pairs))...>::value>(*this);
|
||||
detail::swallow{ (stack::set_field<top_level>(lua_state(), std::get<I * 2>(pairs), std::get<I * 2 + 1>(pairs)), 0)... };
|
||||
}
|
||||
|
||||
template <bool global, typename T, typename Key>
|
||||
decltype(auto) traverse_get_deep( Key&& key ) const {
|
||||
stack::get_field<global>( lua_state( ), std::forward<Key>( key ) );
|
||||
return stack::get<T>( lua_state( ) );
|
||||
}
|
||||
|
||||
template <bool global, typename T, typename Key, typename... Keys>
|
||||
decltype(auto) traverse_get_deep( Key&& key, Keys&&... keys ) const {
|
||||
stack::get_field<global>( lua_state( ), std::forward<Key>( key ) );
|
||||
return traverse_get_deep<false, T>(std::forward<Keys>(keys)...);
|
||||
}
|
||||
|
||||
template <bool global, typename Key, typename Value>
|
||||
void traverse_set_deep( Key&& key, Value&& value ) const {
|
||||
stack::set_field<global>( lua_state( ), std::forward<Key>( key ), std::forward<Value>(value) );
|
||||
}
|
||||
|
||||
template <bool global, typename Key, typename... Keys>
|
||||
void traverse_set_deep( Key&& key, Keys&&... keys ) const {
|
||||
stack::get_field<global>( lua_state( ), std::forward<Key>( key ) );
|
||||
traverse_set_deep<false>(std::forward<Keys>(keys)...);
|
||||
}
|
||||
|
||||
#if SOL_LUA_VERSION < 502
|
||||
|
@ -115,22 +102,27 @@ public:
|
|||
return tuple_get( types<Ret...>( ), std::index_sequence_for<Ret...>( ), std::forward_as_tuple(std::forward<Keys>(keys)...));
|
||||
}
|
||||
|
||||
template <typename T, typename... Keys>
|
||||
decltype(auto) traverse_get( Keys&&... keys ) const {
|
||||
auto pp = stack::push_popper<is_global<Keys...>::value>(*this);
|
||||
struct clean { lua_State* L; clean(lua_State* L) : L(L) {} ~clean() { lua_pop(L, static_cast<int>(sizeof...(Keys))); } } c(lua_state());
|
||||
return traverse_get_deep<top_level, T>(std::forward<Keys>(keys)...);
|
||||
}
|
||||
|
||||
template <typename... Keys>
|
||||
table_core& traverse_set( Keys&&... keys ) {
|
||||
auto pp = stack::push_popper<is_global<Keys...>::value>(*this);
|
||||
traverse_set_deep<top_level>(std::forward<Keys>(keys)...);
|
||||
lua_pop(lua_state(), static_cast<int>(sizeof...(Keys)-2));
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
table_core& set( Args&&... args ) {
|
||||
tuple_set(std::make_index_sequence<sizeof...(Args) / 2>(), std::forward_as_tuple(std::forward<Args>(args)...));
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
SOL_DEPRECATED table_core& set_userdata( usertype<T>& user ) {
|
||||
return set_usertype( user );
|
||||
}
|
||||
|
||||
template<typename Key, typename T>
|
||||
SOL_DEPRECATED table_core& set_userdata( Key&& key, usertype<T>& user ) {
|
||||
return set_usertype(std::forward<Key>(key), user);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
table_core& set_usertype( usertype<T>& user ) {
|
||||
return set_usertype(usertype_traits<T>::name, user);
|
||||
|
|
|
@ -380,6 +380,23 @@ template<typename T>
|
|||
inline T* ptr(T* val) {
|
||||
return val;
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
template <typename T, DisableIf<is_specialization_of<Unqualified<T>, std::tuple>> = 0>
|
||||
decltype(auto) force_tuple(T&& x) {
|
||||
return std::forward_as_tuple(x);
|
||||
}
|
||||
|
||||
template <typename T, EnableIf<is_specialization_of<Unqualified<T>, std::tuple>> = 0>
|
||||
decltype(auto) force_tuple(T&& x) {
|
||||
return std::forward<T>(x);
|
||||
}
|
||||
} // detail
|
||||
|
||||
template <typename... X>
|
||||
decltype(auto) tuplefy(X&&... x ) {
|
||||
return std::tuple_cat(detail::force_tuple(x)...);
|
||||
}
|
||||
} // sol
|
||||
|
||||
#endif // SOL_TRAITS_HPP
|
||||
|
|
|
@ -45,6 +45,8 @@ struct tuple_types_ { typedef types<Args...> type; };
|
|||
template<typename... Args>
|
||||
struct tuple_types_<std::tuple<Args...>> { typedef types<Args...> type; };
|
||||
|
||||
using swallow = std::initializer_list<int>;
|
||||
|
||||
} // detail
|
||||
|
||||
template<typename... Args>
|
||||
|
|
110
tests.cpp
110
tests.cpp
|
@ -5,6 +5,16 @@
|
|||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
struct stack_guard {
|
||||
lua_State* L;
|
||||
int& begintop;
|
||||
int& endtop;
|
||||
stack_guard(lua_State* L, int& begintop, int& endtop) : L(L), begintop(begintop), endtop(endtop) {
|
||||
begintop = lua_gettop(L);
|
||||
}
|
||||
~stack_guard() { endtop = lua_gettop(L); }
|
||||
};
|
||||
|
||||
void test_free_func(std::function<void()> f) {
|
||||
f();
|
||||
}
|
||||
|
@ -214,37 +224,100 @@ struct giver {
|
|||
|
||||
};
|
||||
|
||||
TEST_CASE("table/traversal", "ensure that we can chain requests and tunnel down into a value if we desire") {
|
||||
|
||||
sol::state lua;
|
||||
int begintop = 0, endtop = 0;
|
||||
|
||||
lua.script("t1 = {t2 = {t3 = 24}};");
|
||||
{
|
||||
stack_guard g(lua.lua_state(), begintop, endtop);
|
||||
int traversex24 = lua.traverse_get<int>("t1", "t2", "t3");
|
||||
REQUIRE(traversex24 == 24);
|
||||
} REQUIRE(begintop == endtop);
|
||||
|
||||
{
|
||||
stack_guard g(lua.lua_state(), begintop, endtop);
|
||||
int x24 = lua["t1"]["t2"]["t3"];
|
||||
REQUIRE(x24 == 24);
|
||||
} REQUIRE(begintop == endtop);
|
||||
|
||||
{
|
||||
stack_guard g(lua.lua_state(), begintop, endtop);
|
||||
lua["t1"]["t2"]["t3"] = 64;
|
||||
int traversex64 = lua.traverse_get<int>("t1", "t2", "t3");
|
||||
REQUIRE(traversex64 == 64);
|
||||
} REQUIRE(begintop == endtop);
|
||||
|
||||
{
|
||||
stack_guard g(lua.lua_state(), begintop, endtop);
|
||||
int x64 = lua["t1"]["t2"]["t3"];
|
||||
REQUIRE(x64 == 64);
|
||||
} REQUIRE(begintop == endtop);
|
||||
|
||||
{
|
||||
stack_guard g(lua.lua_state(), begintop, endtop);
|
||||
lua.traverse_set("t1", "t2", "t3", 13);
|
||||
int traversex13 = lua.traverse_get<int>("t1", "t2", "t3");
|
||||
REQUIRE(traversex13 == 13);
|
||||
} REQUIRE(begintop == endtop);
|
||||
|
||||
{
|
||||
stack_guard g(lua.lua_state(), begintop, endtop);
|
||||
int x13 = lua["t1"]["t2"]["t3"];
|
||||
REQUIRE(x13 == 13);
|
||||
} REQUIRE(begintop == endtop);
|
||||
}
|
||||
|
||||
TEST_CASE("simple/set", "Check if the set works properly.") {
|
||||
sol::state lua;
|
||||
|
||||
lua.set("a", 9);
|
||||
int begintop = 0, endtop = 0;
|
||||
{
|
||||
stack_guard g(lua.lua_state(), begintop, endtop);
|
||||
lua.set("a", 9);
|
||||
} REQUIRE(begintop == endtop);
|
||||
REQUIRE_NOTHROW(lua.script("if a ~= 9 then error('wrong value') end"));
|
||||
|
||||
lua.set("d", "hello");
|
||||
{
|
||||
stack_guard g(lua.lua_state(), begintop, endtop);
|
||||
lua.set("d", "hello");
|
||||
} REQUIRE(begintop == endtop);
|
||||
REQUIRE_NOTHROW(lua.script("if d ~= 'hello' then error('expected \\'hello\\', got '.. tostring(d)) end"));
|
||||
|
||||
lua.set("e", std::string("hello"), "f", true);
|
||||
{
|
||||
stack_guard g(lua.lua_state(), begintop, endtop);
|
||||
lua.set("e", std::string("hello"), "f", true);
|
||||
} REQUIRE(begintop == endtop);
|
||||
REQUIRE_NOTHROW(lua.script("if d ~= 'hello' then error('expected \\'hello\\', got '.. tostring(d)) end"));
|
||||
REQUIRE_NOTHROW(lua.script("if f ~= true then error('wrong value') end"));
|
||||
}
|
||||
|
||||
TEST_CASE("simple/get", "Tests if the get function works properly.") {
|
||||
sol::state lua;
|
||||
int begintop = 0, endtop = 0;
|
||||
|
||||
lua.script("a = 9");
|
||||
auto a = lua.get<int>("a");
|
||||
REQUIRE(a == 9.0);
|
||||
{
|
||||
stack_guard g(lua.lua_state(), begintop, endtop);
|
||||
auto a = lua.get<int>("a");
|
||||
REQUIRE(a == 9.0);
|
||||
} REQUIRE(begintop == endtop);
|
||||
|
||||
lua.script("b = nil");
|
||||
REQUIRE_NOTHROW(lua.get<sol::nil_t>("b"));
|
||||
{
|
||||
stack_guard g(lua.lua_state(), begintop, endtop);
|
||||
REQUIRE_NOTHROW(lua.get<sol::nil_t>("b"));
|
||||
} REQUIRE(begintop == endtop);
|
||||
|
||||
lua.script("d = 'hello'");
|
||||
lua.script("e = true");
|
||||
std::string d;
|
||||
bool e;
|
||||
std::tie( d, e ) = lua.get<std::string, bool>("d", "e");
|
||||
REQUIRE(d == "hello");
|
||||
REQUIRE(e == true);
|
||||
{
|
||||
stack_guard g(lua.lua_state(), begintop, endtop);
|
||||
std::string d;
|
||||
bool e;
|
||||
std::tie( d, e ) = lua.get<std::string, bool>("d", "e");
|
||||
REQUIRE(d == "hello");
|
||||
REQUIRE(e == true);
|
||||
} REQUIRE(begintop == endtop);
|
||||
}
|
||||
|
||||
TEST_CASE("simple/set-get-global-integer", "Tests if the get function works properly with global integers") {
|
||||
|
@ -641,8 +714,9 @@ TEST_CASE("tables/operator[]", "Check if operator[] retrieval and setting works
|
|||
// test const table retrieval
|
||||
auto assert1 = [](const sol::table& t) {
|
||||
std::string a = t["foo"];
|
||||
int b = t["bar"];
|
||||
std::cout << a << ',' << b << '\n';
|
||||
double b = t["bar"];
|
||||
REQUIRE(a == "goodbye");
|
||||
REQUIRE(b == 20.4);
|
||||
};
|
||||
|
||||
REQUIRE_NOTHROW(assert1(lua.global()));
|
||||
|
@ -1086,13 +1160,13 @@ TEST_CASE( "functions/function_result-protected_function", "Function result shou
|
|||
func.error_handler = luahandler;
|
||||
luafunc.error_handler = cpphandler;
|
||||
|
||||
sol::function_result result1 = func();
|
||||
sol::protected_function_result result1 = func();
|
||||
int test = lua_gettop(lua.lua_state());
|
||||
REQUIRE(!result1.valid());
|
||||
std::string errorstring = result1;
|
||||
REQUIRE(errorstring == errormessage1);
|
||||
|
||||
sol::function_result result2 = luafunc();
|
||||
sol::protected_function_result result2 = luafunc();
|
||||
REQUIRE(!result2.valid());
|
||||
errorstring = result2;
|
||||
REQUIRE(errorstring == errormessage2);
|
||||
|
@ -1292,4 +1366,4 @@ TEST_CASE("issues/stack-overflow", "make sure various operations repeated don't
|
|||
throw std::logic_error("RIP");
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user