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/object.hpp"
#include "sol/function.hpp"
#include "sol/container.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,45 +76,177 @@ public:
template<typename... Ret, typename... Args>
typename return_type<Ret...>::type call(Args&&... args) const {
push();
stack::push_args(state(), std::forward<Args>(args)...);
stack::push(state(), std::forward<Args>(args)...);
return invoke(types<Ret...>(), sizeof...(Args));
}
};
namespace stack {
namespace detail {
template <typename Signature, typename... FxArgs, typename... Ret>
inline 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;
sol::function f(L, index);
auto fx = [ f, L, index ] (FxArgs&&... args) -> return_t {
return f(types<Ret...>(), std::forward<FxArgs>(args)...);
};
return std::move(fx);
}
template <>
struct pusher<function_t> {
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 Signature, typename... FxArgs>
inline std::function<Signature> get_std_func(types<FxArgs...>, types<void>, lua_State* L, int index = -1) {
sol::function f(L, index);
auto fx = [ f, L, index ] (FxArgs&&... args) -> void {
f(std::forward<FxArgs>(args)...);
};
return std::move(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 Signature, typename... FxArgs>
inline 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);
}
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>
inline std::function<Signature> get(types<std::function<Signature>>, lua_State* L, int index) {
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;
return get_std_func<Signature>(args_t(), ret_t(), L, index);
}
} // detail
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;
sol::function f(L, index);
auto fx = [ f, L, index ] (FxArgs&&... args) -> return_t {
return f(types<Ret...>(), std::forward<FxArgs>(args)...);
};
return std::move(fx);
}
template <typename... FxArgs>
static std::function<Signature> get_std_func(types<FxArgs...>, types<void>, lua_State* L, int index = -1) {
sol::function f(L, index);
auto fx = [ f, L, index ] (FxArgs&&... args) -> void {
f(std::forward<FxArgs>(args)...);
};
return std::move(fx);
}
template <typename... FxArgs>
static std::function<Signature> get_std_func(types<FxArgs...> t, types<>, lua_State* L, int index = -1) {
return get_std_func(std::move(t), types<void>(), L, index);
}
static std::function<Signature> get(lua_State* L, int index) {
return get_std_func(args_t(), ret_t(), L, index);
}
};
} // stack
} // sol

View File

@ -26,6 +26,38 @@
#include <memory>
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>
struct static_function {
@ -56,7 +88,7 @@ struct static_function {
}
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;
int r = typed_call(tuple_types<typename traits_type::return_type>(), typename traits_type::args_type(), fx, L);
return r;
@ -98,8 +130,8 @@ struct static_member_function {
}
static int call(lua_State* L) {
auto memberdata = stack::get_user<function_type>(L, 1);
auto objdata = stack::get_user<T*>(L, memberdata.second);
auto memberdata = stack::detail::get_as_upvalues<function_type>(L, 1);
auto objdata = stack::detail::get_as_upvalues<T*>(L, memberdata.second);
function_type& memfx = memberdata.first;
T& obj = *objdata.first;
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 {
typedef typename std::remove_pointer<Tp>::type T;
typedef typename std::remove_pointer<typename std::decay<Function>::type>::type function_type;
typedef function_traits<function_type> traits_type;
struct functor {
T* item;
function_type invocation;
typedef typename traits_type::return_type return_type;
template<typename... FxArgs>
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;
detail::functor<T, function_type, return_type> fx;
template<typename... FxArgs>
userdata_function(FxArgs&&... fxargs): fx(std::forward<FxArgs>(fxargs)...) {}
template<typename Return, typename Raw = Unqualified<Return>>
typename std::enable_if<std::is_same<T, Raw>::value, void>::type special_push(lua_State*, Return&&) {
// push nothing
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
// 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>>
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));
}
@ -290,12 +317,10 @@ struct userdata_function : public base_function {
template<typename... Ret, typename... Args>
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);
std::ptrdiff_t nargs = sizeof...(Args);
lua_pop(L, nargs);
// stack::push(L, std::move(r));
special_push(L, r);
push(L, std::forward<return_type>(r));
return sizeof...(Ret);
}
@ -305,7 +330,7 @@ struct userdata_function : public base_function {
}
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);
}
};

View File

@ -26,129 +26,141 @@
#include "reference.hpp"
#include "tuple.hpp"
#include "traits.hpp"
#include "userdata_traits.hpp"
#include <utility>
#include <array>
#include <cstring>
#include <functional>
namespace sol {
namespace stack {
namespace detail {
inline nil_t get(types<nil_t>, lua_State* L, int index = -1) {
if (lua_isnil(L, index) == 0)
throw sol::error("not nil");
return nil_t{ };
}
inline lightuserdata_t get(types<lightuserdata_t>, lua_State* L, int index = -1) {
return{ lua_touserdata(L, lua_upvalueindex(index)) };
}
inline userdata_t get(types<userdata_t>, lua_State* L, int index = -1) {
return{ lua_touserdata(L, index) };
}
inline std::string get(types<std::string>, lua_State* L, int index = -1) {
std::string::size_type len;
auto str = lua_tolstring(L, index, &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);
T* get_ptr(T& val) {
return std::addressof(val);
}
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<>
inline bool get_helper<bool>(std::true_type, lua_State* L, int index) {
return lua_toboolean(L, index) != 0;
}
template<typename T>
inline auto get_helper(std::false_type, lua_State* L, int index = -1) -> decltype(get(types<T>(), L, index)) {
// T is a class
return get(types<T>(), L, index);
T* get_ptr(T* val) {
return val;
}
} // detail
template<typename T, typename U = Unqualified<T>>
inline auto get(lua_State* L, int index = -1) -> decltype(detail::get_helper<U>(std::is_arithmetic<U>{}, L, index)) {
return detail::get_helper<U>(std::is_arithmetic<U>{}, L, index);
namespace stack {
namespace detail {
template<typename T, typename Key>
inline void push_userdata(lua_State* L, T&& userdata, Key&& metatablekey) {
T* pdatum = static_cast<T*>(lua_newuserdata(L, sizeof(T)));
T& datum = *pdatum;
datum = std::forward<T>(userdata);
luaL_getmetatable(L, std::addressof(metatablekey[0]));
lua_setmetatable(L, -2);
}
template<typename T>
inline std::pair<T, int> get_user(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 ] = stack::get<lightuserdata_t>(L, index++);
}
return std::pair<T, int>(*reinterpret_cast<T*>(static_cast<void*>(voiddata.data())), index);
}
template<typename T>
auto pop(lua_State* L) -> decltype(get<T>(L)) {
auto&& r = get<T>(L);
lua_pop(L, 1);
return r;
}
template<typename T>
} // detail
template <typename T, typename X = void>
struct getter;
template <typename T, typename X = void>
struct pusher;
template<typename T>
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);
}
template<typename U = T, EnableIf<std::is_integral<U>, std::is_signed<U>> = 0>
static U get(lua_State* L, int index = -1) {
return lua_tounsigned(L, index);
}
template<typename U = T, EnableIf<std::is_integral<U>, std::is_unsigned<U>> = 0>
static U get(lua_State* L, int index = -1) {
return static_cast<T>(lua_tointeger(L, index));
}
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;
auto str = lua_tolstring(L, index, &len);
return{ str, len };
}
};
template <>
struct getter<const char*> {
const char* get(lua_State* L, int index = -1) {
return lua_tostring(L, index);
}
};
template <>
struct getter<nil_t> {
nil_t get(lua_State* L, int index = -1) {
if (lua_isnil(L, index) == 0)
throw sol::error("not nil");
return nil_t{ };
}
};
template <>
struct getter<userdata_t> {
userdata_t get(lua_State* L, int index = -1) {
return{ lua_touserdata(L, index) };
}
};
template <>
struct getter<lightuserdata_t> {
lightuserdata_t get(lua_State* L, int index = 1) {
return{ lua_touserdata(L, lua_upvalueindex(index)) };
}
};
template <>
struct getter<void*> {
void* get(lua_State* L, int index = 1) {
return lua_touserdata(L, index);
}
};
template<typename T, typename>
struct pusher {
template<typename U = T, EnableIf<std::is_floating_point<U>> = 0>
static void push(lua_State* L, const T& value) {
@ -165,34 +177,27 @@ struct pusher {
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>
static void push(lua_State*, T& ref) {
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<>
@ -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<>
struct pusher<const char*> {
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* L, T&& t) {
pusher<Unqualified<T>>::push(L, std::forward<T>(t));
inline void push(lua_State*) {
}
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>
inline void push_user(lua_State* L, T& userdata, const char* metatablekey) {
T* pdatum = static_cast<T*>(lua_newuserdata(L, sizeof(T)));
T& datum = *pdatum;
datum = userdata;
if (metatablekey != nullptr) {
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);
}
auto pop(lua_State* L) -> decltype(get<T>(L)) {
typedef decltype(get<T>(L)) ret_t;
ret_t r = get<T>(L);
lua_pop(L, 1);
return r;
}
namespace detail {
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;
const static std::size_t itemsize = sizeof(TValue);
const static std::size_t voidsize = sizeof(void*);
@ -278,11 +292,23 @@ inline int push_user(lua_State* L, T& item) {
data_t data{{}};
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;
}
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>
inline void push_tuple(lua_State* L, indices<I...>, T&& tuplen) {
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>
inline auto get_call(lua_State* L, TFx&& fx, types<Args...> t) -> decltype(detail::ltr_get(L, 1, std::forward<TFx>(fx), t, t)) {
return 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 get_call(L, 1, std::forward<TFx>(fx), t);
}
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...>());
}
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) {
if (get<type>(L, 1) == type::table) {
if (luaL_newmetatable(L, meta.c_str()) == 0) {

View File

@ -28,18 +28,6 @@
#include "userdata.hpp"
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 {
friend class state;
template<typename T, typename U>
@ -109,17 +97,8 @@ public:
template<typename T>
table& set_userdata(userdata<T>& user) {
auto&& meta = userdata_traits<T>::metatable;
luaL_newmetatable(state(), meta.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());
stack::push(state(), user);
lua_setglobal(state(), user.name().c_str());
return *this;
}
@ -203,7 +182,7 @@ private:
push();
int upvalues = stack::push_user(state(), fxptr);
int upvalues = stack::detail::push_as_upvalues(state(), fxptr);
stack::push(state(), userobjdata);
luaL_setfuncs(state(), funcreg, upvalues + 1);
@ -223,7 +202,7 @@ private:
};
push();
int upvalues = stack::push_user(state(), target);
int upvalues = stack::detail::push_as_upvalues(state(), target);
luaL_setfuncs(state(), funcreg, upvalues);
pop();
@ -253,7 +232,7 @@ private:
}
push();
stack::push_user(state(), userdata, metatablename);
stack::detail::push_userdata(state(), userdata, metatablename);
luaL_setfuncs(state(), funcreg, 1);
pop();
return *this;

View File

@ -47,6 +47,12 @@ struct And : Bool<true> {};
template<typename T, typename... Args>
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>
using EnableIf = typename std::enable_if<And<Args...>::value, int>::type;

View File

@ -31,6 +31,7 @@ struct nil_t {};
const nil_t nil {};
struct void_type {};
const void_type Void {};
struct function_t {};
struct lightuserdata_t {
void* value;
lightuserdata_t(void* data) : value(data) {}

View File

@ -24,7 +24,7 @@
#include "state.hpp"
#include "function_types.hpp"
#include "demangle.hpp"
#include "userdata_traits.hpp"
#include <vector>
namespace sol {
@ -35,26 +35,15 @@ inline std::unique_ptr<T> make_unique(Args&&... args) {
}
} // 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>
class userdata {
private:
friend table;
std::string luaname;
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> ptrfunctiontable;
template<typename... TTypes>
struct constructor {
@ -89,7 +78,7 @@ private:
T* obj = static_cast<T*>(udata);
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);
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");
typedef typename std::decay<decltype(func)>::type function_type;
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 });
ptrfunctiontable.push_back({ functionnames.back().c_str(), &base_function::userdata<N>::call });
build_function_tables<N + 1>(std::forward<Args>(args)...);
}
@ -130,22 +121,81 @@ public:
template<typename... Args, typename... CArgs>
userdata(std::string name, constructors<CArgs...>, Args&&... args): luaname(std::move(name)) {
functionnames.reserve(sizeof...(args) + 2);
functiontable.reserve(sizeof...(args) + 3);
functions.reserve(sizeof...(args) + 2);
functiontable.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)...);
functionnames.push_back("new");
functiontable.push_back({ functionnames.back().c_str(), &constructor<CArgs...>::construct });
functionnames.push_back("__gc");
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 });
ptrfunctiontable.push_back({ nullptr, nullptr });
}
template<typename... Args, typename... CArgs>
userdata(const char* name, constructors<CArgs...> c, 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
#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