Overloaded functions now work properly when types are specified in signature

this triggered overhaul of set_function/pusher<function_t>::push(...)
both state and table reflect changes to userdata structure to make it easier to use
tests updated to account for overload resolution
some function-related traits added to make use easier -- cleaned up archaic typenames in function_types.hpp
Account for std::reference_wrapper for objects -- sol now uses copy-by-default (value-semantics) for all functors
updated tests to reflect this
This commit is contained in:
ThePhD 2014-06-28 23:16:48 -07:00
parent b41f92adc9
commit eb25bb05bb
9 changed files with 364 additions and 321 deletions

View File

@ -85,76 +85,112 @@ public:
}; };
namespace stack { namespace stack {
template <> template <typename... Sigs>
struct pusher<function_t> { struct pusher<function_sig_t<Sigs...>> {
template<typename TFx> template<typename R, typename... Args, typename Fx, typename = typename std::result_of<Fx(Args...)>::type>
static void set_isfunction_fx(std::true_type, lua_State* L, TFx&& fx) { static void set_memfx(types<R(Args...)> t, lua_State* L, Fx&& fx) {
set_fx(std::false_type(), L, std::forward<TFx>(fx)); typedef Decay<Unwrap<Fx>> raw_fx_t;
typedef R(* fx_ptr_t)(Args...);
typedef std::is_convertible<raw_fx_t, fx_ptr_t> is_convertible;
set_isconvertible_fx(is_convertible(), t, L, std::forward<Fx>(fx));
} }
template<typename TFx> template<typename... Args, typename Fx, typename... Cx, typename R = typename std::result_of<Fx(Args...)>::type>
static void set_isfunction_fx(std::false_type, lua_State* L, TFx&& fx) { static void set_memfx(types<Args...>, lua_State* L, Fx&& fx){
typedef Decay<TFx> clean_lambda; set_memfx(types<R(Args...)>(), L, std::forward<Fx>(fx));
typedef typename function_traits<decltype(&clean_lambda::operator())>::free_function_pointer_type raw_func_t;
typedef std::is_convertible<clean_lambda, raw_func_t> is_convertible;
set_isconvertible_fx(is_convertible(), L, std::forward<TFx>(fx));
} }
template<typename TFx> template<typename Fx>
static void set_isconvertible_fx(std::true_type, lua_State* L, TFx&& fx) { static void set_memfx(types<>, lua_State* L, Fx&& fx) {
typedef Decay<TFx> clean_lambda; typedef Unqualified<Unwrap<Fx>> fx_t;
typedef typename function_traits<decltype(&clean_lambda::operator())>::free_function_pointer_type raw_func_t; typedef decltype(&fx_t::operator()) Sig;
set_isfunction_fx(std::true_type(), L, raw_func_t(std::forward<TFx>(fx))); set_memfx(types<function_signature_t<Sig>>(), L, std::forward<Fx>(fx));
} }
template<typename TFx> template<typename... Args, typename R>
static void set_isconvertible_fx(std::false_type, lua_State* L, TFx&& fx) { static void set(lua_State* L, R fxptr(Args...)){
typedef typename std::remove_pointer<Decay<TFx>>::type clean_fx; set_fx(std::false_type(), L, fxptr);
std::unique_ptr<base_function> sptr(new functor_function<clean_fx>(std::forward<TFx>(fx)));
set_fx<TFx>(L, std::move(sptr));
} }
template<typename TFx, typename TObj> template<typename Sig>
static void set_lvalue_fx(std::true_type, lua_State* L, TFx&& fx, TObj&& obj) { static void set(lua_State* L, Sig* fxptr){
set_fx(std::true_type(), L, std::forward<TFx>(fx), std::forward<TObj>(obj)); set_fx(std::false_type(), L, fxptr);
} }
template<typename TFx, typename TObj> template<typename... Args, typename R, typename C, typename T>
static void set_lvalue_fx(std::false_type, lua_State* L, TFx&& fx, TObj&& obj) { static void set(lua_State* L, R (C::*memfxptr)(Args...), T&& obj) {
typedef typename std::remove_pointer<Decay<TFx>>::type clean_fx; typedef Bool<is_specialization_of<T, std::reference_wrapper>::value || std::is_pointer<T>::value> is_reference;
std::unique_ptr<base_function> sptr(new member_function<clean_fx, TObj>(std::forward<TObj>(obj), std::forward<TFx>(fx))); set_reference_fx(is_reference(), L, memfxptr, std::forward<T>(obj));
return set_fx<TFx>(L, std::move(sptr));
} }
template<typename TFx, typename TObj> template<typename Sig, typename C, typename T>
static void set_fx(std::true_type, lua_State* L, TFx&& fx, TObj&& obj) { static void set(lua_State* L, Sig C::* memfxptr, T&& obj) {
typedef Bool<is_specialization_of<T, std::reference_wrapper>::value || std::is_pointer<T>::value> is_reference;
set_reference_fx(is_reference(), L, memfxptr, std::forward<T>(obj));
}
template<typename... Sig, typename Fx>
static void set(lua_State* L, Fx&& fx) {
set_memfx(types<Sig...>(), L, std::forward<Fx>(fx));
}
template<typename Fx, typename R, typename... Args>
static void set_isconvertible_fx(std::true_type, types<R(Args...)>, lua_State* L, Fx&& fx) {
typedef R(* fx_ptr_t)(Args...);
fx_ptr_t fxptr = unwrapper(std::forward<Fx>(fx));
set(L, fxptr);
}
template<typename Fx, typename R, typename... Args>
static void set_isconvertible_fx(std::false_type, types<R(Args...)>, lua_State* L, Fx&& fx) {
typedef Decay<Unwrap<Fx>> fx_t;
std::unique_ptr<base_function> sptr(new functor_function<fx_t>(std::forward<Fx>(fx)));
set_fx<Fx>(L, std::move(sptr));
}
template<typename Fx, typename T>
static void set_reference_fx(std::true_type, lua_State* L, Fx&& fx, T&& obj) {
set_fx(std::true_type(), L, std::forward<Fx>(fx), std::forward<T>(obj));
}
template<typename Fx, typename T>
static void set_reference_fx(std::false_type, lua_State* L, Fx&& fx, T&& obj) {
typedef typename std::remove_pointer<Decay<Fx>>::type clean_fx;
std::unique_ptr<base_function> sptr(new member_function<clean_fx, T>(std::forward<T>(obj), std::forward<Fx>(fx)));
return set_fx<Fx>(L, std::move(sptr));
}
template<typename Fx, typename T>
static void set_fx(std::true_type, lua_State* L, Fx&& fx, T&& obj) {
// Layout: // Layout:
// idx 1...n: verbatim data of member function pointer // idx 1...n: verbatim data of member function pointer
// idx n + 1: is the object's void pointer // idx n + 1: is the object's void pointer
// We don't need to store the size, because the other side is templated // We don't need to store the size, because the other side is templated
// with the same member function pointer type // with the same member function pointer type
Decay<TFx> fxptr(std::forward<TFx>(fx)); Decay<Fx> memfxptr(std::forward<Fx>(fx));
void* userobjdata = static_cast<void*>(sol::detail::get_ptr(obj)); auto userptr = sol::detail::get_ptr(obj);
lua_CFunction freefunc = &static_member_function<Decay<TObj>, TFx>::call; void* userobjdata = static_cast<void*>(userptr);
lua_CFunction freefunc = &static_member_function<Decay<decltype(*userptr)>, Fx>::call;
int upvalues = stack::detail::push_as_upvalues(L, fxptr); int upvalues = stack::detail::push_as_upvalues(L, memfxptr);
stack::push(L, userobjdata); stack::push(L, userobjdata);
stack::pusher<lua_CFunction>{}.push(L, freefunc, upvalues); ++upvalues;
stack::push(L, freefunc, upvalues);
} }
template<typename TFx> template<typename Fx>
static void set_fx(lua_State* L, std::false_type, TFx&& fx) { static void set_fx(std::false_type, lua_State* L, Fx&& fx) {
Decay<TFx> target(std::forward<TFx>(fx)); Decay<Fx> target(std::forward<Fx>(fx));
lua_CFunction freefunc = &static_function<TFx>::call; lua_CFunction freefunc = &static_function<Fx>::call;
int upvalues = stack::detail::push_as_upvalues(L, target); int upvalues = stack::detail::push_as_upvalues(L, target);
stack::pusher<lua_CFunction>{}.push(L, freefunc, upvalues); stack::push(L, freefunc, upvalues);
} }
template<typename TFx> template<typename Fx>
static void set_fx(lua_State* L, std::unique_ptr<base_function> luafunc) { static void set_fx(lua_State* L, std::unique_ptr<base_function> luafunc) {
auto&& metakey = userdata_traits<Unqualified<TFx>>::metatable; auto&& metakey = userdata_traits<Unqualified<Fx>>::metatable;
const char* metatablename = std::addressof(metakey[0]); const char* metatablename = std::addressof(metakey[0]);
base_function* target = luafunc.release(); base_function* target = luafunc.release();
void* userdata = reinterpret_cast<void*>(target); void* userdata = reinterpret_cast<void*>(target);
@ -164,27 +200,16 @@ struct pusher<function_t> {
lua_pushstring(L, "__gc"); lua_pushstring(L, "__gc");
stack::push(L, &base_function::gc); stack::push(L, &base_function::gc);
lua_settable(L, -3); lua_settable(L, -3);
lua_pop(L, 1);
} }
stack::detail::push_userdata<void*>(L, metatablename, userdata); stack::detail::push_userdata<void*>(L, metatablename, userdata);
stack::pusher<lua_CFunction>{}.push(L, freefunc, 1); stack::push(L, freefunc, 1);
}
template<typename TFx>
static void set_function(lua_State* L, TFx&& fx) {
typedef typename std::remove_pointer<Decay<TFx>>::type clean_fx;
set_isfunction_fx(std::is_function<clean_fx>(), L, std::forward<TFx>(fx));
}
template<typename TFx, typename TObj>
static void set_function(lua_State* L, TFx&& fx, TObj&& obj) {
set_lvalue_fx(Bool<std::is_lvalue_reference<TObj>::value || std::is_pointer<TObj>::value>(),
L, std::forward<TFx>(fx), std::forward<TObj>(obj));
} }
template <typename... Args> template <typename... Args>
static void push(lua_State* L, Args&&... args) { static void push(lua_State* L, Args&&... args) {
set_function(L, std::forward<Args>(args)...); set(L, std::forward<Args>(args)...);
} }
}; };

View File

@ -208,7 +208,8 @@ struct base_function {
template<typename Function> template<typename Function>
struct functor_function : public base_function { struct functor_function : public base_function {
typedef decltype(&Function::operator()) function_type; typedef decltype(&Function::operator()) function_type;
typedef function_traits<function_type> traits_type; typedef function_return_t<function_type> return_type;
typedef function_args_t<function_type> args_type;
Function fx; Function fx;
template<typename... FxArgs> template<typename... FxArgs>
@ -229,7 +230,6 @@ struct functor_function : public base_function {
template<typename... Ret, typename... Args> template<typename... Ret, typename... Args>
int operator()(types<Ret...>, types<Args...> t, lua_State* L) { int operator()(types<Ret...>, types<Args...> t, lua_State* L) {
typedef typename return_type<Ret...>::type return_type;
return_type r = stack::get_call(L, fx, t); return_type r = stack::get_call(L, fx, t);
std::ptrdiff_t nargs = sizeof...(Args); std::ptrdiff_t nargs = sizeof...(Args);
lua_pop(L, nargs); lua_pop(L, nargs);
@ -238,29 +238,30 @@ struct functor_function : public base_function {
} }
virtual int operator()(lua_State* L) override { virtual int operator()(lua_State* L) override {
return (*this)(tuple_types<typename traits_type::return_type>(), typename traits_type::args_type(), L); return (*this)(tuple_types<return_type>(), args_type(), L);
} }
}; };
template<typename Function, typename T> template<typename Function, typename T>
struct member_function : public base_function { struct member_function : public base_function {
typedef typename std::remove_pointer<typename std::decay<Function>::type>::type function_type; typedef typename std::remove_pointer<Decay<Function>>::type function_type;
typedef function_traits<function_type> traits_type; typedef function_return_t<function_type> return_type;
typedef function_args_t<function_type> args_type;
struct functor { struct functor {
T member; T member;
function_type invocation; function_type invocation;
template<typename... FxArgs> template<typename Tm, typename... FxArgs>
functor(T m, FxArgs&&... fxargs): member(std::move(m)), invocation(std::forward<FxArgs>(fxargs)...) {} functor(Tm&& m, FxArgs&&... fxargs): member(std::forward<Tm>(m)), invocation(std::forward<FxArgs>(fxargs)...) {}
template<typename... Args> template<typename... Args>
typename traits_type::return_type operator()(Args&&... args) { return_type operator()(Args&&... args) {
return (member.*invocation)(std::forward<Args>(args)...); return (member.*invocation)(std::forward<Args>(args)...);
} }
} fx; } fx;
template<typename... FxArgs> template<typename Tm, typename... FxArgs>
member_function(T m, FxArgs&&... fxargs): fx(std::move(m), std::forward<FxArgs>(fxargs)...) {} member_function(Tm&& m, FxArgs&&... fxargs): fx(std::forward<Tm>(m), std::forward<FxArgs>(fxargs)...) {}
template<typename... Args> template<typename... Args>
int operator()(types<void>, types<Args...> t, lua_State* L) { int operator()(types<void>, types<Args...> t, lua_State* L) {
@ -275,7 +276,6 @@ struct member_function : public base_function {
template<typename... Ret, typename... Args> template<typename... Ret, typename... Args>
int operator()(types<Ret...>, types<Args...> t, lua_State* L) { int operator()(types<Ret...>, types<Args...> t, lua_State* L) {
typedef typename return_type<Ret...>::type return_type;
return_type r = stack::get_call(L, fx, t); return_type r = stack::get_call(L, fx, t);
std::ptrdiff_t nargs = sizeof...(Args); std::ptrdiff_t nargs = sizeof...(Args);
lua_pop(L, nargs); lua_pop(L, nargs);
@ -284,7 +284,7 @@ struct member_function : public base_function {
} }
virtual int operator()(lua_State* L) override { virtual int operator()(lua_State* L) override {
return (*this)(tuple_types<typename traits_type::return_type>(), typename traits_type::args_type(), L); return (*this)(tuple_types<return_type>(), args_type(), L);
} }
}; };
@ -292,8 +292,8 @@ template<typename Function, typename Tp>
struct userdata_function : public base_function { struct userdata_function : public base_function {
typedef typename std::remove_pointer<Tp>::type T; typedef typename std::remove_pointer<Tp>::type T;
typedef typename std::remove_pointer<typename std::decay<Function>::type>::type function_type; typedef typename std::remove_pointer<typename std::decay<Function>::type>::type function_type;
typedef function_traits<function_type> traits_type; typedef function_args_t<function_type> args_type;
typedef typename traits_type::return_type return_type; typedef function_return_t<function_type> return_type;
detail::functor<T, function_type, return_type> fx; detail::functor<T, function_type, return_type> fx;
@ -347,7 +347,7 @@ struct userdata_function : public base_function {
fx.item = detail::get_ptr(stack::get<Tp>(L, 1)); fx.item = detail::get_ptr(stack::get<Tp>(L, 1));
if (fx.item == nullptr) if (fx.item == nullptr)
throw error("userdata for function call is null: are you using wrong call syntax? (use item:function(...) synax)"); throw error("userdata for function call is null: are you using wrong call syntax? (use item:function(...) synax)");
return (*this)(tuple_types<typename traits_type::return_type>(), typename traits_type::args_type(), L); return (*this)(tuple_types<return_type>(), args_type(), L);
} }
}; };

View File

@ -39,6 +39,11 @@ inline T* get_ptr(T& val) {
return std::addressof(val); return std::addressof(val);
} }
template<typename T>
inline T* get_ptr(std::reference_wrapper<T> val) {
return std::addressof(val.get());
}
template<typename T> template<typename T>
inline T* get_ptr(T* val) { inline T* get_ptr(T* val) {
return val; return val;
@ -310,8 +315,8 @@ inline void push(lua_State* L, T&& t, Args&&... args) {
// overload allows to use a pusher of a specific type, but pass in any kind of args // overload allows to use a pusher of a specific type, but pass in any kind of args
template<typename T, typename Arg, typename... Args> template<typename T, typename Arg, typename... Args>
inline void push(lua_State* L, Arg&& t, Args&&... args) { inline void push(lua_State* L, Arg&& arg, Args&&... args) {
pusher<Unqualified<T>>{}.push(L, std::forward<T>(t), std::forward<Args>(args)...); pusher<Unqualified<T>>{}.push(L, std::forward<Arg>(arg), std::forward<Args>(args)...);
} }
inline void push_args(lua_State*) { inline void push_args(lua_State*) {

View File

@ -61,6 +61,10 @@ public:
lua_atpanic(L.get(), detail::atpanic); lua_atpanic(L.get(), detail::atpanic);
} }
lua_State* lua_state() const {
return L.get();
}
template<typename... Args> template<typename... Args>
void open_libraries(Args&&... args) { void open_libraries(Args&&... args) {
static_assert(are_same<lib, Args...>::value, "all types must be libraries"); static_assert(are_same<lib, Args...>::value, "all types must be libraries");
@ -133,21 +137,14 @@ public:
return *this; return *this;
} }
template<typename T, typename TFx>
state& set_function(T&& key, TFx&& fx) {
global.set_function(std::forward<T>(key), std::forward<TFx>(fx));
return *this;
}
template<typename T, typename TFx, typename TObj>
state& set_function(T&& key, TFx&& fx, TObj&& obj) {
global.set_function(std::forward<T>(key), std::forward<TFx>(fx), std::forward<TObj>(obj));
return *this;
}
template<typename T> template<typename T>
state& set_userdata(userdata<T>& user) { state& set_userdata(userdata<T>& user) {
global.set_userdata(user); return set_userdata(user.name(), user);
}
template<typename Key, typename T>
state& set_userdata(Key&& key, userdata<T>& user) {
global.set_userdata(std::forward<Key>(key), user);
return *this; return *this;
} }
@ -198,8 +195,34 @@ public:
return global[std::forward<T>(key)]; return global[std::forward<T>(key)];
} }
lua_State* lua_state() const { template<typename... Args, typename R, typename Key>
return L.get(); state& set_function(Key&& key, R fun_ptr(Args...)){
global.set_function(std::forward<Key>(key), fun_ptr);
return *this;
}
template<typename Sig, typename Key>
state& set_function(Key&& key, Sig* fun_ptr){
global.set_function(std::forward<Key>(key), fun_ptr);
return *this;
}
template<typename... Args, typename R, typename C, typename T, typename Key>
state& set_function(Key&& key, R (C::*mem_ptr)(Args...), T&& obj) {
global.set_function(std::forward<Key>(key), mem_ptr, std::forward<T>(obj));
return *this;
}
template<typename Sig, typename C, typename T, typename Key>
state& set_function(Key&& key, Sig C::* mem_ptr, T&& obj) {
global.set_function(std::forward<Key>(key), mem_ptr, std::forward<T>(obj));
return *this;
}
template<typename... Sig, typename Fx, typename Key>
state& set_function(Key&& key, Fx&& fx) {
global.set_function<Sig...>(std::forward<Key>(key), std::forward<Fx>(fx));
return *this;
} }
}; };
} // sol } // sol

View File

@ -83,23 +83,16 @@ public:
return *this; return *this;
} }
template<typename T, typename TFx>
table& set_function(T&& key, TFx&& fx) {
typedef typename std::remove_pointer<Decay<TFx>>::type clean_fx;
return set_isfunction_fx(std::is_function<clean_fx>(), std::forward<T>(key), std::forward<TFx>(fx));
}
template<typename T, typename TFx, typename TObj>
table& set_function(T&& key, TFx&& fx, TObj&& obj) {
return set_lvalue_fx(Bool<std::is_lvalue_reference<TObj>::value || std::is_pointer<TObj>::value>(),
std::forward<T>(key), std::forward<TFx>(fx), std::forward<TObj>(obj));
}
template<typename T> template<typename T>
table& set_userdata(userdata<T>& user) { table& set_userdata(userdata<T>& user) {
stack::push(state(), user); return set_userdata(user.name(), user);
lua_setglobal(state(), user.name().c_str()); }
template<typename Key, typename T>
table& set_userdata(Key&& key, userdata<T>& user) {
std::string ukey(std::forward<Key>(key));
stack::push(state(), user);
lua_setglobal(state(), ukey.c_str());
return *this; return *this;
} }
@ -121,121 +114,63 @@ public:
void pop(int n = 1) const noexcept { void pop(int n = 1) const noexcept {
lua_pop(state(), n); lua_pop(state(), n);
} }
template<typename... Args, typename R, typename Key>
table& set_function(Key&& key, R fun_ptr(Args...)){
set_resolved_function(std::forward<Key>(key), fun_ptr);
return *this;
}
template<typename Sig, typename Key>
table& set_function(Key&& key, Sig* fun_ptr){
set_resolved_function(std::forward<Key>(key), fun_ptr);
return *this;
}
template<typename... Args, typename R, typename C, typename T, typename Key>
table& set_function(Key&& key, R (C::*mem_ptr)(Args...), T&& obj) {
set_resolved_function(std::forward<Key>(key), mem_ptr, std::forward<T>(obj));
return *this;
}
template<typename Sig, typename C, typename T, typename Key>
table& set_function(Key&& key, Sig C::* mem_ptr, T&& obj) {
set_resolved_function(std::forward<Key>(key), mem_ptr, std::forward<T>(obj));
return *this;
}
template<typename... Sig, typename Fx, typename Key>
table& set_function(Key&& key, Fx&& fx) {
set_fx(types<Sig...>(), std::forward<Key>(key), std::forward<Fx>(fx));
return *this;
}
private: private:
template<typename T, typename TFx> template<typename R, typename... Args, typename Fx, typename Key, typename = typename std::result_of<Fx(Args...)>::type>
table& set_isfunction_fx(std::true_type, T&& key, TFx&& fx) { void set_fx(types<R(Args...)>, Key&& key, Fx&& fx) {
return set_fx(std::false_type(), std::forward<T>(key), std::forward<TFx>(fx)); set_resolved_function<R(Args...)>(std::forward<Key>(key), std::forward<Fx>(fx));
} }
template<typename T, typename TFx> template<typename... Args, typename Fx, typename Key, typename R = typename std::result_of<Fx(Args...)>::type>
table& set_isfunction_fx(std::false_type, T&& key, TFx&& fx) { void set_fx(types<Args...>, Key&& key, Fx&& fx){
typedef Decay<TFx> clean_lambda; set_fx(types<R(Args...)>(), std::forward<Key>(key), std::forward<Fx>(fx));
typedef typename function_traits<decltype(&clean_lambda::operator())>::free_function_pointer_type raw_func_t;
typedef std::is_convertible<clean_lambda, raw_func_t> isconvertible;
return set_isconvertible_fx(isconvertible(), std::forward<T>(key), std::forward<TFx>(fx));
} }
template<typename T, typename TFx> template<typename Fx, typename Key>
table& set_isconvertible_fx(std::true_type, T&& key, TFx&& fx) { void set_fx(types<>, Key&& key, Fx&& fx) {
typedef Decay<TFx> clean_lambda; typedef Unqualified<Unwrap<Fx>> fx_t;
typedef typename function_traits<decltype(&clean_lambda::operator())>::free_function_pointer_type raw_func_t; typedef decltype(&fx_t::operator()) Sig;
return set_isfunction_fx(std::true_type(), std::forward<T>(key), raw_func_t(std::forward<TFx>(fx))); set_fx(types<function_signature_t<Sig>>(), std::forward<Key>(key), std::forward<Fx>(fx));
} }
template<typename T, typename TFx> template<typename... Sig, typename... Args, typename Key>
table& set_isconvertible_fx(std::false_type, T&& key, TFx&& fx) { void set_resolved_function(Key&& key, Args&&... args) {
typedef typename std::remove_pointer<Decay<TFx>>::type clean_fx; std::string fkey(std::forward<Key>(key));
std::unique_ptr<base_function> sptr(new functor_function<clean_fx>(std::forward<TFx>(fx)));
return set_fx(std::forward<T>(key), std::move(sptr));
}
template<typename T, typename TFx, typename TObj>
table& set_lvalue_fx(std::true_type, T&& key, TFx&& fx, TObj&& obj) {
return set_fx(std::true_type(), std::forward<T>(key), std::forward<TFx>(fx), std::forward<TObj>(obj));
}
template<typename T, typename TFx, typename TObj>
table& set_lvalue_fx(std::false_type, T&& key, TFx&& fx, TObj&& obj) {
typedef typename std::remove_pointer<Decay<TFx>>::type clean_fx;
std::unique_ptr<base_function> sptr(new member_function<clean_fx, TObj>(std::forward<TObj>(obj), std::forward<TFx>(fx)));
return set_fx(std::forward<T>(key), std::move(sptr));
}
template<typename T, typename TFx, typename TObj>
table& set_fx(std::true_type, T&& key, TFx&& fx, TObj&& obj) {
std::string fkey(key);
// Layout:
// idx 1...n: verbatim data of member function pointer
// idx n + 1: is the object's void pointer
// We don't need to store the size, because the other side is templated
// with the same member function pointer type
Decay<TFx> fxptr(std::forward<TFx>(fx));
void* userobjdata = static_cast<void*>(detail::get_ptr(obj));
lua_CFunction freefunc = &static_member_function<Decay<TObj>, TFx>::call;
const char* freefuncname = fkey.c_str();
const luaL_Reg funcreg[2] = {
{ freefuncname, freefunc },
{ nullptr, nullptr }
};
push(); push();
int tabletarget = lua_gettop(state());
int upvalues = stack::detail::push_as_upvalues(state(), fxptr); stack::push<function_sig_t<Sig...>>(state(), std::forward<Args>(args)...);
stack::push(state(), userobjdata); lua_setfield(state(), tabletarget, fkey.c_str());
luaL_setfuncs(state(), funcreg, upvalues + 1);
pop(); pop();
return *this;
}
template<typename T, typename TFx>
table& set_fx(std::false_type, T&& key, TFx&& fx) {
std::string fkey(key);
Decay<TFx> target(std::forward<TFx>(fx));
lua_CFunction freefunc = &static_function<TFx>::call;
const char* freefuncname = fkey.c_str();
const luaL_Reg funcreg[2] = {
{ freefuncname, freefunc },
{ nullptr, nullptr }
};
push();
int upvalues = stack::detail::push_as_upvalues(state(), target);
luaL_setfuncs(state(), funcreg, upvalues);
pop();
return *this;
}
template<typename T>
table& set_fx(T&& key, std::unique_ptr<base_function> luafunc) {
std::string fkey(key);
std::string metakey("sol.stateful.");
metakey += fkey;
metakey += ".meta";
base_function* target = luafunc.release();
void* userdata = reinterpret_cast<void*>(target);
lua_CFunction freefunc = &base_function::call;
const char* freefuncname = fkey.c_str();
const char* metatablename = metakey.c_str();
const luaL_Reg funcreg[2] = {
{ freefuncname, freefunc },
{ nullptr, nullptr }
};
if (luaL_newmetatable(state(), metatablename) == 1) {
lua_pushstring(state(), "__gc");
lua_pushcclosure(state(), &base_function::gc, 0);
lua_settable(state(), -3);
}
push();
stack::detail::push_userdata<void*>(state(), metatablename, userdata);
luaL_setfuncs(state(), funcreg, 1);
pop();
return *this;
} }
}; };
} // sol } // sol

View File

@ -24,11 +24,33 @@
#include "tuple.hpp" #include "tuple.hpp"
#include <type_traits> #include <type_traits>
#include <functional>
namespace sol { namespace sol {
template <typename T> template <typename T>
struct identity { typedef T type; }; struct identity { typedef T type; };
template<typename... Args>
struct is_tuple : std::false_type{ };
template<typename... Args>
struct is_tuple<std::tuple<Args...>> : std::true_type{ };
template <typename T>
struct unwrap {
typedef T type;
};
template <typename T>
struct unwrap<std::reference_wrapper<T>> {
typedef typename std::add_lvalue_reference<T>::type type;
};
template <typename T, template <typename...> class Templ>
struct is_specialization_of : std::false_type { };
template <typename... T, template <typename...> class Templ>
struct is_specialization_of<Templ<T...>, Templ> : std::true_type { };
template<class T, class...> template<class T, class...>
struct are_same : std::true_type { }; struct are_same : std::true_type { };
@ -68,6 +90,9 @@ using Unqualified = typename std::remove_cv<typename std::remove_reference<T>::t
template<typename T> template<typename T>
using Decay = typename std::decay<T>::type; using Decay = typename std::decay<T>::type;
template <typename T>
using Unwrap = typename unwrap<T>::type;
template<typename... Args> template<typename... Args>
struct return_type { struct return_type {
typedef std::tuple<Args...> type; typedef std::tuple<Args...> type;
@ -83,17 +108,6 @@ struct return_type<> : types<>{
typedef void type; typedef void type;
}; };
template<typename... Args>
struct is_tuple : std::false_type{ };
template<typename... Args>
struct is_tuple<std::tuple<Args...>> : std::true_type{ };
template <typename T, template <typename...> class Templ>
struct is_specialization_of : std::false_type { };
template <typename... T, template <typename...> class Templ>
struct is_specialization_of<Templ<T...>, Templ> : std::true_type { };
namespace detail { namespace detail {
template<typename T, bool isclass = std::is_class<Unqualified<T>>::value> template<typename T, bool isclass = std::is_class<Unqualified<T>>::value>
struct is_function_impl : std::is_function<typename std::remove_pointer<T>::type> {}; struct is_function_impl : std::is_function<typename std::remove_pointer<T>::type> {};
@ -123,6 +137,15 @@ struct Function : Bool<detail::is_function_impl<T>::value> {};
template<typename TFuncSignature> template<typename TFuncSignature>
struct function_traits; struct function_traits;
template <typename TFuncSignature>
using function_args_t = typename function_traits<TFuncSignature>::args_type;
template <typename TFuncSignature>
using function_signature_t = typename function_traits<TFuncSignature>::signature_type;
template <typename TFuncSignature>
using function_return_t = typename function_traits<TFuncSignature>::return_type;
template<typename T, typename R, typename... Args> template<typename T, typename R, typename... Args>
struct function_traits<R(T::*)(Args...)> { struct function_traits<R(T::*)(Args...)> {
static const std::size_t arity = sizeof...(Args); static const std::size_t arity = sizeof...(Args);
@ -133,6 +156,7 @@ struct function_traits<R(T::*)(Args...)> {
typedef typename std::remove_pointer<function_pointer_type>::type function_type; typedef typename std::remove_pointer<function_pointer_type>::type function_type;
typedef R(*free_function_pointer_type)(Args...); typedef R(*free_function_pointer_type)(Args...);
typedef R return_type; typedef R return_type;
typedef typename std::remove_pointer<free_function_pointer_type>::type signature_type;
template<std::size_t i> template<std::size_t i>
using arg = typename std::tuple_element<i, arg_tuple_type>::type; using arg = typename std::tuple_element<i, arg_tuple_type>::type;
}; };
@ -147,6 +171,7 @@ struct function_traits<R(T::*)(Args...) const> {
typedef typename std::remove_pointer<function_pointer_type>::type function_type; typedef typename std::remove_pointer<function_pointer_type>::type function_type;
typedef R(*free_function_pointer_type)(Args...); typedef R(*free_function_pointer_type)(Args...);
typedef R return_type; typedef R return_type;
typedef typename std::remove_pointer<free_function_pointer_type>::type signature_type;
template<std::size_t i> template<std::size_t i>
using arg = typename std::tuple_element<i, arg_tuple_type>::type; using arg = typename std::tuple_element<i, arg_tuple_type>::type;
}; };
@ -161,6 +186,7 @@ struct function_traits<R(Args...)> {
typedef R(*function_pointer_type)(Args...); typedef R(*function_pointer_type)(Args...);
typedef R(*free_function_pointer_type)(Args...); typedef R(*free_function_pointer_type)(Args...);
typedef R return_type; typedef R return_type;
typedef typename std::remove_pointer<free_function_pointer_type>::type signature_type;
template<std::size_t i> template<std::size_t i>
using arg = typename std::tuple_element<i, arg_tuple_type>::type; using arg = typename std::tuple_element<i, arg_tuple_type>::type;
}; };
@ -175,6 +201,7 @@ struct function_traits<R(*)(Args...)> {
typedef R(*function_pointer_type)(Args...); typedef R(*function_pointer_type)(Args...);
typedef R(*free_function_pointer_type)(Args...); typedef R(*free_function_pointer_type)(Args...);
typedef R return_type; typedef R return_type;
typedef typename std::remove_pointer<free_function_pointer_type>::type signature_type;
template<std::size_t i> template<std::size_t i>
using arg = typename std::tuple_element<i, arg_tuple_type>::type; using arg = typename std::tuple_element<i, arg_tuple_type>::type;
}; };
@ -205,6 +232,16 @@ struct has_key_value_pair_impl {
template<typename T> template<typename T>
struct has_key_value_pair : decltype(has_key_value_pair_impl::test<T>(0)) {}; struct has_key_value_pair : decltype(has_key_value_pair_impl::test<T>(0)) {};
template <typename T>
auto unwrapper(T&& item) -> decltype(std::forward<T>(item)) {
return std::forward<T>(item);
}
template <typename Arg>
Unwrap<Arg> unwrapper(std::reference_wrapper<Arg> arg) {
return arg.get();
}
} // sol } // sol
#endif // SOL_TRAITS_HPP #endif // SOL_TRAITS_HPP

View File

@ -31,7 +31,11 @@ struct nil_t {};
const nil_t nil {}; const nil_t nil {};
struct void_type {}; struct void_type {};
const void_type Void {}; const void_type Void {};
struct function_t {};
template <typename... T>
struct function_sig_t {};
using function_t = function_sig_t<>;
struct upvalue_t { struct upvalue_t {
void* value; void* value;
upvalue_t(void* data) : value(data) {} upvalue_t(void* data) : value(data) {}

View File

@ -108,6 +108,36 @@ private:
} }
}; };
template <bool release = false, typename TCont>
static int push_upvalues (lua_State* L, TCont&& cont) {
int n = 0;
for (auto& c : cont) {
if (release)
stack::push<upvalue_t>(L, c.release());
else
stack::push<upvalue_t>(L, c.get());
++n;
}
return n;
}
template <typename Meta, typename Funcs, typename FuncTable, typename MetaFuncs, typename MetaFuncTable>
static void push_metatable(lua_State* L, Meta&& meta, Funcs&& funcs, FuncTable&& functable, MetaFuncs&& metafuncs, MetaFuncTable&& metafunctable) {
luaL_newmetatable(L, std::addressof(meta[0]));
if (functable.size() > 1) {
// regular functions accessed through __index semantics
int up = push_upvalues(L, funcs);
luaL_setfuncs(L, functable.data(), up);
}
if (metafunctable.size() > 1) {
// meta functions
int up = push_upvalues(L, metafuncs);
luaL_setfuncs(L, metafunctable.data(), up);
}
lua_pushvalue(L, -1);
lua_setfield(L, -1, "__index");
}
template<std::size_t N, std::size_t M> template<std::size_t N, std::size_t M>
void build_function_tables() {} void build_function_tables() {}
@ -177,49 +207,33 @@ public:
userdata(const char* name, constructors<CArgs...> c, Args&&... args) : userdata(const char* name, constructors<CArgs...> c, Args&&... args) :
userdata(std::string(name), std::move(c), std::forward<Args>(args)...) {} userdata(std::string(name), std::move(c), std::forward<Args>(args)...) {}
std::vector<std::string>& function_names () {
return functionnames;
}
std::vector<std::unique_ptr<base_function>>& functions () {
return funcs;
}
std::vector<std::unique_ptr<base_function>>& reference_functions () {
return ptrfuncs;
}
std::vector<std::unique_ptr<base_function>>& meta_functions () {
return metafuncs;
}
std::vector<std::unique_ptr<base_function>>& meta_reference_functions () {
return ptrmetafuncs;
}
std::vector<luaL_Reg>& function_table () {
return functiontable;
}
std::vector<luaL_Reg>& reference_function_table () {
return ptrfunctiontable;
}
std::vector<luaL_Reg>& meta_function_table () {
return metafunctiontable;
}
std::vector<luaL_Reg>& meta_reference_function_table () {
return ptrmetafunctiontable;
}
lua_CFunction cleanup_function () {
return cleanup;
}
const std::string& name () const { const std::string& name () const {
return luaname; return luaname;
} }
void push (lua_State* L) {
// push pointer tables first,
// but leave the regular T table on last so it can be linked to a type for usage with `.new(...)`
push_metatable(L, userdata_traits<T*>::metatable,
ptrfuncs, ptrfunctiontable,
ptrmetafuncs, ptrmetafunctiontable);
push_metatable(L, userdata_traits<T>::metatable,
funcs, functiontable,
metafuncs, metafunctiontable);
// Automatic deleter table -- stays alive until lua VM dies
// even if the user calls collectgarbage()
lua_createtable(L, 0, 0);
lua_createtable(L, 0, 1);
int up = push_upvalues<true>(L, funcs);
up += push_upvalues<true>(L, ptrfuncs);
up += push_upvalues<true>(L, metafuncs);
up += push_upvalues<true>(L, ptrmetafuncs);
lua_pushcclosure(L, cleanup, up);
lua_setfield(L, -2, "__gc");
lua_setmetatable(L, -2);
// gctable name by default has ♻ part of it
lua_setglobal(L, std::addressof(userdata_traits<T>::gctable[0]));
}
}; };
template <typename T> template <typename T>
@ -248,56 +262,8 @@ const std::array<std::string, 19> userdata<T>::metafunctionnames = {
namespace stack { namespace stack {
template <typename T> template <typename T>
struct pusher<userdata<T>> { struct pusher<userdata<T>> {
template <bool release = false, typename TCont>
static int push_upvalues (lua_State* L, TCont&& cont) {
int n = 0;
for (auto& c : cont) {
if (release)
stack::push<upvalue_t>(L, c.release());
else
stack::push<upvalue_t>(L, c.get());
++n;
}
return n;
}
template <typename Meta, typename Funcs, typename FuncTable, typename MetaFuncs, typename MetaFuncTable>
static void push_metatable(lua_State* L, Meta&& meta, Funcs&& funcs, FuncTable&& functable, MetaFuncs&& metafuncs, MetaFuncTable&& metafunctable) {
luaL_newmetatable(L, std::addressof(meta[0]));
if (functable.size() > 1) {
// regular functions accessed through __index semantics
int up = push_upvalues(L, funcs);
luaL_setfuncs(L, functable.data(), up);
}
if (metafunctable.size() > 1) {
// meta functions
int up = push_upvalues(L, metafuncs);
luaL_setfuncs(L, metafunctable.data(), up);
}
lua_pushvalue(L, -1);
lua_setfield(L, -1, "__index");
}
static void push (lua_State* L, userdata<T>& user) { static void push (lua_State* L, userdata<T>& user) {
// push pointer tables first, user.push(L);
// but leave the regular T table on last so it can be linked to a type for usage with `.new(...)`
push_metatable(L, userdata_traits<T*>::metatable, user.reference_functions(), user.reference_function_table(), user.meta_reference_functions(), user.meta_reference_function_table());
push_metatable(L, userdata_traits<T>::metatable, user.functions(), user.function_table(), user.meta_functions(), user.meta_function_table());
// Automatic deleter table -- stays alive until lua VM dies
// even if the user calls collectgarbage()
auto cleanup = user.cleanup_function();
lua_createtable(L, 0, 0);
lua_createtable(L, 0, 1);
int up = push_upvalues<true>(L, user.functions());
up += push_upvalues<true>(L, user.reference_functions());
up += push_upvalues<true>(L, user.meta_functions());
up += push_upvalues<true>(L, user.meta_reference_functions());
lua_pushcclosure(L, cleanup, up);
lua_setfield(L, -2, "__gc");
lua_setmetatable(L, -2);
// gctable name by default has ♻ part of it
lua_setglobal(L, std::addressof(userdata_traits<T>::gctable[0]));
} }
}; };
} // stack } // stack

View File

@ -31,6 +31,26 @@ std::string free_function() {
return "test"; return "test";
} }
int overloaded(int x) {
std::cout << x << std::endl;
return 3;
}
int overloaded(int x, int y) {
std::cout << x << " " << y << std::endl;
return 7;
}
int overloaded(int x, int y, int z) {
std::cout << x << " " << y << " " << z << std::endl;
return 11;
}
int non_overloaded(int x, int y, int z) {
std::cout << x << " " << y << " " << z << std::endl;
return 13;
}
std::vector<int> test_table_return_one() { std::vector<int> test_table_return_one() {
return { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; return { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
} }
@ -332,25 +352,33 @@ TEST_CASE("tables/functions_variables", "Check if tables and function calls work
std::cout << "stateless lambda()" << std::endl; std::cout << "stateless lambda()" << std::endl;
return "test"; return "test";
} }
); );
REQUIRE_NOTHROW(run_script(lua)); REQUIRE_NOTHROW(run_script(lua));
lua.get<sol::table>("os").set_function("fun", &free_function); lua.get<sol::table>("os").set_function("fun", &free_function);
REQUIRE_NOTHROW(run_script(lua)); REQUIRE_NOTHROW(run_script(lua));
// l-value, can optimise // l-value, canNOT optimise
// prefer value semantics unless wrapped with std::reference_wrapper
{
auto lval = object(); auto lval = object();
lua.get<sol::table>("os").set_function("fun", &object::operator(), lval); lua.get<sol::table>("os").set_function("fun", &object::operator(), lval);
}
REQUIRE_NOTHROW(run_script(lua)); REQUIRE_NOTHROW(run_script(lua));
auto reflval = object();
lua.get<sol::table>("os").set_function("fun", &object::operator(), std::ref(reflval));
REQUIRE_NOTHROW(run_script(lua));
// stateful lambda: non-convertible, cannot be optimised // stateful lambda: non-convertible, cannot be optimised
int breakit = 50; int breakit = 50;
lua.get<sol::table>("os").set_function("fun", lua.get<sol::table>("os").set_function("fun",
[&breakit] () { [&breakit] () {
std::cout << "stateless lambda()" << std::endl; std::cout << "stateful lambda()" << std::endl;
return "test"; return "test";
} }
); );
REQUIRE_NOTHROW(run_script(lua)); REQUIRE_NOTHROW(run_script(lua));
// r-value, cannot optimise // r-value, cannot optimise
@ -363,6 +391,26 @@ TEST_CASE("tables/functions_variables", "Check if tables and function calls work
REQUIRE_NOTHROW(run_script(lua)); REQUIRE_NOTHROW(run_script(lua));
} }
TEST_CASE("functions/overloaded", "Check if overloaded function resolution templates compile/work") {
sol::state lua;
lua.open_libraries(sol::lib::base);
lua.set_function("non_overloaded", non_overloaded);
REQUIRE_NOTHROW(lua.script("x = non_overloaded(1)\nprint(x)"));
lua.set_function<int>("overloaded", overloaded);
REQUIRE_NOTHROW(lua.script("print(overloaded(1))"));
lua.set_function<int, int>("overloaded", overloaded);
REQUIRE_NOTHROW(lua.script("print(overloaded(1, 2))"));
lua.set_function<int(int, int)>("overloaded", overloaded);
REQUIRE_NOTHROW(lua.script("print(overloaded(1, 2))"));
lua.set_function<int, int, int>("overloaded", overloaded);
REQUIRE_NOTHROW(lua.script("print(overloaded(1, 2, 3))"));
}
TEST_CASE("functions/return_order_and_multi_get", "Check if return order is in the same reading order specified in Lua") { TEST_CASE("functions/return_order_and_multi_get", "Check if return order is in the same reading order specified in Lua") {
const static std::tuple<int, int, int> triple = std::make_tuple(10, 11, 12); const static std::tuple<int, int, int> triple = std::make_tuple(10, 11, 12);
sol::state lua; sol::state lua;
@ -730,10 +778,10 @@ TEST_CASE("userdata/lua-stored-userdata", "ensure userdata values can be stored
// userdata dies, but still usable in lua! // userdata dies, but still usable in lua!
} }
lua.script("collectgarbage()\n" REQUIRE_NOTHROW(lua.script("collectgarbage()\n"
"v = Vec.new(1, 2, 3)\n" "v = Vec.new(1, 2, 3)\n"
"print(v:length())"); "print(v:length())"));
lua.script("v = Vec.new(1, 2, 3)\n" REQUIRE_NOTHROW(lua.script("v = Vec.new(1, 2, 3)\n"
"print(v:normalized():length())" ); "print(v:normalized():length())" ));
} }