massive rework of stack

get turned into getter<T>, matches pusher<T> and uses same semantics as std::allocator and other things used throughout the codebase
-----
userdata has its traits defined outside in new file of userdata to prevent errors when trying to use those typetraits in places before userdata.hpp gets included
userdata was changed to support returning itself via pointers or references.
rework of stack changes semantics based on T&, T*, and T&& (the last one tries to create a new userdata and move in data)
solves problems maybe presented in https://github.com/Rapptz/sol/issues/25

-----
container.hpp is attempt at solving original problem before going on wild tangent with userdata, stack, and get
is going to attempt to use userdata to allow transporation of containers losslessly, perhaps without copying need
-----
found out trying to return a std::function does not work -- not sure what do exactly?
perhaps should push c closure as last thing, but right now it is tied to a key value (code comes from table.hpp and set_function)
will just have to think over how stack arranges itself and learn what to do
This commit is contained in:
PrincessNyanara 2014-06-06 22:54:45 -04:00 committed by Rapptz
parent ab935a01a2
commit 77901bb654
10 changed files with 609 additions and 268 deletions

View File

@ -25,5 +25,6 @@
#include "sol/state.hpp" #include "sol/state.hpp"
#include "sol/object.hpp" #include "sol/object.hpp"
#include "sol/function.hpp" #include "sol/function.hpp"
#include "sol/container.hpp"
#endif // SOL_HPP #endif // SOL_HPP

89
sol/container.hpp Normal file
View File

@ -0,0 +1,89 @@
#ifndef SOL_CONTAINER_HPP
#define SOL_CONTAINER_HPP
#include "userdata.hpp"
namespace sol {
template <typename Tc>
struct container {
typedef typename std::conditional<std::is_lvalue_reference<Tc>::value, Tc, Decay<Tc>>::type T;
typedef Decay<decltype(*(std::declval<T>().begin()))> value_type;
T cont;
template <typename... Args>
container (Args&&... args) : cont(std::forward<Args>(args)...){
}
operator T& () const {
return cont;
}
void set( std::ptrdiff_t i, const value_type& value ) {
cont[ i ] = value;
}
value_type& get( std::ptrdiff_t i ) {
return cont[ i ];
}
std::size_t size( ) const {
return cont.size();
}
};
namespace stack {
template<typename T>
struct pusher<T, typename std::enable_if<has_begin_end<T>::value>::type> {
template <typename U>
static void push(lua_State *L, U&& t){
typedef container<U> container_t;
// todo: NEED to find a different way of handling this...
static std::vector<std::shared_ptr<void>> classes{};
userdata<container_t> classdata(default_constructor,
"__index", &container_t::get,
"__newindex", &container_t::set,
"__len", &container_t::size);
classes.emplace_back(std::make_shared<userdata<container_t>>(std::move(classdata)));
auto&& ptr = classes.back();
auto udata = std::static_pointer_cast<userdata<container_t>>(ptr);
stack::push(L, *udata);
container_t* c = static_cast<container_t*>(lua_newuserdata(L, sizeof(container_t)));
std::allocator<container_t> alloc{};
alloc.construct(c, std::forward<U>(t));
auto&& meta = userdata_traits<T>::metatable;
luaL_getmetatable(L, std::addressof(meta[0]));
lua_setmetatable(L, -2);
}
template<typename U = T, EnableIf<Not<has_key_value_pair<U>>> = 0>
static void push(lua_State* L, const T& cont) {
lua_createtable(L, cont.size(), 0);
unsigned index = 1;
for(auto&& i : cont) {
// push the index
pusher<unsigned>::push(L, index++);
// push the value
pusher<Unqualified<decltype(i)>>::push(L, i);
// set the table
lua_settable(L, -3);
}
}
template<typename U = T, EnableIf<has_key_value_pair<U>> = 0>
static void push(lua_State* L, const T& cont) {
lua_createtable(L, cont.size(), 0);
for(auto&& pair : cont) {
pusher<Unqualified<decltype(pair.first)>>::push(L, pair.first);
pusher<Unqualified<decltype(pair.second)>>::push(L, pair.second);
lua_settable(L, -3);
}
}
};
} // stack
} // sol
#endif // SOL_CONTAINER_HPP

View File

@ -76,15 +76,151 @@ public:
template<typename... Ret, typename... Args> template<typename... Ret, typename... Args>
typename return_type<Ret...>::type call(Args&&... args) const { typename return_type<Ret...>::type call(Args&&... args) const {
push(); push();
stack::push_args(state(), std::forward<Args>(args)...); stack::push(state(), std::forward<Args>(args)...);
return invoke(types<Ret...>(), sizeof...(Args)); return invoke(types<Ret...>(), sizeof...(Args));
} }
}; };
namespace stack { namespace stack {
namespace detail { template <>
template <typename Signature, typename... FxArgs, typename... Ret> struct pusher<function_t> {
inline std::function<Signature> get_std_func(types<FxArgs...>, types<Ret...>, lua_State* L, int index = -1) { template<typename T, typename TFx>
void set_isfunction_fx(lua_State* L, std::true_type, T&& key, TFx&& fx) {
set_fx(std::false_type(), std::forward<T>(key), std::forward<TFx>(fx));
}
template<typename T, typename TFx>
void set_isfunction_fx(lua_State* L, std::false_type, T&& key, TFx&& fx) {
typedef Decay<TFx> clean_lambda;
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;
set_isconvertible_fx(isconvertible(), std::forward<T>(key), std::forward<TFx>(fx));
}
template<typename T, typename TFx>
void set_isconvertible_fx(lua_State* L, std::true_type, T&& key, TFx&& fx) {
typedef Decay<TFx> clean_lambda;
typedef typename function_traits<decltype(&clean_lambda::operator())>::free_function_pointer_type raw_func_t;
set_isfunction_fx(std::true_type(), std::forward<T>(key), raw_func_t(std::forward<TFx>(fx)));
}
template<typename T, typename TFx>
void set_isconvertible_fx(lua_State* L, std::false_type, T&& key, TFx&& fx) {
typedef typename std::remove_pointer<Decay<TFx>>::type clean_fx;
std::unique_ptr<base_function> sptr(new functor_function<clean_fx>(std::forward<TFx>(fx)));
set_fx(std::forward<T>(key), std::move(sptr));
}
template<typename T, typename TFx, typename TObj>
void set_lvalue_fx(lua_State* L, std::true_type, T&& key, TFx&& fx, TObj&& obj) {
set_fx(std::true_type(), std::forward<T>(key), std::forward<TFx>(fx), std::forward<TObj>(obj));
}
template<typename T, typename TFx, typename TObj>
void set_lvalue_fx(lua_State* L, 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>
void set_fx(lua_State* L, 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 }
};
int upvalues = stack::detail::push_as_upvalues(L, fxptr);
stack::push(L, userobjdata);
luaL_setfuncs(L, funcreg, upvalues + 1);
}
template<typename T, typename TFx>
void set_fx(lua_State* L, 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 }
};
int upvalues = stack::detail::push_as_upvalues(L, target);
luaL_setfuncs(L, funcreg, upvalues);
}
template<typename T>
void set_fx(lua_State* L, 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(L, metatablename) == 1) {
lua_pushstring(L, "__gc");
lua_pushcclosure(L, &base_function::gc, 0);
lua_settable(L, -3);
}
stack::detail::push_userdata(L, userdata, metatablename);
luaL_setfuncs(L, funcreg, 1);
}
template<typename T, typename TFx>
void set_function(T&& key, TFx&& fx) {
typedef typename std::remove_pointer<Decay<TFx>>::type clean_fx;
set_isfunction_fx(std::is_function<clean_fx>(), std::forward<T>(key), std::forward<TFx>(fx));
}
template<typename T, typename TFx, typename TObj>
void set_function(T&& key, TFx&& fx, TObj&& obj) {
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 Key, typename... Args>
void push(lua_State* L, Key&& key, Args&&... args) {
set_function(L, std::forward<Key>(key), std::forward<Args>(args)...);
}
};
template <typename Signature>
struct pusher<std::function<Signature>> {
};
template <typename Signature>
struct getter<std::function<Signature>> {
typedef function_traits<Signature> fx_t;
typedef typename fx_t::args_type args_t;
typedef typename tuple_types<typename fx_t::return_type>::type ret_t;
template <typename... FxArgs, typename... Ret>
static std::function<Signature> get_std_func(types<FxArgs...>, types<Ret...>, lua_State* L, int index = -1) {
typedef typename function_traits<Signature>::return_type return_t; typedef typename function_traits<Signature>::return_type return_t;
sol::function f(L, index); sol::function f(L, index);
auto fx = [ f, L, index ] (FxArgs&&... args) -> return_t { auto fx = [ f, L, index ] (FxArgs&&... args) -> return_t {
@ -93,8 +229,8 @@ inline std::function<Signature> get_std_func(types<FxArgs...>, types<Ret...>, lu
return std::move(fx); return std::move(fx);
} }
template <typename Signature, typename... FxArgs> template <typename... FxArgs>
inline std::function<Signature> get_std_func(types<FxArgs...>, types<void>, lua_State* L, int index = -1) { static std::function<Signature> get_std_func(types<FxArgs...>, types<void>, lua_State* L, int index = -1) {
sol::function f(L, index); sol::function f(L, index);
auto fx = [ f, L, index ] (FxArgs&&... args) -> void { auto fx = [ f, L, index ] (FxArgs&&... args) -> void {
f(std::forward<FxArgs>(args)...); f(std::forward<FxArgs>(args)...);
@ -102,19 +238,15 @@ inline std::function<Signature> get_std_func(types<FxArgs...>, types<void>, lua_
return std::move(fx); return std::move(fx);
} }
template <typename Signature, typename... FxArgs> template <typename... FxArgs>
inline std::function<Signature> get_std_func(types<FxArgs...> t, types<>, lua_State* L, int index = -1) { static std::function<Signature> get_std_func(types<FxArgs...> t, types<>, lua_State* L, int index = -1) {
return get_std_func<Signature>(std::move(t), types<void>(), L, index); return get_std_func(std::move(t), types<void>(), L, index);
} }
template <typename Signature> static std::function<Signature> get(lua_State* L, int index) {
inline std::function<Signature> get(types<std::function<Signature>>, lua_State* L, int index) { return get_std_func(args_t(), ret_t(), L, index);
typedef function_traits<Signature> fx_t;
typedef typename fx_t::args_type args_t;
typedef typename tuple_types<typename fx_t::return_type>::type ret_t;
return get_std_func<Signature>(args_t(), ret_t(), L, index);
} }
} // detail };
} // stack } // stack
} // sol } // sol

View File

@ -26,6 +26,38 @@
#include <memory> #include <memory>
namespace sol { namespace sol {
namespace detail {
template <typename T, typename Func, typename R>
struct functor {
T* item;
Func invocation;
template<typename... FxArgs>
functor(FxArgs&&... fxargs): item(nullptr), invocation(std::forward<FxArgs>(fxargs)...) {}
template<typename... Args>
R operator()(Args&&... args) {
T& member = *item;
return (member.*invocation)(std::forward<Args>(args)...);
}
};
template <typename T, typename Func>
struct functor<T, Func, void> {
T* item;
Func invocation;
template<typename... FxArgs>
functor(FxArgs&&... fxargs): item(nullptr), invocation(std::forward<FxArgs>(fxargs)...) {}
template<typename... Args>
void operator()(Args&&... args) {
T& member = *item;
(member.*invocation)(std::forward<Args>(args)...);
}
};
} // detail
template<typename Function> template<typename Function>
struct static_function { struct static_function {
@ -56,7 +88,7 @@ struct static_function {
} }
static int call(lua_State* L) { static int call(lua_State* L) {
auto udata = stack::get_user<function_type*>(L); auto udata = stack::detail::get_as_upvalues<function_type*>(L);
function_type* fx = udata.first; function_type* fx = udata.first;
int r = typed_call(tuple_types<typename traits_type::return_type>(), typename traits_type::args_type(), fx, L); int r = typed_call(tuple_types<typename traits_type::return_type>(), typename traits_type::args_type(), fx, L);
return r; return r;
@ -98,8 +130,8 @@ struct static_member_function {
} }
static int call(lua_State* L) { static int call(lua_State* L) {
auto memberdata = stack::get_user<function_type>(L, 1); auto memberdata = stack::detail::get_as_upvalues<function_type>(L, 1);
auto objdata = stack::get_user<T*>(L, memberdata.second); auto objdata = stack::detail::get_as_upvalues<T*>(L, memberdata.second);
function_type& memfx = memberdata.first; function_type& memfx = memberdata.first;
T& obj = *objdata.first; T& obj = *objdata.first;
int r = typed_call(tuple_types<typename traits_type::return_type>(), typename traits_type::args_type(), obj, memfx, L); int r = typed_call(tuple_types<typename traits_type::return_type>(), typename traits_type::args_type(), obj, memfx, L);
@ -242,41 +274,36 @@ struct member_function : public base_function {
} }
}; };
template<typename Function, typename T> 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<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_traits<function_type> traits_type;
struct functor { typedef typename traits_type::return_type return_type;
T* item;
function_type invocation;
template<typename... FxArgs> detail::functor<T, function_type, return_type> fx;
functor(FxArgs&&... fxargs): item(nullptr), invocation(std::forward<FxArgs>(fxargs)...) {}
void prepare(lua_State* L) {
void* udata = stack::get<userdata_t>(L, 1);
if (udata == nullptr)
throw error("must use the syntax [object]:[function] to call member functions bound to C++");
item = static_cast<T*>(udata);
}
template<typename... Args>
typename traits_type::return_type operator()(Args&&... args) {
T& member = *item;
return (member.*invocation)(std::forward<Args>(args)...);
}
} fx;
template<typename... FxArgs> template<typename... FxArgs>
userdata_function(FxArgs&&... fxargs): fx(std::forward<FxArgs>(fxargs)...) {} userdata_function(FxArgs&&... fxargs): fx(std::forward<FxArgs>(fxargs)...) {}
template<typename Return, typename Raw = Unqualified<Return>> template<typename Return, typename Raw = Unqualified<Return>>
typename std::enable_if<std::is_same<T, Raw>::value, void>::type special_push(lua_State*, Return&&) { typename std::enable_if<std::is_same<T, Raw>::value, void>::type push(lua_State* L, Return&& r) {
if ( detail::get_ptr(r) == fx.item ) {
// push nothing // 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;
}
stack::push(L, std::forward<Return>(r));
} }
template<typename Return, typename Raw = Unqualified<Return>> template<typename Return, typename Raw = Unqualified<Return>>
typename std::enable_if<!std::is_same<T, Raw>::value, void>::type special_push(lua_State* L, Return&& r) { typename std::enable_if<!std::is_same<T, Raw>::value, void>::type push(lua_State* L, Return&& r) {
stack::push(L, std::forward<Return>(r)); stack::push(L, std::forward<Return>(r));
} }
@ -290,12 +317,10 @@ struct userdata_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, 2, fx, t); return_type r = stack::get_call(L, 2, fx, t);
std::ptrdiff_t nargs = sizeof...(Args); std::ptrdiff_t nargs = sizeof...(Args);
lua_pop(L, nargs); lua_pop(L, nargs);
// stack::push(L, std::move(r)); push(L, std::forward<return_type>(r));
special_push(L, r);
return sizeof...(Ret); return sizeof...(Ret);
} }
@ -305,7 +330,7 @@ struct userdata_function : public base_function {
} }
virtual int operator()(lua_State* L) override { virtual int operator()(lua_State* L) override {
fx.prepare(L); fx.item = detail::get_ptr(stack::get<Tp>(L, 1));
return (*this)(tuple_types<typename traits_type::return_type>(), typename traits_type::args_type(), L); return (*this)(tuple_types<typename traits_type::return_type>(), typename traits_type::args_type(), L);
} }
}; };

View File

@ -26,129 +26,141 @@
#include "reference.hpp" #include "reference.hpp"
#include "tuple.hpp" #include "tuple.hpp"
#include "traits.hpp" #include "traits.hpp"
#include "userdata_traits.hpp"
#include <utility> #include <utility>
#include <array> #include <array>
#include <cstring> #include <cstring>
#include <functional> #include <functional>
namespace sol { namespace sol {
namespace detail {
template<typename T>
T* get_ptr(T& val) {
return std::addressof(val);
}
template<typename T>
T* get_ptr(T* val) {
return val;
}
} // detail
namespace stack { namespace stack {
namespace detail { namespace detail {
inline nil_t get(types<nil_t>, lua_State* L, int index = -1) { template<typename T, typename Key>
if (lua_isnil(L, index) == 0) inline void push_userdata(lua_State* L, T&& userdata, Key&& metatablekey) {
throw sol::error("not nil"); T* pdatum = static_cast<T*>(lua_newuserdata(L, sizeof(T)));
return nil_t{ }; T& datum = *pdatum;
datum = std::forward<T>(userdata);
luaL_getmetatable(L, std::addressof(metatablekey[0]));
lua_setmetatable(L, -2);
}
} // detail
template <typename T, typename X = void>
struct getter;
template <typename T, typename X = void>
struct pusher;
template <typename T, typename>
struct getter {
template<typename U = T, EnableIf<std::is_floating_point<U>> = 0>
static U get(lua_State* L, int index = -1) {
return lua_tonumber(L, index);
} }
inline lightuserdata_t get(types<lightuserdata_t>, lua_State* L, int index = -1) { template<typename U = T, EnableIf<std::is_integral<U>, std::is_signed<U>> = 0>
return{ lua_touserdata(L, lua_upvalueindex(index)) }; static U get(lua_State* L, int index = -1) {
return lua_tounsigned(L, index);
} }
inline userdata_t get(types<userdata_t>, lua_State* L, int index = -1) { template<typename U = T, EnableIf<std::is_integral<U>, std::is_unsigned<U>> = 0>
return{ lua_touserdata(L, index) }; static U get(lua_State* L, int index = -1) {
return static_cast<T>(lua_tointeger(L, index));
} }
inline std::string get(types<std::string>, lua_State* L, int index = -1) { template<typename U = T, EnableIf<std::is_base_of<reference, U>> = 0>
static U get(lua_State* L, int index = -1) {
return T(L, index);
}
template<typename U = T, EnableIf<Not<std::is_base_of<reference, U>>, Not<std::is_integral<U>>, Not<std::is_floating_point<U>>> = 0>
static U& get(lua_State* L, int index = -1) {
void* udata = lua_touserdata(L, index);
T* obj = static_cast<T*>(udata);
return *obj;
}
};
template <typename T>
struct getter<T*> {
static T* get(lua_State* L, int index = -1) {
void* udata = lua_touserdata(L, index);
T** obj = static_cast<T**>(udata);
return *obj;
}
};
template <>
struct getter<type> {
type get(lua_State *L, int index){
return static_cast<type>(lua_type(L, index));
}
};
template <>
struct getter<bool> {
static bool get(lua_State* L, int index) {
return lua_toboolean(L, index) != 0;
}
};
template <>
struct getter<std::string> {
static std::string get(lua_State* L, int index = -1) {
std::string::size_type len; std::string::size_type len;
auto str = lua_tolstring(L, index, &len); auto str = lua_tolstring(L, index, &len);
return{ str, len }; return{ str, len };
} }
};
inline const char* get(types<const char*>, lua_State* L, int index = -1) {
return lua_tostring(L, index);
}
inline type get(types<type>, lua_State* L, int index = -1) {
return static_cast<type>(lua_type(L, index));
}
template <typename T, typename U = typename std::remove_reference<T>::type>
inline U get_sol_type(std::true_type, types<T>, lua_State* L, int index = -1) {
return U(L, index);
}
template <typename T>
inline T& get_sol_type(std::false_type, types<T>, lua_State* L, int index = -1) {
userdata_t udata = get(types<userdata_t>{}, L, index);
T* obj = static_cast<T*>(udata.value);
return *obj;
}
template <typename T, typename U = Unqualified<T>>
inline auto get(types<T> t, lua_State* L, int index = -1) -> decltype(get_sol_type(std::is_base_of<sol::reference, U>(), t, L, index)) {
return get_sol_type(std::is_base_of<sol::reference, U>(), t, L, index);
}
template <typename Signature>
inline std::function<Signature> get(types<std::function<Signature>>, lua_State* L, int index = -1);
template<typename T>
inline T get_unsigned(std::true_type, lua_State* L, int index = -1) {
return lua_tounsigned(L, index);
}
template<typename T>
inline T get_unsigned(std::false_type, lua_State* L, int index = -1) {
return static_cast<T>(lua_tointeger(L, index));
}
template<typename T>
inline T get_arithmetic(std::false_type, lua_State* L, int index = -1) {
// T is a floating point
return static_cast<T>(lua_tonumber(L, index));
}
template<typename T>
inline T get_arithmetic(std::true_type, lua_State* L, int index = -1) {
// T is an integral
return get_unsigned<T>(std::is_unsigned<T>{}, L, index);
}
template<typename T>
inline T get_helper(std::true_type, lua_State* L, int index = -1) {
// T is a fundamental type
return get_arithmetic<T>(std::is_integral<T>{}, L, index);
}
template <> template <>
inline bool get_helper<bool>(std::true_type, lua_State* L, int index) { struct getter<const char*> {
return lua_toboolean(L, index) != 0; const char* get(lua_State* L, int index = -1) {
return lua_tostring(L, index);
} }
};
template<typename T> template <>
inline auto get_helper(std::false_type, lua_State* L, int index = -1) -> decltype(get(types<T>(), L, index)) { struct getter<nil_t> {
// T is a class nil_t get(lua_State* L, int index = -1) {
return get(types<T>(), L, index); if (lua_isnil(L, index) == 0)
throw sol::error("not nil");
return nil_t{ };
} }
} // detail };
template<typename T, typename U = Unqualified<T>> template <>
inline auto get(lua_State* L, int index = -1) -> decltype(detail::get_helper<U>(std::is_arithmetic<U>{}, L, index)) { struct getter<userdata_t> {
return detail::get_helper<U>(std::is_arithmetic<U>{}, L, index); userdata_t get(lua_State* L, int index = -1) {
return{ lua_touserdata(L, index) };
} }
};
template<typename T> template <>
inline std::pair<T, int> get_user(lua_State* L, int index = 1) { struct getter<lightuserdata_t> {
const static std::size_t data_t_count = (sizeof(T)+(sizeof(void*)-1)) / sizeof(void*); lightuserdata_t get(lua_State* L, int index = 1) {
typedef std::array<void*, data_t_count> data_t; return{ lua_touserdata(L, lua_upvalueindex(index)) };
data_t voiddata{ {} };
for (std::size_t i = 0, d = 0; d < sizeof(T); ++i, d += sizeof(void*)) {
voiddata[ i ] = stack::get<lightuserdata_t>(L, index++);
} }
return std::pair<T, int>(*reinterpret_cast<T*>(static_cast<void*>(voiddata.data())), index); };
template <>
struct getter<void*> {
void* get(lua_State* L, int index = 1) {
return lua_touserdata(L, index);
} }
};
template<typename T> template<typename T, typename>
auto pop(lua_State* L) -> decltype(get<T>(L)) {
auto&& r = get<T>(L);
lua_pop(L, 1);
return r;
}
template<typename T>
struct pusher;
template<typename T>
struct pusher { struct pusher {
template<typename U = T, EnableIf<std::is_floating_point<U>> = 0> template<typename U = T, EnableIf<std::is_floating_point<U>> = 0>
static void push(lua_State* L, const T& value) { static void push(lua_State* L, const T& value) {
@ -165,34 +177,27 @@ struct pusher {
lua_pushunsigned(L, value); lua_pushunsigned(L, value);
} }
template<typename U = T, EnableIf<has_begin_end<U>, Not<has_key_value_pair<U>>> = 0>
static void push(lua_State* L, const T& cont) {
lua_createtable(L, cont.size(), 0);
unsigned index = 1;
for(auto&& i : cont) {
// push the index
pusher<unsigned>::push(L, index++);
// push the value
pusher<Unqualified<decltype(i)>>::push(L, i);
// set the table
lua_settable(L, -3);
}
}
template<typename U = T, EnableIf<has_begin_end<U>, has_key_value_pair<U>> = 0>
static void push(lua_State* L, const T& cont) {
lua_createtable(L, cont.size(), 0);
for(auto&& pair : cont) {
pusher<Unqualified<decltype(pair.first)>>::push(L, pair.first);
pusher<Unqualified<decltype(pair.second)>>::push(L, pair.second);
lua_settable(L, -3);
}
}
template<typename U = T, EnableIf<std::is_base_of<reference, U>> = 0> template<typename U = T, EnableIf<std::is_base_of<reference, U>> = 0>
static void push(lua_State*, T& ref) { static void push(lua_State*, T& ref) {
ref.push(); ref.push();
} }
template<typename U = T, EnableIf<Not<std::is_base_of<reference, U>>, Not<std::is_integral<U>>, Not<std::is_floating_point<U>>> = 0>
static void push(lua_State* L, T& t) {
pusher<T*>{}.push(L, std::addressof(t));
}
template<typename U = T, EnableIf<Not<std::is_base_of<reference, U>>, Not<std::is_integral<U>>, Not<std::is_floating_point<U>>> = 0>
static void push(lua_State* L, T&& t) {
detail::push_userdata(L, std::move(t), userdata_traits<T*>::metatable);
}
};
template<typename T>
struct pusher<T*> {
static void push(lua_State* L, T* obj) {
detail::push_userdata(L, obj, userdata_traits<T*>::metatable);
}
}; };
template<> template<>
@ -223,6 +228,13 @@ struct pusher<void*> {
} }
}; };
template<>
struct pusher<lightuserdata_t> {
static void push(lua_State* L, lightuserdata_t userdata) {
lua_pushlightuserdata(L, userdata);
}
};
template<> template<>
struct pusher<const char*> { struct pusher<const char*> {
static void push(lua_State* L, const char* str) { static void push(lua_State* L, const char* str) {
@ -244,31 +256,33 @@ struct pusher<std::string> {
} }
}; };
template<typename T> inline void push(lua_State*) {
inline void push(lua_State* L, T&& t) {
pusher<Unqualified<T>>::push(L, std::forward<T>(t)); }
template<typename T, typename... Args>
inline void push(lua_State* L, T&& t, Args&&... args) {
using swallow = char[];
pusher<Unqualified<T>>{}.push(L, std::forward<T>(t));
void(swallow{'\0', (pusher<Unqualified<T>>{}.push(L, std::forward<Args>(args)), '\0')... });
}
template<typename T, typename U = Unqualified<T>>
inline auto get(lua_State* L, int index = -1) -> decltype(getter<U>{}.get(L, index)) {
return getter<U>{}.get(L, index);
} }
template<typename T> template<typename T>
inline void push_user(lua_State* L, T& userdata, const char* metatablekey) { auto pop(lua_State* L) -> decltype(get<T>(L)) {
T* pdatum = static_cast<T*>(lua_newuserdata(L, sizeof(T))); typedef decltype(get<T>(L)) ret_t;
T& datum = *pdatum; ret_t r = get<T>(L);
datum = userdata; lua_pop(L, 1);
if (metatablekey != nullptr) { return r;
lua_getfield(L, LUA_REGISTRYINDEX, metatablekey);
lua_setmetatable(L, -2);
}
}
template<typename T, size_t N>
inline void push_as_upvalues(lua_State* L, const std::array<T, N>& data) {
for(auto&& i : data) {
push(L, i);
}
} }
namespace detail {
template<typename T> template<typename T>
inline int push_user(lua_State* L, T& item) { inline int push_as_upvalues(lua_State* L, T& item) {
typedef typename std::decay<T>::type TValue; typedef typename std::decay<T>::type TValue;
const static std::size_t itemsize = sizeof(TValue); const static std::size_t itemsize = sizeof(TValue);
const static std::size_t voidsize = sizeof(void*); const static std::size_t voidsize = sizeof(void*);
@ -278,11 +292,23 @@ inline int push_user(lua_State* L, T& item) {
data_t data{{}}; data_t data{{}};
std::memcpy(std::addressof(data[0]), std::addressof(item), itemsize); std::memcpy(std::addressof(data[0]), std::addressof(item), itemsize);
push_as_upvalues(L, data); for (auto&& v : data) {
push(L, v);
}
return data_t_count; return data_t_count;
} }
namespace detail { template<typename T>
inline std::pair<T, int> get_as_upvalues(lua_State* L, int index = 1) {
const static std::size_t data_t_count = (sizeof(T)+(sizeof(void*)-1)) / sizeof(void*);
typedef std::array<void*, data_t_count> data_t;
data_t voiddata{ {} };
for (std::size_t i = 0, d = 0; d < sizeof(T); ++i, d += sizeof(void*)) {
voiddata[ i ] = get<lightuserdata_t>(L, index++);
}
return std::pair<T, int>(*reinterpret_cast<T*>(static_cast<void*>(voiddata.data())), index);
}
template<typename T, std::size_t... I> template<typename T, std::size_t... I>
inline void push_tuple(lua_State* L, indices<I...>, T&& tuplen) { inline void push_tuple(lua_State* L, indices<I...>, T&& tuplen) {
using swallow = char[1 + sizeof...(I)]; using swallow = char[1 + sizeof...(I)];
@ -349,8 +375,8 @@ inline auto get_call(lua_State* L, int index, TFx&& fx, types<Args...> t) -> dec
} }
template<typename... Args, typename TFx> template<typename... Args, typename TFx>
inline auto get_call(lua_State* L, TFx&& fx, types<Args...> t) -> decltype(detail::ltr_get(L, 1, std::forward<TFx>(fx), t, t)) { inline auto get_call(lua_State* L, TFx&& fx, types<Args...> t) -> decltype(get_call(L, 1, std::forward<TFx>(fx), t)) {
return detail::ltr_get(L, 1, std::forward<TFx>(fx), t, t); return get_call(L, 1, std::forward<TFx>(fx), t);
} }
template<typename... Args, typename TFx> template<typename... Args, typename TFx>
@ -363,17 +389,6 @@ inline auto pop_reverse_call(lua_State* L, TFx&& fx, types<Args...> t) -> declty
return detail::rtl_pop(L, std::forward<TFx>(fx), t, reversed<Args...>()); return detail::rtl_pop(L, std::forward<TFx>(fx), t, reversed<Args...>());
} }
inline void push_args(lua_State*) {
}
template<typename Arg, typename... Args>
inline void push_args(lua_State* L, Arg&& arg, Args&&... args) {
using swallow = char[];
stack::push(L, std::forward<Arg>(arg));
void(swallow{'\0', (stack::push(L, std::forward<Args>(args)), '\0')... });
}
inline call_syntax get_call_syntax(lua_State* L, const std::string& meta) { inline call_syntax get_call_syntax(lua_State* L, const std::string& meta) {
if (get<type>(L, 1) == type::table) { if (get<type>(L, 1) == type::table) {
if (luaL_newmetatable(L, meta.c_str()) == 0) { if (luaL_newmetatable(L, meta.c_str()) == 0) {

View File

@ -28,18 +28,6 @@
#include "userdata.hpp" #include "userdata.hpp"
namespace sol { namespace sol {
namespace detail {
template<typename T>
T* get_ptr(T& val) {
return std::addressof(val);
}
template<typename T>
T* get_ptr(T* val) {
return val;
}
} // detail
class table : public reference { class table : public reference {
friend class state; friend class state;
template<typename T, typename U> template<typename T, typename U>
@ -109,17 +97,8 @@ public:
template<typename T> template<typename T>
table& set_userdata(userdata<T>& user) { table& set_userdata(userdata<T>& user) {
auto&& meta = userdata_traits<T>::metatable; stack::push(state(), user);
luaL_newmetatable(state(), meta.c_str()); lua_setglobal(state(), user.name().c_str());
for (std::size_t upvalues = 0; upvalues < user.functions.size(); ++upvalues) {
stack::push(state(), static_cast<void*>(user.functions[ upvalues ].get()));
}
luaL_setfuncs(state(), user.functiontable.data(), static_cast<uint32_t>(user.functions.size()));
lua_pushvalue(state(), -1);
lua_setfield(state(), -1, "__index");
lua_setglobal(state(), user.luaname.c_str());
return *this; return *this;
} }
@ -203,7 +182,7 @@ private:
push(); push();
int upvalues = stack::push_user(state(), fxptr); int upvalues = stack::detail::push_as_upvalues(state(), fxptr);
stack::push(state(), userobjdata); stack::push(state(), userobjdata);
luaL_setfuncs(state(), funcreg, upvalues + 1); luaL_setfuncs(state(), funcreg, upvalues + 1);
@ -223,7 +202,7 @@ private:
}; };
push(); push();
int upvalues = stack::push_user(state(), target); int upvalues = stack::detail::push_as_upvalues(state(), target);
luaL_setfuncs(state(), funcreg, upvalues); luaL_setfuncs(state(), funcreg, upvalues);
pop(); pop();
@ -253,7 +232,7 @@ private:
} }
push(); push();
stack::push_user(state(), userdata, metatablename); stack::detail::push_userdata(state(), userdata, metatablename);
luaL_setfuncs(state(), funcreg, 1); luaL_setfuncs(state(), funcreg, 1);
pop(); pop();
return *this; return *this;

View File

@ -47,6 +47,12 @@ struct And : Bool<true> {};
template<typename T, typename... Args> template<typename T, typename... Args>
struct And<T, Args...> : If<T, And<Args...>, Bool<false>> {}; struct And<T, Args...> : If<T, And<Args...>, Bool<false>> {};
template<typename... Args>
struct Or : Bool<true> {};
template<typename T, typename... Args>
struct Or<T, Args...> : If<Not<T>, Or<Args...>, Bool<true>> {};
template<typename... Args> template<typename... Args>
using EnableIf = typename std::enable_if<And<Args...>::value, int>::type; using EnableIf = typename std::enable_if<And<Args...>::value, int>::type;

View File

@ -31,6 +31,7 @@ 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 {};
struct lightuserdata_t { struct lightuserdata_t {
void* value; void* value;
lightuserdata_t(void* data) : value(data) {} lightuserdata_t(void* data) : value(data) {}

View File

@ -24,7 +24,7 @@
#include "state.hpp" #include "state.hpp"
#include "function_types.hpp" #include "function_types.hpp"
#include "demangle.hpp" #include "userdata_traits.hpp"
#include <vector> #include <vector>
namespace sol { namespace sol {
@ -35,26 +35,15 @@ inline std::unique_ptr<T> make_unique(Args&&... args) {
} }
} // detail } // detail
template<typename T>
struct userdata_traits {
static const std::string name;
static const std::string metatable;
};
template<typename T>
const std::string userdata_traits<T>::name = detail::demangle(typeid(T));
template<typename T>
const std::string userdata_traits<T>::metatable = std::string("sol.stateful.").append(name);
template<typename T> template<typename T>
class userdata { class userdata {
private: private:
friend table;
std::string luaname; std::string luaname;
std::vector<std::string> functionnames; std::vector<std::string> functionnames;
std::vector<std::unique_ptr<base_function>> functions; std::vector<std::unique_ptr<base_function>> funcs;
std::vector<std::unique_ptr<base_function>> ptrfuncs;
std::vector<luaL_Reg> functiontable; std::vector<luaL_Reg> functiontable;
std::vector<luaL_Reg> ptrfunctiontable;
template<typename... TTypes> template<typename... TTypes>
struct constructor { struct constructor {
@ -89,7 +78,7 @@ private:
T* obj = static_cast<T*>(udata); T* obj = static_cast<T*>(udata);
match_constructor(L, obj, syntax, argcount - static_cast<int>(syntax), typename std::common_type<TTypes>::type()...); match_constructor(L, obj, syntax, argcount - static_cast<int>(syntax), typename std::common_type<TTypes>::type()...);
luaL_getmetatable(L, meta.c_str()); luaL_getmetatable(L, std::addressof(meta[0]));
lua_setmetatable(L, -2); lua_setmetatable(L, -2);
return 1; return 1;
@ -115,8 +104,10 @@ private:
static_assert(std::is_base_of<TBase, T>::value, "Any registered function must be part of the class"); static_assert(std::is_base_of<TBase, T>::value, "Any registered function must be part of the class");
typedef typename std::decay<decltype(func)>::type function_type; typedef typename std::decay<decltype(func)>::type function_type;
functionnames.push_back(std::move(name)); functionnames.push_back(std::move(name));
functions.emplace_back(detail::make_unique<userdata_function<function_type, T>>(std::move(func))); funcs.emplace_back(detail::make_unique<userdata_function<function_type, T>>(std::move(func)));
ptrfuncs.emplace_back(detail::make_unique<userdata_function<function_type, typename std::add_pointer<T>::type>>(std::move(func)));
functiontable.push_back({ functionnames.back().c_str(), &base_function::userdata<N>::call }); functiontable.push_back({ functionnames.back().c_str(), &base_function::userdata<N>::call });
ptrfunctiontable.push_back({ functionnames.back().c_str(), &base_function::userdata<N>::call });
build_function_tables<N + 1>(std::forward<Args>(args)...); build_function_tables<N + 1>(std::forward<Args>(args)...);
} }
@ -130,22 +121,81 @@ public:
template<typename... Args, typename... CArgs> template<typename... Args, typename... CArgs>
userdata(std::string name, constructors<CArgs...>, Args&&... args): luaname(std::move(name)) { userdata(std::string name, constructors<CArgs...>, Args&&... args): luaname(std::move(name)) {
functionnames.reserve(sizeof...(args) + 2); functionnames.reserve(sizeof...(args) + 2);
functiontable.reserve(sizeof...(args) + 3); functiontable.reserve(sizeof...(args) + 2);
functions.reserve(sizeof...(args) + 2); ptrfunctiontable.reserve(sizeof...(args) + 2);
funcs.reserve(sizeof...(args) + 2);
ptrfuncs.reserve(sizeof...(args) + 2);
build_function_tables<0>(std::forward<Args>(args)...); build_function_tables<0>(std::forward<Args>(args)...);
functionnames.push_back("new"); functionnames.push_back("new");
functiontable.push_back({ functionnames.back().c_str(), &constructor<CArgs...>::construct }); functiontable.push_back({ functionnames.back().c_str(), &constructor<CArgs...>::construct });
functionnames.push_back("__gc"); functionnames.push_back("__gc");
functiontable.push_back({ functionnames.back().c_str(), &destructor<sizeof...(Args) / 2>::destruct }); functiontable.push_back({ functionnames.back().c_str(), &destructor<sizeof...(Args) / 2>::destruct });
// ptr_functions does not participate in garbage collection/new,
// as all pointered types are considered
// to be references. This makes returns of
// `std::vector<int>&` and `std::vector<int>*` work
functiontable.push_back({ nullptr, nullptr }); functiontable.push_back({ nullptr, nullptr });
ptrfunctiontable.push_back({ nullptr, nullptr });
} }
template<typename... Args, typename... CArgs> template<typename... Args, typename... CArgs>
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)...) {}
const std::vector<std::string>& function_names () const {
return functionnames;
}
const std::vector<std::unique_ptr<base_function>>& functions () const {
return funcs;
}
const std::vector<std::unique_ptr<base_function>>& reference_functions () const {
return ptrfuncs;
}
const std::vector<luaL_Reg>& function_table () const {
return functiontable;
}
const std::vector<luaL_Reg>& reference_function_table () const {
return ptrfunctiontable;
}
const std::string& name () const {
return luaname;
}
}; };
namespace stack {
template <typename T>
struct pusher<userdata<T>> {
static void push ( lua_State* L, userdata<T>& user ) {
auto&& ptrmeta = userdata_traits<typename std::add_pointer<T>::type>::metatable;
luaL_newmetatable(L, ptrmeta.c_str());
for (std::size_t upvalues = 0; upvalues < user.reference_functions().size(); ++upvalues) {
stack::push(L, static_cast<void*>(user.reference_functions()[ upvalues ].get()));
}
luaL_setfuncs(L, user.reference_function_table().data(), static_cast<uint32_t>(user.reference_functions().size()));
lua_pushvalue(L, -1);
lua_setfield(L, -1, "__index");
auto&& meta = userdata_traits<T>::metatable;
luaL_newmetatable(L, meta.c_str());
for (std::size_t upvalues = 0; upvalues < user.functions().size(); ++upvalues) {
stack::push(L, static_cast<void*>(user.functions()[ upvalues ].get()));
}
luaL_setfuncs(L, user.function_table().data(), static_cast<uint32_t>(user.functions().size()));
lua_pushvalue(L, -1);
lua_setfield(L, -1, "__index");
}
};
} // stack
} // sol } // sol
#endif // SOL_USERDATA_HPP #endif // SOL_USERDATA_HPP

43
sol/userdata_traits.hpp Normal file
View File

@ -0,0 +1,43 @@
// The MIT License (MIT)
// Copyright (c) 2013 Danny Y., Rapptz
// 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_USERDATA_TRAITS_HPP
#define SOL_USERDATA_TRAITS_HPP
#include "demangle.hpp"
namespace sol {
template<typename T>
struct userdata_traits {
static const std::string name;
static const std::string metatable;
};
template<typename T>
const std::string userdata_traits<T>::name = detail::demangle(typeid(T));
template<typename T>
const std::string userdata_traits<T>::metatable = std::string("sol.stateful.").append(detail::demangle(typeid(T)));
}
#endif // SOL_USERDATA_TRAITS_HPP