sol::property is implemented for usertypes now

sol::this_state is  a transparent argument that gets the current state at any position in any callback
sol::variadic_args allows a person to get something that can reference the "rest of the arguments", though it doesn't enforce that it has to be the last argument

Closes #57
Closes #59
Closes #60
This commit is contained in:
ThePhD 2016-04-17 02:18:34 -04:00
parent f10988363c
commit 88155d44e0
19 changed files with 539 additions and 150 deletions

View File

@ -33,7 +33,6 @@
#include "resolve.hpp"
namespace sol {
template <typename Sig, typename... Ps>
struct function_arguments {
std::tuple<Ps...> params;

View File

@ -80,7 +80,7 @@ struct upvalue_member_function {
template <int N, typename R, typename M, typename V>
int set_assignable(std::false_type, lua_State* L, M&, V&) {
lua_pop(L, N);
return luaL_error(L, "cannot write to this type: copy assignment/constructor not available");
return luaL_error(L, "sol: cannot write to this type: copy assignment/constructor not available");
}
template <int N, typename R, typename M, typename V>
@ -98,7 +98,7 @@ int set_variable(std::true_type, lua_State* L, M& mem, V& var) {
template <int N, typename R, typename M, typename V>
int set_variable(std::false_type, lua_State* L, M&, V&) {
lua_pop(L, N);
return luaL_error(L, "cannot write to a const variable");
return luaL_error(L, "sol: cannot write to a const variable");
}
template<typename T, typename Function>

View File

@ -26,6 +26,108 @@
#include <memory>
namespace sol {
template <typename RSig = void, typename WSig = void>
struct member_property {
typedef std::conditional_t<std::is_void<RSig>::value, detail::empty, RSig> R;
typedef std::conditional_t<std::is_void<WSig>::value, detail::empty, WSig> W;
R read;
W write;
member_property(R read, W write) : read(std::move(read)), write(std::move(write)) {}
template <typename T, typename Arg>
void write_if(std::true_type, T& mem, Arg&& arg) {
write(mem, arg);
}
template <typename T, typename Arg>
void write_if(std::false_type, T&, Arg&&) {
// This is a fatal error if we get here...
// Should never happen but...
// Crash horrifically, for safety?
std::abort();
}
template <typename T, typename Arg>
void operator()(T& mem, Arg&& arg) {
write_if(meta::Not<std::is_void<WSig>>(), mem, arg);
}
template <typename T>
decltype(auto) read_if(std::true_type, T& mem) {
return read(mem);
}
template <typename T>
decltype(auto) read_if(std::false_type, T&) {
typedef typename meta::bind_traits<WSig>::template arg_at<1> Arg;
typedef std::add_pointer_t<std::remove_reference_t<Arg>> pret;
// This is a fatal error if we get here...
// Should never happen but...
// Crash horrifically, for safety?
std::abort();
return *pret();
}
template <typename T>
decltype(auto) operator()(T& mem) {
return read_if(meta::Not<std::is_void<RSig>>(), mem);
}
};
template <typename R, typename T>
inline decltype(auto) property( R(T::* readfunc )() const) {
auto rf = [readfunc](T& mem) -> R {return (mem.*readfunc)();};
return member_property<decltype(rf)>(std::move(rf), detail::empty());
}
template <typename R, typename T>
inline decltype(auto) property( R(T::* readfunc )()) {
auto rf = [readfunc](T& mem) -> R {return (mem.*readfunc)();};
return member_property<decltype(rf)>(std::move(rf), detail::empty());
}
template <typename R, typename T, typename Arg>
inline decltype(auto) property(R(T::* writefunc)(Arg)) {
auto wf = [writefunc](T& mem, Arg arg) {(mem.*writefunc)(std::forward<Arg>(arg));};
return member_property<void, decltype(wf)>(detail::empty(), std::move(wf));
}
template <typename R, typename T, typename Arg>
inline decltype(auto) property(R(T::* writefunc)(Arg) const) {
auto wf = [writefunc](T& mem, Arg arg) {(mem.*writefunc)(std::forward<Arg>(arg));};
return member_property<void, decltype(wf)>(detail::empty(), std::move(wf));
}
template <typename RR, typename RT, typename WR, typename WT, typename Arg>
inline decltype(auto) property(RR(RT::* readfunc)(), WR(WT::* writefunc)(Arg)) {
auto rf = [readfunc](RT& mem) -> RR {return (mem.*readfunc)();};
auto wf = [writefunc](WT& mem, Arg arg) {(mem.*writefunc)(std::forward<Arg>(arg));};
return member_property<decltype(rf), decltype(wf)>(std::move(rf), std::move(wf));
}
template <typename RR, typename RT, typename WR, typename WT, typename Arg>
inline decltype(auto) property(RR(RT::* readfunc)() const, WR(WT::* writefunc)(Arg)) {
auto rf = [readfunc](RT& mem) -> RR {return (mem.*readfunc)();};
auto wf = [writefunc](WT& mem, Arg arg) {(mem.*writefunc)(std::forward<Arg>(arg));};
return member_property<decltype(rf), decltype(wf)>(std::move(rf), std::move(wf));
}
template <typename RR, typename RT, typename WR, typename WT, typename Arg>
inline decltype(auto) property(RR(RT::* readfunc)(), WR(WT::* writefunc)(Arg) const) {
auto rf = [readfunc](RT& mem) -> RR {return (mem.*readfunc)();};
auto wf = [writefunc](WT& mem, Arg arg) {(mem.*writefunc)(std::forward<Arg>(arg));};
return member_property<decltype(rf), decltype(wf)>(std::move(rf), std::move(wf));
}
template <typename RR, typename RT, typename WR, typename WT, typename Arg>
inline decltype(auto) property(RR(RT::* readfunc)() const, WR(WT::* writefunc)(Arg) const) {
auto rf = [readfunc](RT& mem) -> RR {return (mem.*readfunc)();};
auto wf = [writefunc](WT& mem, Arg arg) {(mem.*writefunc)(std::forward<Arg>(arg));};
return member_property<decltype(rf), decltype(wf)>(std::move(rf), std::move(wf));
}
namespace function_detail {
template <typename T>
struct implicit_wrapper {
@ -48,35 +150,62 @@ inline decltype(auto) cleanup_key() {
template<typename T, typename Func, typename = void>
struct functor {
typedef meta::bind_traits<Func> traits_type;
typedef typename traits_type::args_type args_type;
typedef meta::pop_front_type_t<typename traits_type::args_type> args_type;
typedef typename traits_type::return_type return_type;
static const std::size_t arity = traits_type::arity;
typedef meta::tuple_element_t<0, typename traits_type::args_tuple_type> Arg0;
typedef std::conditional_t<std::is_pointer<Func>::value || std::is_class<Func>::value, Func, std::add_pointer_t<Func>> function_type;
T* item;
Func invocation;
function_type invocation;
template<typename... Args>
functor(Args&&... args): item(nullptr), invocation(std::forward<Args>(args)...) {}
bool check () const {
return invocation != nullptr;
}
template<typename... Args>
void call(types<void>, Args&&... args) {
T& member = *item;
(member.*invocation)(std::forward<Args>(args)...);
invocation(implicit_wrapper<T>(member), std::forward<Args>(args)...);
}
template<typename Ret, typename... Args>
Ret call(types<Ret>, Args&&... args) {
T& member = *item;
return (member.*invocation)(std::forward<Args>(args)...);
return invocation(implicit_wrapper<T>(member), std::forward<Args>(args)...);
}
template<typename... Args>
auto operator()(Args&&... args) -> decltype(std::declval<functor>().call(types<return_type>{}, std::forward<Args>(args)...)) {
return this->call(types<return_type>(), std::forward<Args>(args)...);
}
};
template<typename T, typename RSig, typename WSig, typename C>
struct functor<T, member_property<RSig, WSig>, C> {
typedef meta::bind_traits<std::conditional_t<std::is_void<WSig>::value, RSig, WSig>> traits_type;
typedef meta::pop_front_type_t<typename traits_type::args_type> args_type;
typedef std::conditional_t<std::is_void<typename traits_type::return_type>::value, typename traits_type::template arg_at<0>, typename traits_type::return_type> return_type;
typedef member_property<RSig, WSig> function_type;
typedef meta::Not<std::is_void<RSig>> can_read;
typedef meta::Not<std::is_void<WSig>> can_write;
T* item;
function_type invocation;
template<typename... Args>
functor(Args&&... args): item(nullptr), invocation(std::forward<Args>(args)...) {}
template<typename Arg>
void call(Arg&& arg) {
T& member = *item;
invocation(member, std::forward<Arg>(arg));
}
decltype(auto) call() {
T& member = *item;
return invocation(member);
}
template<typename... Args>
decltype(auto) operator()(Args&&... args) {
return this->call(types<return_type>{}, std::forward<Args>(args)...);
return this->call(std::forward<Args>(args)...);
}
};
@ -86,16 +215,15 @@ struct functor<T, Func, std::enable_if_t<std::is_member_object_pointer<Func>::va
typedef typename traits_type::args_type args_type;
typedef typename traits_type::return_type return_type;
static const std::size_t arity = traits_type::arity;
typedef std::true_type can_read;
typedef std::true_type can_write;
T* item;
Func invocation;
template<typename... Args>
functor(Args&&... args): item(nullptr), invocation(std::forward<Args>(args)...) {}
bool check () const {
return this->fx.invocation != nullptr;
}
template<typename Arg>
void call(Arg&& arg) {
T& member = *item;
@ -114,50 +242,33 @@ struct functor<T, Func, std::enable_if_t<std::is_member_object_pointer<Func>::va
};
template<typename T, typename Func>
struct functor<T, Func, std::enable_if_t<std::is_function<Func>::value || std::is_class<Func>::value>> {
struct functor<T, Func, std::enable_if_t<std::is_member_function_pointer<Func>::value>> {
typedef meta::bind_traits<Func> traits_type;
typedef meta::pop_front_type_t<typename traits_type::args_type> args_type;
typedef typename traits_type::args_type args_type;
typedef typename traits_type::return_type return_type;
static const std::size_t arity = traits_type::arity;
typedef meta::tuple_element_t<0, typename traits_type::args_tuple_type> Arg0;
typedef std::conditional_t<std::is_pointer<Func>::value || std::is_class<Func>::value, Func, std::add_pointer_t<Func>> function_type;
static_assert(std::is_base_of<meta::Unqualified<std::remove_pointer_t<Arg0>>, T>::value, "Any non-member-function must have a first argument which is covariant with the desired userdata type.");
T* item;
function_type invocation;
private:
bool check(std::false_type) const {
return true;
}
bool check(std::true_type) const {
return this->invocation != nullptr;
}
public:
Func invocation;
template<typename... Args>
functor(Args&&... args): item(nullptr), invocation(std::forward<Args>(args)...) {}
bool check () const {
return this->check(std::is_function<Func>());
}
template<typename... Args>
void call(types<void>, Args&&... args) {
T& member = *item;
invocation(implicit_wrapper<T>(member), std::forward<Args>(args)...);
(member.*invocation)(std::forward<Args>(args)...);
}
template<typename Ret, typename... Args>
Ret call(types<Ret>, Args&&... args) {
T& member = *item;
return invocation(implicit_wrapper<T>(member), std::forward<Args>(args)...);
return (member.*invocation)(std::forward<Args>(args)...);
}
template<typename... Args>
auto operator()(Args&&... args) -> decltype(std::declval<functor>().call(types<return_type>{}, std::forward<Args>(args)...)) {
return this->call(types<return_type>(), std::forward<Args>(args)...);
decltype(auto) operator()(Args&&... args) {
return this->call(types<return_type>{}, std::forward<Args>(args)...);
}
};

View File

@ -113,7 +113,7 @@ struct member_variable : public base_function {
int set_assignable(std::false_type, lua_State* L, M) {
lua_pop(L, 1);
return luaL_error(L, "cannot write to this type: copy assignment/constructor not available");
return luaL_error(L, "sol: cannot write to this type: copy assignment/constructor not available");
}
int set_assignable(std::true_type, lua_State* L, M mem) {
@ -128,7 +128,7 @@ struct member_variable : public base_function {
int set_variable(std::false_type, lua_State* L, M) {
lua_pop(L, 1);
return luaL_error(L, "cannot write to a const variable");
return luaL_error(L, "sol: cannot write to a const variable");
}
int call(lua_State* L) {

View File

@ -28,7 +28,7 @@
namespace sol {
namespace function_detail {
template<typename Function, typename Tp>
template<typename Tp, typename Function>
struct usertype_function_core : public base_function {
typedef std::remove_pointer_t<Tp> T;
typedef std::remove_pointer_t<std::decay_t<Function>> function_type;
@ -42,48 +42,15 @@ struct usertype_function_core : public base_function {
template<typename... Args>
usertype_function_core(Args&&... args): fx(std::forward<Args>(args)...) {}
template<typename Return, typename Raw = meta::Unqualified<Return>>
std::enable_if_t<std::is_same<T, Raw>::value, int> push(lua_State* L, Return&& r) {
if(detail::ptr(detail::unwrap(r)) == fx.item) {
// push nothing
// note that pushing nothing with the ':'
// syntax means we leave the instance of what
// was pushed onto the stack by lua to do the
// function call alone,
// and naturally lua returns that.
// It's an "easy" way to return *this,
// without allocating an extra userdata, apparently!
return 1;
}
return stack::push_reference(L, std::forward<Return>(r));
}
template<typename Return, typename Raw = meta::Unqualified<Return>>
std::enable_if_t<!std::is_same<T, Raw>::value, int> push(lua_State* L, Return&& r) {
return stack::push_reference(L, std::forward<Return>(r));
}
template<typename... Args, std::size_t Start>
int operator()(types<void> tr, types<Args...> ta, index_value<Start>, lua_State* L) {
stack::call_into_lua(tr, ta, L, static_cast<int>(Start), fx);
int nargs = static_cast<int>(sizeof...(Args));
lua_pop(L, nargs);
return 0;
}
template<typename... Ret, typename... Args, std::size_t Start>
int operator()(types<Ret...> tr, types<Args...> ta, index_value<Start>, lua_State* L) {
decltype(auto) r = stack::call(tr, ta, L, static_cast<int>(Start), fx);
int nargs = static_cast<int>(sizeof...(Args));
lua_pop(L, nargs);
int pushcount = push(L, std::forward<decltype(r)>(r));
return pushcount;
return stack::call_into_lua<1>(tr, ta, L, static_cast<int>(Start), fx);
}
};
template<typename Function, typename Tp>
struct usertype_function : public usertype_function_core<Function, Tp> {
typedef usertype_function_core<Function, Tp> base_t;
template<typename Tp, typename Function>
struct usertype_function : public usertype_function_core<Tp, Function> {
typedef usertype_function_core<Tp, Function> base_t;
typedef std::remove_pointer_t<Tp> T;
typedef typename base_t::traits_type traits_type;
typedef typename base_t::args_type args_type;
@ -102,41 +69,62 @@ struct usertype_function : public usertype_function_core<Function, Tp> {
}
};
template<typename Function, typename Tp>
struct usertype_variable_function : public usertype_function_core<Function, Tp> {
typedef usertype_function_core<Function, Tp> base_t;
template<typename Tp, typename Function>
struct usertype_variable_function : public usertype_function_core<Tp, Function> {
typedef usertype_function_core<Tp, Function> base_t;
typedef std::remove_pointer_t<Tp> T;
typedef typename base_t::fx_t fx_t;
typedef typename base_t::traits_type traits_type;
typedef typename base_t::args_type args_type;
typedef typename base_t::return_type return_type;
typedef typename fx_t::can_read can_read;
typedef typename fx_t::can_write can_write;
template<typename... Args>
usertype_variable_function(Args&&... args): base_t(std::forward<Args>(args)...) {}
int set_assignable(std::false_type, lua_State* L) {
lua_pop(L, 2);
return luaL_error(L, "cannot write to this type: copy assignment/constructor not available");
lua_pop(L, 3);
return luaL_error(L, "sol: cannot write to this type: copy assignment/constructor not available");
}
int set_assignable(std::true_type, lua_State* L) {
return set_writable(can_write(), L);
}
int set_writable(std::false_type, lua_State* L) {
lua_pop(L, 3);
return luaL_error(L, "sol: cannot write to readonly variable");
}
int set_writable(std::true_type, lua_State* L) {
return static_cast<base_t&>(*this)(meta::tuple_types<void>(), args_type(), index_value<3>(), L);
}
int set_variable(std::false_type, lua_State* L) {
lua_pop(L, 2);
return luaL_error(L, "cannot write to a const variable");
lua_pop(L, 3);
return luaL_error(L, "sol: cannot write to a const variable");
}
int set_variable(std::true_type, lua_State* L) {
return set_assignable(std::is_assignable<std::add_lvalue_reference_t<return_type>, return_type>(), L);
}
int get_variable(std::false_type, lua_State* L) {
lua_pop(L, 2);
return luaL_error(L, "sol: cannot read from a readonly property");
}
int get_variable(std::true_type, lua_State* L) {
return static_cast<base_t&>(*this)(meta::tuple_types<return_type>(), types<>(), index_value<2>(), L);
}
int prelude(lua_State* L) {
int argcount = lua_gettop(L);
this->fx.item = stack::get<T*>(L, 1);
switch(argcount) {
case 2:
return static_cast<base_t&>(*this)(meta::tuple_types<return_type>(), types<>(), index_value<2>(), L);
return get_variable(can_read(), L);
case 3:
return set_variable(meta::Not<std::is_const<return_type>>(), L);
default:

View File

@ -25,6 +25,7 @@
#include "reference.hpp"
#include "userdata.hpp"
#include "stack.hpp"
#include "variadic_args.hpp"
namespace sol {
template <typename base_t>

View File

@ -69,14 +69,18 @@ inline std::pair<T, int> get_as_upvalues(lua_State* L, int index = 1) {
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 decltype(auto) call(types<R>, types<Args...> ta, std::index_sequence<I...> tai, lua_State* L, int start, Fx&& fx, FxArgs&&... args) {
typedef meta::index_in_pack<this_state, Args...> state_pack_index;
typedef std::integral_constant<int, state_pack_index::value == -1 ? std::numeric_limits<int>::max() : static_cast<int>(state_pack_index::value)> state_idx;
check_types<checkargs>{}.check(ta, tai, L, start, type_panic);
return fx(std::forward<FxArgs>(args)..., stack_detail::unchecked_get<Args>(L, start + I)...);
return fx(std::forward<FxArgs>(args)..., stack_detail::unchecked_get<Args>(L, start + (state_idx::value < I ? I - 1 : I))...);
}
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) {
typedef meta::index_in_pack<this_state, Args...> state_pack_index;
typedef std::integral_constant<int, state_pack_index::value == -1 ? std::numeric_limits<int>::max() : static_cast<int>(state_pack_index::value)> state_idx;
check_types<checkargs>{}.check(ta, tai, L, start, type_panic);
fx(std::forward<FxArgs>(args)..., stack_detail::unchecked_get<Args>(L, start + I)...);
fx(std::forward<FxArgs>(args)..., stack_detail::unchecked_get<Args>(L, start + (state_idx::value < I ? I - 1 : I))...);
}
} // stack_detail
@ -104,7 +108,7 @@ inline void remove( lua_State* L, int index, int count ) {
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 decltype(auto) call(types<R> tr, types<Args...> ta, lua_State* L, int start, Fx&& fx, FxArgs&&... args) {
typedef typename types<Args...>::indices args_indices;
typedef std::make_index_sequence<sizeof...(Args)> args_indices;
return stack_detail::call<check_args>(tr, ta, args_indices(), L, start, std::forward<Fx>(fx), std::forward<FxArgs>(args)...);
}
@ -115,7 +119,7 @@ inline decltype(auto) call(types<R> tr, types<Args...> ta, lua_State* L, Fx&& fx
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;
typedef std::make_index_sequence<sizeof...(Args)> args_indices;
stack_detail::call<check_args>(tr, ta, args_indices(), L, start, std::forward<Fx>(fx), std::forward<FxArgs>(args)...);
}
@ -147,11 +151,11 @@ inline int call_into_lua(types<Ret0, Ret...>, types<Args...> ta, lua_State* L, i
decltype(auto) r = call<check_args>(types<meta::return_type_t<Ret0, Ret...>>(), ta, L, start, std::forward<Fx>(fx), std::forward<FxArgs>(fxargs)...);
int nargs = static_cast<int>(sizeof...(Args)) + additionalpop;
lua_pop(L, nargs);
return push(L, std::forward<decltype(r)>(r));
return push_reference(L, std::forward<decltype(r)>(r));
}
inline call_syntax get_call_syntax(lua_State* L, const std::string& meta) {
luaL_getmetatable(L, meta.c_str());
inline call_syntax get_call_syntax(lua_State* L, const std::string& key) {
luaL_getmetatable(L, key.c_str());
if (lua_compare(L, -1, -2, LUA_OPEQ) == 1) {
lua_pop(L, 1);
return call_syntax::colon;

View File

@ -65,7 +65,7 @@ struct check_types {
static bool check(types<Arg0, Args...>, std::index_sequence<I0, I...>, lua_State* L, int firstargument, Handler&& handler) {
if (!stack::check<Arg0>(L, firstargument + I0, handler))
return false;
return check(types<Args...>(), std::index_sequence<I...>(), L, firstargument, std::forward<Handler>(handler));
return check(types<Args...>(), std::index_sequence<I...>(), L, firstargument - static_cast<int>(std::is_same<this_state, meta::Unqualified<Arg0>>::value), std::forward<Handler>(handler));
}
template <typename Handler>
@ -121,6 +121,22 @@ struct checker<nil_t, expected, C> {
template <type expected, typename C>
struct checker<nullopt_t, expected, C> : checker<nil_t> {};
template <typename C>
struct checker<this_state, type::none, C> {
template <typename Handler>
static bool check (lua_State*, int, Handler&&) {
return true;
}
};
template <typename C>
struct checker<variadic_args, type::poly, C> {
template <typename Handler>
static bool check (lua_State*, int, Handler&&) {
return true;
}
};
template <typename T, typename C>
struct checker<T, type::poly, C> {
template <typename Handler>

View File

@ -63,26 +63,12 @@ struct getter<T, std::enable_if_t<meta::And<std::is_integral<T>, std::is_unsigne
};
template<typename T>
struct getter<T, std::enable_if_t<std::is_base_of<reference, T>::value>> {
struct getter<T, std::enable_if_t<std::is_base_of<reference, T>::value || std::is_base_of<stack_reference, T>::value>> {
static T get(lua_State* L, int index = -1) {
return T(L, index);
}
};
template<typename T>
struct getter<T, std::enable_if_t<std::is_base_of<stack_reference, T>::value>> {
static T get(lua_State* L, int index = -1) {
return T(L, index);
}
};
template<>
struct getter<stack_reference> {
static stack_reference get(lua_State* L, int index = -1) {
return stack_reference(L, index);
}
};
template<>
struct getter<userdata_value> {
static userdata_value get(lua_State* L, int index = -1) {
@ -148,6 +134,13 @@ struct getter<nullopt_t> {
}
};
template<>
struct getter<this_state> {
static this_state get(lua_State* L, int = -1) {
return this_state{L};
}
};
template<>
struct getter<lua_CFunction> {
static lua_CFunction get(lua_State* L, int index = -1) {

View File

@ -39,9 +39,30 @@ public:
return stack::get<T>(L, stack_index());
}
int push () const {
lua_pushvalue(L, index);
return 1;
}
lua_State* lua_state() const { return L; }
int stack_index() const { return index; }
};
namespace stack {
template <>
struct getter<stack_proxy> {
static stack_proxy get(lua_State* L, int index = -1) {
return stack_proxy(L, index);
}
};
template <>
struct pusher<stack_proxy> {
static int push(lua_State*, const stack_proxy& ref) {
return ref.push();
}
};
} // stack
} // sol
#endif // SOL_STACK_PROXY_HPP

View File

@ -175,7 +175,7 @@ struct pusher<T, std::enable_if_t<meta::And<meta::has_begin_end<T>, meta::has_ke
};
template<typename T>
struct pusher<T, std::enable_if_t<std::is_base_of<reference, T>::value>> {
struct pusher<T, std::enable_if_t<std::is_base_of<reference, T>::value || std::is_base_of<stack_reference, T>::value>> {
static int push(lua_State*, T& ref) {
return ref.push();
}
@ -185,13 +185,6 @@ struct pusher<T, std::enable_if_t<std::is_base_of<reference, T>::value>> {
}
};
template<>
struct pusher<stack_reference> {
static int push(lua_State*, const stack_reference& ref) {
return ref.push();
}
};
template<>
struct pusher<bool> {
static int push(lua_State* L, bool b) {
@ -333,6 +326,13 @@ struct pusher<nullopt_t> {
return stack::push(L, nil);
}
};
template<>
struct pusher<this_state> {
static int push(lua_State*, const this_state&) {
return 0;
}
};
} // stack
} // sol

View File

@ -132,6 +132,17 @@ struct find_in_pack_v : Bool<false> { };
template<typename V, typename Vs1, typename... Vs>
struct find_in_pack_v<V, Vs1, Vs...> : Or<Bool<(V::value == Vs1::value)>, find_in_pack_v<V, Vs...>> { };
namespace meta_detail {
template<std::size_t I, typename T, typename... Args>
struct index_in_pack : std::integral_constant<std::ptrdiff_t, -1> { };
template<std::size_t I, typename T, typename T1, typename... Args>
struct index_in_pack<I, T, T1, Args...> : std::conditional_t<std::is_same<T, T1>::value, std::integral_constant<std::ptrdiff_t, I>, index_in_pack<I + 1, T, Args...>> { };
}
template<typename T, typename... Args>
struct index_in_pack : meta_detail::index_in_pack<0, T, Args...> { };
template<std::size_t I, typename... Args>
struct at_in_pack {};

View File

@ -83,7 +83,8 @@ inline int c_trampoline(lua_State* L, lua_CFunction f) {
return trampoline(L, f);
}
#endif // Exceptions vs. No Exceptions
}
struct empty { void operator()() {} };
} // detail
struct nil_t {};
const nil_t nil {};
struct metatable_key_t {};
@ -91,7 +92,7 @@ const metatable_key_t metatable_key = {};
inline bool operator==(nil_t, nil_t) { return true; }
inline bool operator!=(nil_t, nil_t) { return false; }
typedef std::add_lvalue_reference_t<std::remove_pointer_t<lua_CFunction>> lua_r_CFunction;
typedef std::remove_pointer_t<lua_CFunction> lua_r_CFunction;
template <typename T, typename = void>
struct unique_usertype {};
@ -132,6 +133,16 @@ struct c_closure {
c_closure(lua_CFunction f, int upvalues = 0) : c_function(f), upvalues(upvalues) {}
};
struct this_state {
lua_State* L;
operator lua_State* () const {
return L;
}
lua_State* operator-> () const {
return L;
}
};
enum class call_syntax {
dot = 0,
colon = 1
@ -295,6 +306,7 @@ 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>;
@ -377,7 +389,10 @@ template <typename Signature>
struct lua_type_of<std::function<Signature>> : std::integral_constant<type, type::function>{};
template <typename T>
struct lua_type_of<optional<T>> : std::integral_constant<type, type::poly>{};
struct lua_type_of<optional<T>> : std::integral_constant<type, type::poly> {};
template <>
struct lua_type_of<variadic_args> : std::integral_constant<type, type::poly> {};
template <typename T>
struct lua_type_of<T*> : std::integral_constant<type, type::userdata> {};
@ -388,6 +403,9 @@ struct lua_type_of<T, std::enable_if_t<std::is_arithmetic<T>::value>> : std::int
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<this_state> : std::integral_constant<type, type::none> {};
template <typename T>
struct is_lua_primitive : std::integral_constant<bool,
type::userdata != lua_type_of<meta::Unqualified<T>>::value

View File

@ -197,10 +197,15 @@ private:
return std::make_unique<function_detail::usertype_constructor_function<T, Functions...>>(std::move(func.set));
}
template<typename RSig, typename WSig>
std::unique_ptr<function_detail::base_function> make_function(const std::string&, member_property<RSig, WSig> func) {
return std::make_unique<function_detail::usertype_variable_function<T, member_property<RSig, WSig>>>(std::move(func));
}
template<typename Fx>
std::unique_ptr<function_detail::base_function> make_free_function(std::true_type, const std::string&, Fx&& func) {
typedef std::decay_t<meta::Unqualified<Fx>> function_type;
return std::make_unique<function_detail::usertype_function<function_type, T>>(func);
return std::make_unique<function_detail::usertype_function<T, function_type>>(func);
}
template<typename Fx>
@ -219,16 +224,16 @@ private:
template<typename Base, typename Ret>
std::unique_ptr<function_detail::base_function> make_variable_function(std::true_type, const std::string&, Ret Base::* func) {
static_assert(std::is_base_of<Base, T>::value, "Any registered function must be part of the class");
static_assert(std::is_base_of<Base, T>::value, "Any registered member variable must be part of the class");
typedef std::decay_t<decltype(func)> function_type;
return std::make_unique<function_detail::usertype_variable_function<function_type, T>>(func);
return std::make_unique<function_detail::usertype_variable_function<T, function_type>>(func);
}
template<typename Base, typename Ret>
std::unique_ptr<function_detail::base_function> make_variable_function(std::false_type, const std::string&, Ret Base::* func) {
static_assert(std::is_base_of<Base, T>::value, "Any registered function must be part of the class");
static_assert(std::is_base_of<Base, T>::value, "Any registered member function must be part of the class");
typedef std::decay_t<decltype(func)> function_type;
return std::make_unique<function_detail::usertype_function<function_type, T>>(func);
return std::make_unique<function_detail::usertype_function<T, function_type>>(func);
}
template<typename Base, typename Ret>
@ -240,7 +245,7 @@ private:
template<typename Fx>
std::unique_ptr<function_detail::base_function> make_functor_function(std::true_type, const std::string&, Fx&& func) {
typedef std::decay_t<meta::Unqualified<Fx>> function_type;
return std::make_unique<function_detail::usertype_function<function_type, T>>(func);
return std::make_unique<function_detail::usertype_function<T, function_type>>(func);
}
template<typename Fx>
@ -339,7 +344,7 @@ private:
template<std::size_t N, typename Fx>
void build_function(std::string funcname, Fx&& func) {
typedef std::is_member_object_pointer<meta::Unqualified<Fx>> is_variable;
typedef meta::Or<std::is_member_object_pointer<meta::Unqualified<Fx>>, meta::is_specialization_of<member_property, meta::Unqualified<Fx>>> is_variable;
functionnames.push_back(std::move(funcname));
std::string& name = functionnames.back();
auto baseptr = make_function(name, std::forward<Fx>(func));

102
sol/variadic_args.hpp Normal file
View File

@ -0,0 +1,102 @@
// The MIT License (MIT)
// Copyright (c) 2013-2016 Rapptz, ThePhD and contributors
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#ifndef SOL_VARIADIC_ARGS_HPP
#define SOL_VARIADIC_ARGS_HPP
#include "stack.hpp"
#include "stack_proxy.hpp"
namespace sol {
struct variadic_args {
private:
lua_State* L;
int index;
int stacktop;
public:
variadic_args() = default;
variadic_args(lua_State* L, int index = -1): L(L), index(lua_absindex(L, index)), stacktop(lua_gettop(L)) {}
variadic_args(const variadic_args&) = default;
variadic_args& operator=(const variadic_args&) = default;
variadic_args(variadic_args&& o) : L(o.L), index(o.index), stacktop(o.stacktop) {
// 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.stacktop = 0;
}
variadic_args& operator=(variadic_args&& o) {
L = o.L;
index = o.index;
stacktop = o.stacktop;
// 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.stacktop = 0;
return *this;
}
int push () const {
int pushcount = 0;
for (int i = index; i <= stacktop; ++i) {
lua_pushvalue(L, i);
pushcount += 1;
}
return pushcount;
}
template<typename T>
decltype(auto) get(int start = 0) const {
return stack::get<T>(L, index + start);
}
stack_proxy operator[](int start) const {
return stack_proxy(L, index + start);
}
lua_State* lua_state() const { return L; };
int stack_index() const { return index; };
int leftover_count() const { return stacktop - (index - 1); }
int top() const { return stacktop; }
};
namespace stack {
template <>
struct getter<variadic_args> {
static variadic_args get(lua_State* L, int index = -1) {
return variadic_args(L, index);
}
};
template <>
struct pusher<variadic_args> {
static int push(lua_State*, const variadic_args& ref) {
return ref.push();
}
};
} // stack
} // sol
#endif // SOL_VARIADIC_ARGS_HPP

View File

@ -749,3 +749,35 @@ end)");
REQUIRE(b == 2);
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 (int i = 0; i < va.leftover_count(); ++i) {
int v = va[i];
r += v;
}
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);
}

View File

@ -1,11 +1,11 @@
#pragma once
struct stack_guard {
struct test_stack_guard {
lua_State* L;
int& begintop;
int& endtop;
stack_guard(lua_State* L, int& begintop, int& endtop) : L(L), begintop(begintop), endtop(endtop) {
test_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); }
~test_stack_guard() { endtop = lua_gettop(L); }
};

View File

@ -168,7 +168,7 @@ TEST_CASE("tables/iterators", "Testing the use of iteratrs to get values from a
int begintop = 0;
int endtop = 0;
{
stack_guard s(lua.lua_state(), begintop, endtop);
test_stack_guard s(lua.lua_state(), begintop, endtop);
for (auto& kvp : tbl) {
[&iterations](sol::object key, sol::object value) {
++iterations;

112
tests.cpp
View File

@ -192,39 +192,39 @@ TEST_CASE("table/traversal", "ensure that we can chain requests and tunnel down
lua.script("t1 = {t2 = {t3 = 24}};");
{
stack_guard g(lua.lua_state(), begintop, endtop);
test_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);
test_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);
test_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);
test_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);
test_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);
test_stack_guard g(lua.lua_state(), begintop, endtop);
int x13 = lua["t1"]["t2"]["t3"];
REQUIRE(x13 == 13);
} REQUIRE(begintop == endtop);
@ -234,18 +234,18 @@ TEST_CASE("simple/set", "Check if the set works properly.") {
sol::state lua;
int begintop = 0, endtop = 0;
{
stack_guard g(lua.lua_state(), begintop, endtop);
test_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"));
{
stack_guard g(lua.lua_state(), begintop, endtop);
test_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"));
{
stack_guard g(lua.lua_state(), begintop, endtop);
test_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"));
@ -258,21 +258,21 @@ TEST_CASE("simple/get", "Tests if the get function works properly.") {
lua.script("a = 9");
{
stack_guard g(lua.lua_state(), begintop, endtop);
test_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");
{
stack_guard g(lua.lua_state(), begintop, endtop);
test_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");
{
stack_guard g(lua.lua_state(), begintop, endtop);
test_stack_guard g(lua.lua_state(), begintop, endtop);
std::string d;
bool e;
std::tie( d, e ) = lua.get<std::string, bool>("d", "e");
@ -944,3 +944,91 @@ lw2 = bark.something2(2, 3)
REQUIRE_THROWS(lua.script("b.var2 = 2"));
}
TEST_CASE("usertype/properties", "Check if member properties/variables work") {
struct bark {
int var = 50;
int var2 = 25;
int get_var2() const {
return var2;
}
int get_var3() {
return var2;
}
void set_var2( int x ) {
var2 = x;
}
};
sol::state lua;
lua.open_libraries(sol::lib::base);
lua.new_usertype<bark>("bark",
"var", &bark::var,
"var2", sol::readonly( &bark::var2 ),
"a", sol::property(&bark::get_var2, &bark::set_var2),
"b", sol::property(&bark::get_var2),
"c", sol::property(&bark::get_var3),
"d", sol::property(&bark::set_var2)
);
bark b;
lua.set("b", &b);
lua.script("b.a = 59");
lua.script("var2_0 = b.a");
lua.script("var2_1 = b.b");
lua.script("b.d = 1568");
lua.script("var2_2 = b.c");
int var2_0 = lua["var2_0"];
int var2_1 = lua["var2_1"];
int var2_2 = lua["var2_2"];
REQUIRE(var2_0 == 59);
REQUIRE(var2_1 == 59);
REQUIRE(var2_2 == 1568);
REQUIRE_THROWS(lua.script("b.var2 = 24"));
REQUIRE_THROWS(lua.script("r = b.d"));
REQUIRE_THROWS(lua.script("r = b.d"));
REQUIRE_THROWS(lua.script("b.b = 25"));
REQUIRE_THROWS(lua.script("b.c = 11"));
}
TEST_CASE("utilities/this_state", "Ensure this_state argument can be gotten anywhere in the function.") {
struct bark {
int with_state(sol::this_state l, int a, int b) {
lua_State* L = l;
int c = lua_gettop(L);
return a + b + (c - c);
}
static int with_state_2(int a, sol::this_state l, int b) {
lua_State* L = l;
int c = lua_gettop(L);
return a * b + (c - c);
}
};
sol::state lua;
lua.open_libraries(sol::lib::base);
lua.new_usertype<bark>("bark",
"with_state", &bark::with_state
);
bark b;
lua.set("b", &b);
lua.set("with_state_2", bark::with_state_2);
sol::function fx = lua["with_state_2"];
int a = fx(25, 25);
lua.script("a = with_state_2(25, 25)");
lua.script("c = b:with_state(25, 25)");
int la = lua["a"];
int lc = lua["c"];
REQUIRE(lc == 50);
REQUIRE(a == 625);
REQUIRE(la == 625);
}