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:
ThePhD 2016-02-13 20:14:31 -05:00
parent 17c8f8e04d
commit 1293213775
12 changed files with 377 additions and 158 deletions

View File

@ -50,7 +50,7 @@ args = parser.parse_args()
# general variables # general variables
include = [ '.', os.path.join('Catch', 'include')] include = [ '.', os.path.join('Catch', 'include')]
depends = [] depends = []
cxxflags = [ '-Wall', '-Wextra', '-pedantic', '-pedantic-errors', '-std=c++14', '-Wno-unused-variable' ] cxxflags = [ '-Wall', '-Wextra', '-pedantic', '-pedantic-errors', '-std=c++14' ]
ldflags = [] ldflags = []
script_dir = os.path.dirname(os.path.realpath(sys.argv[0])) script_dir = os.path.dirname(os.path.realpath(sys.argv[0]))
sol_dir = os.path.join(script_dir, 'sol') sol_dir = os.path.join(script_dir, 'sol')
@ -67,8 +67,8 @@ if args.debug:
else: else:
cxxflags.extend(['-DNDEBUG', '-O3']) cxxflags.extend(['-DNDEBUG', '-O3'])
if args.cxx == 'clang++': #if args.cxx == 'clang++':
cxxflags.extend(['-Wno-unused-value', '-Wno-constexpr-not-const']) # cxxflags.extend(['-Wno-unused-value', '-Wno-constexpr-not-const'])
if args.lua_dir: if args.lua_dir:
include.extend([os.path.join(args.lua_dir, 'include')]) include.extend([os.path.join(args.lua_dir, 'include')])

View File

@ -67,7 +67,7 @@ private:
luacall(n, LUA_MULTRET); luacall(n, LUA_MULTRET);
int poststacksize = lua_gettop( lua_state( ) ); int poststacksize = lua_gettop( lua_state( ) );
int returncount = poststacksize - (firstreturn - 1); int returncount = poststacksize - (firstreturn - 1);
return function_result( lua_state( ), firstreturn, returncount, returncount, call_error::ok ); return function_result( lua_state( ), firstreturn, returncount );
} }
public: public:
@ -156,7 +156,7 @@ private:
luacall(n, 0, h); 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(); bool handlerpushed = error_handler.valid();
int stacksize = lua_gettop(lua_state()); int stacksize = lua_gettop(lua_state());
int firstreturn = std::max(1, stacksize - static_cast<int>(n) - 1); int firstreturn = std::max(1, stacksize - static_cast<int>(n) - 1);
@ -173,12 +173,12 @@ private:
h.stackindex = 0; h.stackindex = 0;
stack::push(lua_state(), error.what()); stack::push(lua_state(), error.what());
firstreturn = lua_gettop(lua_state()); 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 (...) { catch (...) {
throw; 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: public:
@ -194,7 +194,7 @@ public:
protected_function& operator=( protected_function&& ) = default; protected_function& operator=( protected_function&& ) = default;
template<typename... Args> template<typename... Args>
function_result operator()(Args&&... args) const { protected_function_result operator()(Args&&... args) const {
return call<>(std::forward<Args>(args)...); return call<>(std::forward<Args>(args)...);
} }
@ -297,7 +297,7 @@ struct pusher<function_sig<Sigs...>> {
void* userobjdata = static_cast<void*>(userptr); void* userobjdata = static_cast<void*>(userptr);
lua_CFunction freefunc = &static_member_function<std::decay_t<decltype(*userptr)>, uFx>::call; 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); upvalues += stack::push(L, userobjdata);
stack::push(L, freefunc, upvalues); stack::push(L, freefunc, upvalues);
@ -308,7 +308,7 @@ struct pusher<function_sig<Sigs...>> {
std::decay_t<Fx> target(std::forward<Fx>(fx)); std::decay_t<Fx> target(std::forward<Fx>(fx));
lua_CFunction freefunc = &static_function<Fx>::call; 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); stack::push(L, freefunc, upvalues);
} }
@ -328,7 +328,7 @@ struct pusher<function_sig<Sigs...>> {
lua_pop(L, 1); 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); stack::push(L, freefunc, 1);
} }

View File

@ -33,17 +33,61 @@ private:
lua_State* L; lua_State* L;
int index; int index;
int returncount; int returncount;
int popcount;
call_error error;
public: public:
function_result() = default; 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(const function_result&) = default;
function_result& operator=(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 // Must be manual, otherwise destructor will screw us
// return count being 0 is enough to keep things clean // return count being 0 is enough to keep things clean
// but will be thorough // but will be thorough
@ -53,10 +97,11 @@ public:
o.popcount = 0; o.popcount = 0;
o.error = call_error::runtime; o.error = call_error::runtime;
} }
function_result& operator=(function_result&& o) { protected_function_result& operator=(protected_function_result&& o) {
L = o.L; L = o.L;
index = o.index; index = o.index;
returncount = o.returncount; returncount = o.returncount;
popcount = o.popcount;
error = o.error; error = o.error;
// Must be manual, otherwise destructor will screw us // Must be manual, otherwise destructor will screw us
// return count being 0 is enough to keep things clean // return count being 0 is enough to keep things clean
@ -78,7 +123,7 @@ public:
return stack::get<T>(L, index); return stack::get<T>(L, index);
} }
~function_result() { ~protected_function_result() {
stack::remove(L, index, popcount); stack::remove(L, index, popcount);
} }
}; };

View File

@ -31,7 +31,7 @@ struct static_function {
typedef function_traits<function_type> traits_type; typedef function_traits<function_type> traits_type;
static int call(lua_State* L) { 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; function_type* fx = udata.first;
int r = stack::typed_call(tuple_types<typename traits_type::return_type>(), typename traits_type::args_type(), fx, L); int r = stack::typed_call(tuple_types<typename traits_type::return_type>(), typename traits_type::args_type(), fx, L);
return r; return r;
@ -48,8 +48,8 @@ struct static_member_function {
typedef function_traits<function_type> traits_type; typedef function_traits<function_type> traits_type;
static int call(lua_State* L) { static int call(lua_State* L) {
auto memberdata = stack::detail::get_as_upvalues<function_type>(L, 1); auto memberdata = stack::stack_detail::get_as_upvalues<function_type>(L, 1);
auto objdata = stack::detail::get_as_upvalues<T*>(L, memberdata.second); auto objdata = stack::stack_detail::get_as_upvalues<T*>(L, memberdata.second);
function_type& memfx = memberdata.first; function_type& memfx = memberdata.first;
T& item = *objdata.first; T& item = *objdata.first;
auto fx = [&item, &memfx](auto&&... args) -> typename traits_type::return_type { return (item.*memfx)(std::forward<decltype(args)>(args)...); }; auto fx = [&item, &memfx](auto&&... args) -> typename traits_type::return_type { return (item.*memfx)(std::forward<decltype(args)>(args)...); };

View File

@ -31,8 +31,19 @@ namespace sol {
template<typename Table, typename Key> template<typename Table, typename Key>
struct proxy : public proxy_base<proxy<Table, Key>> { struct proxy : public proxy_base<proxy<Table, Key>> {
private: 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; 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: public:
@ -41,7 +52,7 @@ public:
template<typename T> template<typename T>
proxy& set(T&& item) { 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; return *this;
} }
@ -53,24 +64,23 @@ public:
template<typename U, EnableIf<Function<Unqualified<U>>> = 0> template<typename U, EnableIf<Function<Unqualified<U>>> = 0>
proxy& operator=(U&& other) { proxy& operator=(U&& other) {
tbl.set_function(key, std::forward<U>(other)); return set_function(std::forward<U>(other));
return *this;
} }
template<typename U, DisableIf<Function<Unqualified<U>>> = 0> template<typename U, DisableIf<Function<Unqualified<U>>> = 0>
proxy& operator=(U&& other) { proxy& operator=(U&& other) {
tbl.set(key, std::forward<U>(other)); return set(std::forward<U>(other));
return *this;
} }
template<typename T> template<typename T>
decltype(auto) get() const { 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> template <typename K>
decltype(auto) operator[](K&& key) const { decltype(auto) operator[](K&& k) const {
return get<table>()[std::forward<K>(key)]; auto keys = tuplefy(key, std::forward<K>(k));
return proxy<Table, decltype(keys)>(tbl, std::move(keys));
} }
template<typename... Ret, typename... Args> template<typename... Ret, typename... Args>

View File

@ -34,7 +34,7 @@ struct push_pop {
}; };
template <typename T> template <typename T>
struct push_pop<true, T> { struct push_pop<true, T> {
push_pop (T x) {} push_pop (T) {}
~push_pop() {} ~push_pop() {}
}; };
template <bool top_level = false, typename T> template <bool top_level = false, typename T>

View File

@ -35,6 +35,10 @@
namespace sol { namespace sol {
namespace stack { 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> template<typename T, typename = void>
struct getter; struct getter;
template<typename T, typename = void> template<typename T, typename = void>
@ -63,8 +67,7 @@ inline int push_args(lua_State*) {
template<typename T, typename... Args> template<typename T, typename... Args>
inline int push_args(lua_State* L, T&& t, Args&&... args) { inline int push_args(lua_State* L, T&& t, Args&&... args) {
int pushcount = push(L, std::forward<T>(t)); int pushcount = push(L, std::forward<T>(t));
using swallow = char[]; void(sol::detail::swallow{(pushcount += sol::stack::push(L, std::forward<Args>(args)), 0)... });
void(swallow{'\0', (pushcount += sol::stack::push(L, std::forward<Args>(args)), '\0')... });
return pushcount; return pushcount;
} }
@ -93,7 +96,27 @@ bool check(lua_State* L, int index) {
return check<T>(L, index, handler); 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 = const bool default_check_arguments =
#ifdef SOL_CHECK_ARGUMENTS #ifdef SOL_CHECK_ARGUMENTS
true; true;
@ -136,9 +159,8 @@ struct userdata_pusher<T*> {
template<typename T, std::size_t... I> template<typename T, std::size_t... I>
inline int push_tuple(std::index_sequence<I...>, lua_State* L, T&& tuplen) { inline int push_tuple(std::index_sequence<I...>, lua_State* L, T&& tuplen) {
using swallow = char[1 + sizeof...(I)];
int pushcount = 0; 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; 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) { 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)...); return push_userdata_pointer<T>(L, std::forward<Key>(metatablekey), std::forward<Args>(args)...);
} }
} // detail } // stack_detail
template <typename T, type expected, typename> template <typename T, type expected, typename>
struct checker { struct checker {
@ -391,8 +413,8 @@ struct popper {
template <typename... Args> template <typename... Args>
struct popper<std::tuple<Args...>> { struct popper<std::tuple<Args...>> {
inline decltype(auto) pop(lua_State* L) { inline decltype(auto) pop(lua_State* L) {
decltype(auto) r = get<std::tuple<Args...>>(L, lua_gettop(L) - sizeof...(Args)); decltype(auto) r = get<std::tuple<Args...>>(L, lua_gettop(L) - sizeof...(Args) + 1);
lua_pop(L, sizeof...(Args)); lua_pop(L, static_cast<int>(sizeof...(Args)));
return r; return r;
} }
}; };
@ -400,11 +422,11 @@ struct popper<std::tuple<Args...>> {
template<typename T, typename> template<typename T, typename>
struct pusher { struct pusher {
static int push(lua_State* L, T& t) { 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) { 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> template<typename T>
struct pusher<T*> { struct pusher<T*> {
static int push(lua_State* L, T* obj) { 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...>> { struct pusher<std::tuple<Args...>> {
template <typename Tuple> template <typename Tuple>
static int push(lua_State* L, Tuple&& tuplen) { 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> template<typename T>
inline int push_as_upvalues(lua_State* L, T& item) { inline int push_as_upvalues(lua_State* L, T& item) {
typedef std::decay_t<T> TValue; 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) { static bool check(types<Arg0, Args...>, std::index_sequence<I0, I...>, lua_State* L, int firstargument) {
bool checks = true; bool checks = true;
stack::check<Arg0>(L, firstargument + I0); stack::check<Arg0>(L, firstargument + I0);
using swallow = int[sizeof...(Args)+2]; (void)detail::swallow{(checks &= stack::check<Args>(L, firstargument + I))...};
(void)swallow {
0, (checks &= stack::check<Args>(L, firstargument + I))..., 0
};
return checks; 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) { 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 stacksize = lua_gettop(L);
const int firstargument = static_cast<int>(start + stacksize - std::max(sizeof...(Args)-1, static_cast<std::size_t>(0))); 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)...); 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) { 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 stacksize = lua_gettop(L);
const int firstargument = static_cast<int>(start + stacksize - std::max(sizeof...(Args)-1, static_cast<std::size_t>(0))); 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 ) if ( !checks )
throw error("Arguments not of the proper types for this function call"); throw error("Arguments not of the proper types for this function call");
fx(std::forward<FxArgs>(args)..., stack::get<Args>(L, firstargument + I)...); fx(std::forward<FxArgs>(args)..., stack::get<Args>(L, firstargument + I)...);
} }
} // detail } // stack_detail
inline void remove( lua_State* L, int index, int count ) { inline void remove( lua_State* L, int index, int count ) {
if ( count < 1 ) 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) { 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; 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) { 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)...); 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) { 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; 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) { 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)...); 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) { 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); call<check_args>(tr, ta, L, start, fx);
int nargs = static_cast<int>(sizeof...(Args)); 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; 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) { 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); decltype(auto) r = call<check_args>(types<ReturnType<Ret...>>(), ta, L, start, fx);
int nargs = static_cast<int>(sizeof...(Args)); int nargs = static_cast<int>(sizeof...(Args));

View File

@ -145,8 +145,7 @@ public:
} }
template<typename... Args, typename... Keys> template<typename... Args, typename... Keys>
auto get(Keys&&... keys) const decltype(auto) get(Keys&&... keys) const {
-> decltype(globals.get<Args...>(std::forward<Keys>(keys)...)) {
return globals.get<Args...>(std::forward<Keys>(keys)...); return globals.get<Args...>(std::forward<Keys>(keys)...);
} }
@ -156,24 +155,15 @@ public:
return *this; return *this;
} }
template<typename T> template<typename T, typename... Keys>
SOL_DEPRECATED state_view& set_userdata(usertype<T>& user) { decltype(auto) traverse_get(Keys&&... keys) const {
return set_usertype(user); return globals.traverse_get<T>(std::forward<Keys>(keys)...);
} }
template<typename Key, typename T> template<typename... Args>
SOL_DEPRECATED state_view& set_userdata(Key&& key, usertype<T>& user) { state_view& traverse_set(Args&&... args) {
return set_usertype(std::forward<Key>(key), user); globals.traverse_set(std::forward<Args>(args)...);
} return *this;
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 T> template<typename T>

View File

@ -37,65 +37,52 @@ class table_core : public reference {
friend class state; friend class state;
friend class state_view; friend class state_view;
template<typename T, typename Key, EnableIf<Bool<top_level>, is_c_str<Key>> = 0> template <typename... Args>
decltype(auto) single_get( Key&& key ) const { using is_global = And<Bool<top_level>, is_c_str<Args>...>;
lua_getglobal(lua_state( ), &key[0]);
return stack::pop<T>(lua_state()); 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> template<typename Ret, std::size_t I, typename Keys>
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>
decltype(auto) tuple_get( types<Ret>, std::index_sequence<I>, Keys&& keys ) const { 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> template<typename Pairs, std::size_t... I>
void tuple_set( std::index_sequence<I...>, Pairs&& pairs ) { void tuple_set( std::index_sequence<I...>, Pairs&& pairs ) {
using swallow = int[]; auto pp = stack::push_popper<is_global<decltype(std::get<I * 2>(pairs))...>::value>(*this);
swallow{ 0, ( single_set(std::get<I * 2>(pairs), std::get<I * 2 + 1>(pairs)) , 0)..., 0 }; 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 #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)...)); 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> template<typename... Args>
table_core& set( Args&&... args ) { table_core& set( Args&&... args ) {
tuple_set(std::make_index_sequence<sizeof...(Args) / 2>(), std::forward_as_tuple(std::forward<Args>(args)...)); tuple_set(std::make_index_sequence<sizeof...(Args) / 2>(), std::forward_as_tuple(std::forward<Args>(args)...));
return *this; 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> template<typename T>
table_core& set_usertype( usertype<T>& user ) { table_core& set_usertype( usertype<T>& user ) {
return set_usertype(usertype_traits<T>::name, user); return set_usertype(usertype_traits<T>::name, user);

View File

@ -380,6 +380,23 @@ template<typename T>
inline T* ptr(T* val) { inline T* ptr(T* val) {
return 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 } // sol
#endif // SOL_TRAITS_HPP #endif // SOL_TRAITS_HPP

View File

@ -45,6 +45,8 @@ struct tuple_types_ { typedef types<Args...> type; };
template<typename... Args> template<typename... Args>
struct tuple_types_<std::tuple<Args...>> { typedef types<Args...> type; }; struct tuple_types_<std::tuple<Args...>> { typedef types<Args...> type; };
using swallow = std::initializer_list<int>;
} // detail } // detail
template<typename... Args> template<typename... Args>

View File

@ -5,6 +5,16 @@
#include <vector> #include <vector>
#include <map> #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) { void test_free_func(std::function<void()> f) {
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.") { TEST_CASE("simple/set", "Check if the set works properly.") {
sol::state lua; sol::state lua;
int begintop = 0, endtop = 0;
{
stack_guard g(lua.lua_state(), begintop, endtop);
lua.set("a", 9); lua.set("a", 9);
} REQUIRE(begintop == endtop);
REQUIRE_NOTHROW(lua.script("if a ~= 9 then error('wrong value') end")); REQUIRE_NOTHROW(lua.script("if a ~= 9 then error('wrong value') end"));
{
stack_guard g(lua.lua_state(), begintop, endtop);
lua.set("d", "hello"); lua.set("d", "hello");
} REQUIRE(begintop == endtop);
REQUIRE_NOTHROW(lua.script("if d ~= 'hello' then error('expected \\'hello\\', got '.. tostring(d)) end")); REQUIRE_NOTHROW(lua.script("if d ~= 'hello' then error('expected \\'hello\\', got '.. tostring(d)) end"));
{
stack_guard g(lua.lua_state(), begintop, endtop);
lua.set("e", std::string("hello"), "f", true); 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 d ~= 'hello' then error('expected \\'hello\\', got '.. tostring(d)) end"));
REQUIRE_NOTHROW(lua.script("if f ~= true then error('wrong value') end")); REQUIRE_NOTHROW(lua.script("if f ~= true then error('wrong value') end"));
} }
TEST_CASE("simple/get", "Tests if the get function works properly.") { TEST_CASE("simple/get", "Tests if the get function works properly.") {
sol::state lua; sol::state lua;
int begintop = 0, endtop = 0;
lua.script("a = 9"); lua.script("a = 9");
{
stack_guard g(lua.lua_state(), begintop, endtop);
auto a = lua.get<int>("a"); auto a = lua.get<int>("a");
REQUIRE(a == 9.0); REQUIRE(a == 9.0);
} REQUIRE(begintop == endtop);
lua.script("b = nil"); lua.script("b = nil");
{
stack_guard g(lua.lua_state(), begintop, endtop);
REQUIRE_NOTHROW(lua.get<sol::nil_t>("b")); REQUIRE_NOTHROW(lua.get<sol::nil_t>("b"));
} REQUIRE(begintop == endtop);
lua.script("d = 'hello'"); lua.script("d = 'hello'");
lua.script("e = true"); lua.script("e = true");
{
stack_guard g(lua.lua_state(), begintop, endtop);
std::string d; std::string d;
bool e; bool e;
std::tie( d, e ) = lua.get<std::string, bool>("d", "e"); std::tie( d, e ) = lua.get<std::string, bool>("d", "e");
REQUIRE(d == "hello"); REQUIRE(d == "hello");
REQUIRE(e == true); REQUIRE(e == true);
} REQUIRE(begintop == endtop);
} }
TEST_CASE("simple/set-get-global-integer", "Tests if the get function works properly with global integers") { 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 // test const table retrieval
auto assert1 = [](const sol::table& t) { auto assert1 = [](const sol::table& t) {
std::string a = t["foo"]; std::string a = t["foo"];
int b = t["bar"]; double b = t["bar"];
std::cout << a << ',' << b << '\n'; REQUIRE(a == "goodbye");
REQUIRE(b == 20.4);
}; };
REQUIRE_NOTHROW(assert1(lua.global())); REQUIRE_NOTHROW(assert1(lua.global()));
@ -1086,13 +1160,13 @@ TEST_CASE( "functions/function_result-protected_function", "Function result shou
func.error_handler = luahandler; func.error_handler = luahandler;
luafunc.error_handler = cpphandler; luafunc.error_handler = cpphandler;
sol::function_result result1 = func(); sol::protected_function_result result1 = func();
int test = lua_gettop(lua.lua_state()); int test = lua_gettop(lua.lua_state());
REQUIRE(!result1.valid()); REQUIRE(!result1.valid());
std::string errorstring = result1; std::string errorstring = result1;
REQUIRE(errorstring == errormessage1); REQUIRE(errorstring == errormessage1);
sol::function_result result2 = luafunc(); sol::protected_function_result result2 = luafunc();
REQUIRE(!result2.valid()); REQUIRE(!result2.valid());
errorstring = result2; errorstring = result2;
REQUIRE(errorstring == errormessage2); REQUIRE(errorstring == errormessage2);