diff --git a/sol.hpp b/sol.hpp index ce093f66..ad511394 100644 --- a/sol.hpp +++ b/sol.hpp @@ -25,5 +25,6 @@ #include "sol/state.hpp" #include "sol/object.hpp" #include "sol/function.hpp" +#include "sol/container.hpp" #endif // SOL_HPP diff --git a/sol/container.hpp b/sol/container.hpp new file mode 100644 index 00000000..a11703ea --- /dev/null +++ b/sol/container.hpp @@ -0,0 +1,89 @@ +#ifndef SOL_CONTAINER_HPP +#define SOL_CONTAINER_HPP + +#include "userdata.hpp" + +namespace sol { + +template +struct container { + typedef typename std::conditional::value, Tc, Decay>::type T; + typedef Decay().begin()))> value_type; + T cont; + + template + container (Args&&... args) : cont(std::forward(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 +struct pusher::value>::type> { + template + static void push(lua_State *L, U&& t){ + typedef container container_t; + // todo: NEED to find a different way of handling this... + static std::vector> classes{}; + userdata classdata(default_constructor, + "__index", &container_t::get, + "__newindex", &container_t::set, + "__len", &container_t::size); + classes.emplace_back(std::make_shared>(std::move(classdata))); + auto&& ptr = classes.back(); + auto udata = std::static_pointer_cast>(ptr); + stack::push(L, *udata); + + container_t* c = static_cast(lua_newuserdata(L, sizeof(container_t))); + std::allocator alloc{}; + alloc.construct(c, std::forward(t)); + + auto&& meta = userdata_traits::metatable; + luaL_getmetatable(L, std::addressof(meta[0])); + lua_setmetatable(L, -2); + } + + template>> = 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::push(L, index++); + // push the value + pusher>::push(L, i); + // set the table + lua_settable(L, -3); + } + } + + template> = 0> + static void push(lua_State* L, const T& cont) { + lua_createtable(L, cont.size(), 0); + for(auto&& pair : cont) { + pusher>::push(L, pair.first); + pusher>::push(L, pair.second); + lua_settable(L, -3); + } + } +}; +} // stack +} // sol +#endif // SOL_CONTAINER_HPP diff --git a/sol/function.hpp b/sol/function.hpp index a3affe0a..184c8dc5 100644 --- a/sol/function.hpp +++ b/sol/function.hpp @@ -76,45 +76,177 @@ public: template typename return_type::type call(Args&&... args) const { push(); - stack::push_args(state(), std::forward(args)...); + stack::push(state(), std::forward(args)...); return invoke(types(), sizeof...(Args)); } }; namespace stack { -namespace detail { -template -inline std::function get_std_func(types, types, lua_State* L, int index = -1) { - typedef typename function_traits::return_type return_t; - sol::function f(L, index); - auto fx = [ f, L, index ] (FxArgs&&... args) -> return_t { - return f(types(), std::forward(args)...); - }; - return std::move(fx); -} +template <> +struct pusher { + template + void set_isfunction_fx(lua_State* L, std::true_type, T&& key, TFx&& fx) { + set_fx(std::false_type(), std::forward(key), std::forward(fx)); + } -template -inline std::function get_std_func(types, types, lua_State* L, int index = -1) { - sol::function f(L, index); - auto fx = [ f, L, index ] (FxArgs&&... args) -> void { - f(std::forward(args)...); - }; - return std::move(fx); -} + template + void set_isfunction_fx(lua_State* L, std::false_type, T&& key, TFx&& fx) { + typedef Decay clean_lambda; + typedef typename function_traits::free_function_pointer_type raw_func_t; + typedef std::is_convertible isconvertible; + set_isconvertible_fx(isconvertible(), std::forward(key), std::forward(fx)); + } -template -inline std::function get_std_func(types t, types<>, lua_State* L, int index = -1) { - return get_std_func(std::move(t), types(), L, index); -} + template + void set_isconvertible_fx(lua_State* L, std::true_type, T&& key, TFx&& fx) { + typedef Decay clean_lambda; + typedef typename function_traits::free_function_pointer_type raw_func_t; + set_isfunction_fx(std::true_type(), std::forward(key), raw_func_t(std::forward(fx))); + } + + template + void set_isconvertible_fx(lua_State* L, std::false_type, T&& key, TFx&& fx) { + typedef typename std::remove_pointer>::type clean_fx; + std::unique_ptr sptr(new functor_function(std::forward(fx))); + set_fx(std::forward(key), std::move(sptr)); + } + + template + void set_lvalue_fx(lua_State* L, std::true_type, T&& key, TFx&& fx, TObj&& obj) { + set_fx(std::true_type(), std::forward(key), std::forward(fx), std::forward(obj)); + } + + template + void set_lvalue_fx(lua_State* L, std::false_type, T&& key, TFx&& fx, TObj&& obj) { + typedef typename std::remove_pointer>::type clean_fx; + std::unique_ptr sptr(new member_function(std::forward(obj), std::forward(fx))); + return set_fx(std::forward(key), std::move(sptr)); + } + + template + 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 fxptr(std::forward(fx)); + void* userobjdata = static_cast(detail::get_ptr(obj)); + lua_CFunction freefunc = &static_member_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, fxptr); + stack::push(L, userobjdata); + luaL_setfuncs(L, funcreg, upvalues + 1); + + } + + template + void set_fx(lua_State* L, std::false_type, T&& key, TFx&& fx) { + std::string fkey(key); + Decay target(std::forward(fx)); + lua_CFunction freefunc = &static_function::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 + void set_fx(lua_State* L, T&& key, std::unique_ptr luafunc) { + std::string fkey(key); + std::string metakey("sol.stateful."); + metakey += fkey; + metakey += ".meta"; + base_function* target = luafunc.release(); + void* userdata = reinterpret_cast(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 + void set_function(T&& key, TFx&& fx) { + typedef typename std::remove_pointer>::type clean_fx; + set_isfunction_fx(std::is_function(), std::forward(key), std::forward(fx)); + } + + template + void set_function(T&& key, TFx&& fx, TObj&& obj) { + set_lvalue_fx(Bool::value || std::is_pointer::value>(), + std::forward(key), std::forward(fx), std::forward(obj)); + } + + template + void push(lua_State* L, Key&& key, Args&&... args) { + set_function(L, std::forward(key), std::forward(args)...); + } + +}; +template +struct pusher> { + +}; template -inline std::function get(types>, lua_State* L, int index) { +struct getter> { typedef function_traits fx_t; typedef typename fx_t::args_type args_t; typedef typename tuple_types::type ret_t; - return get_std_func(args_t(), ret_t(), L, index); -} -} // detail + + template + static std::function get_std_func(types, types, lua_State* L, int index = -1) { + typedef typename function_traits::return_type return_t; + sol::function f(L, index); + auto fx = [ f, L, index ] (FxArgs&&... args) -> return_t { + return f(types(), std::forward(args)...); + }; + return std::move(fx); + } + + template + static std::function get_std_func(types, types, lua_State* L, int index = -1) { + sol::function f(L, index); + auto fx = [ f, L, index ] (FxArgs&&... args) -> void { + f(std::forward(args)...); + }; + return std::move(fx); + } + + template + static std::function get_std_func(types t, types<>, lua_State* L, int index = -1) { + return get_std_func(std::move(t), types(), L, index); + } + + static std::function get(lua_State* L, int index) { + return get_std_func(args_t(), ret_t(), L, index); + } +}; } // stack } // sol diff --git a/sol/function_types.hpp b/sol/function_types.hpp index 0a90866c..bb9c326d 100644 --- a/sol/function_types.hpp +++ b/sol/function_types.hpp @@ -26,6 +26,38 @@ #include namespace sol { +namespace detail { +template +struct functor { + T* item; + Func invocation; + + template + functor(FxArgs&&... fxargs): item(nullptr), invocation(std::forward(fxargs)...) {} + + template + R operator()(Args&&... args) { + T& member = *item; + return (member.*invocation)(std::forward(args)...); + } +}; + +template +struct functor { + T* item; + Func invocation; + + template + functor(FxArgs&&... fxargs): item(nullptr), invocation(std::forward(fxargs)...) {} + + template + void operator()(Args&&... args) { + T& member = *item; + (member.*invocation)(std::forward(args)...); + } +}; +} // detail + template struct static_function { @@ -56,7 +88,7 @@ struct static_function { } static int call(lua_State* L) { - auto udata = stack::get_user(L); + auto udata = stack::detail::get_as_upvalues(L); function_type* fx = udata.first; int r = typed_call(tuple_types(), 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(L, 1); - auto objdata = stack::get_user(L, memberdata.second); + auto memberdata = stack::detail::get_as_upvalues(L, 1); + auto objdata = stack::detail::get_as_upvalues(L, memberdata.second); function_type& memfx = memberdata.first; T& obj = *objdata.first; int r = typed_call(tuple_types(), typename traits_type::args_type(), obj, memfx, L); @@ -242,41 +274,36 @@ struct member_function : public base_function { } }; -template +template struct userdata_function : public base_function { + typedef typename std::remove_pointer::type T; typedef typename std::remove_pointer::type>::type function_type; typedef function_traits traits_type; - struct functor { - T* item; - function_type invocation; + typedef typename traits_type::return_type return_type; - template - functor(FxArgs&&... fxargs): item(nullptr), invocation(std::forward(fxargs)...) {} - - void prepare(lua_State* L) { - void* udata = stack::get(L, 1); - if (udata == nullptr) - throw error("must use the syntax [object]:[function] to call member functions bound to C++"); - item = static_cast(udata); - } - - template - typename traits_type::return_type operator()(Args&&... args) { - T& member = *item; - return (member.*invocation)(std::forward(args)...); - } - } fx; + detail::functor fx; template userdata_function(FxArgs&&... fxargs): fx(std::forward(fxargs)...) {} template> - typename std::enable_if::value, void>::type special_push(lua_State*, Return&&) { - // push nothing + typename std::enable_if::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(r)); } template> - typename std::enable_if::value, void>::type special_push(lua_State* L, Return&& r) { + typename std::enable_if::value, void>::type push(lua_State* L, Return&& r) { stack::push(L, std::forward(r)); } @@ -290,12 +317,10 @@ struct userdata_function : public base_function { template int operator()(types, types t, lua_State* L) { - typedef typename return_type::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(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(L, 1)); return (*this)(tuple_types(), typename traits_type::args_type(), L); } }; diff --git a/sol/stack.hpp b/sol/stack.hpp index e6dbfdde..afbfae3e 100644 --- a/sol/stack.hpp +++ b/sol/stack.hpp @@ -26,129 +26,141 @@ #include "reference.hpp" #include "tuple.hpp" #include "traits.hpp" +#include "userdata_traits.hpp" #include #include #include #include namespace sol { -namespace stack { namespace detail { -inline nil_t get(types, 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, lua_State* L, int index = -1) { - return{ lua_touserdata(L, lua_upvalueindex(index)) }; -} - -inline userdata_t get(types, lua_State* L, int index = -1) { - return{ lua_touserdata(L, index) }; -} - -inline std::string get(types, 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, lua_State* L, int index = -1) { - return lua_tostring(L, index); -} - -inline type get(types, lua_State* L, int index = -1) { - return static_cast(lua_type(L, index)); -} - -template ::type> -inline U get_sol_type(std::true_type, types, lua_State* L, int index = -1) { - return U(L, index); -} - -template -inline T& get_sol_type(std::false_type, types, lua_State* L, int index = -1) { - userdata_t udata = get(types{}, L, index); - T* obj = static_cast(udata.value); - return *obj; -} - -template > -inline auto get(types t, lua_State* L, int index = -1) -> decltype(get_sol_type(std::is_base_of(), t, L, index)) { - return get_sol_type(std::is_base_of(), t, L, index); -} - -template -inline std::function get(types>, lua_State* L, int index = -1); - template -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 -inline T get_unsigned(std::false_type, lua_State* L, int index = -1) { - return static_cast(lua_tointeger(L, index)); -} - -template -inline T get_arithmetic(std::false_type, lua_State* L, int index = -1) { - // T is a floating point - return static_cast(lua_tonumber(L, index)); -} - -template -inline T get_arithmetic(std::true_type, lua_State* L, int index = -1) { - // T is an integral - return get_unsigned(std::is_unsigned{}, L, index); -} - -template -inline T get_helper(std::true_type, lua_State* L, int index = -1) { - // T is a fundamental type - return get_arithmetic(std::is_integral{}, L, index); -} - -template<> -inline bool get_helper(std::true_type, lua_State* L, int index) { - return lua_toboolean(L, index) != 0; -} - -template -inline auto get_helper(std::false_type, lua_State* L, int index = -1) -> decltype(get(types(), L, index)) { - // T is a class - return get(types(), L, index); +T* get_ptr(T* val) { + return val; } } // detail -template> -inline auto get(lua_State* L, int index = -1) -> decltype(detail::get_helper(std::is_arithmetic{}, L, index)) { - return detail::get_helper(std::is_arithmetic{}, L, index); +namespace stack { +namespace detail { +template +inline void push_userdata(lua_State* L, T&& userdata, Key&& metatablekey) { + T* pdatum = static_cast(lua_newuserdata(L, sizeof(T))); + T& datum = *pdatum; + datum = std::forward(userdata); + luaL_getmetatable(L, std::addressof(metatablekey[0])); + lua_setmetatable(L, -2); } - -template -inline std::pair 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 data_t; - data_t voiddata{ {} }; - for (std::size_t i = 0, d = 0; d < sizeof(T); ++i, d += sizeof(void*)) { - voiddata[ i ] = stack::get(L, index++); - } - return std::pair(*reinterpret_cast(static_cast(voiddata.data())), index); -} - -template -auto pop(lua_State* L) -> decltype(get(L)) { - auto&& r = get(L); - lua_pop(L, 1); - return r; -} - -template +} // detail +template +struct getter; +template struct pusher; -template +template +struct getter { + template> = 0> + static U get(lua_State* L, int index = -1) { + return lua_tonumber(L, index); + } + + template, std::is_signed> = 0> + static U get(lua_State* L, int index = -1) { + return lua_tounsigned(L, index); + } + + template, std::is_unsigned> = 0> + static U get(lua_State* L, int index = -1) { + return static_cast(lua_tointeger(L, index)); + } + + template> = 0> + static U get(lua_State* L, int index = -1) { + return T(L, index); + } + + template>, Not>, Not>> = 0> + static U& get(lua_State* L, int index = -1) { + void* udata = lua_touserdata(L, index); + T* obj = static_cast(udata); + return *obj; + } +}; + +template +struct getter { + static T* get(lua_State* L, int index = -1) { + void* udata = lua_touserdata(L, index); + T** obj = static_cast(udata); + return *obj; + } +}; + +template <> +struct getter { + type get(lua_State *L, int index){ + return static_cast(lua_type(L, index)); + } +}; + +template <> +struct getter { + static bool get(lua_State* L, int index) { + return lua_toboolean(L, index) != 0; + } +}; + +template <> +struct getter { + 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* get(lua_State* L, int index = -1) { + return lua_tostring(L, index); + } +}; + +template <> +struct getter { + 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 get(lua_State* L, int index = -1) { + return{ lua_touserdata(L, index) }; + } +}; + +template <> +struct getter { + lightuserdata_t get(lua_State* L, int index = 1) { + return{ lua_touserdata(L, lua_upvalueindex(index)) }; + } +}; + +template <> +struct getter { + void* get(lua_State* L, int index = 1) { + return lua_touserdata(L, index); + } +}; + +template struct pusher { template> = 0> static void push(lua_State* L, const T& value) { @@ -165,34 +177,27 @@ struct pusher { lua_pushunsigned(L, value); } - template, Not>> = 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::push(L, index++); - // push the value - pusher>::push(L, i); - // set the table - lua_settable(L, -3); - } - } - - template, has_key_value_pair> = 0> - static void push(lua_State* L, const T& cont) { - lua_createtable(L, cont.size(), 0); - for(auto&& pair : cont) { - pusher>::push(L, pair.first); - pusher>::push(L, pair.second); - lua_settable(L, -3); - } - } - template> = 0> static void push(lua_State*, T& ref) { ref.push(); } + + template>, Not>, Not>> = 0> + static void push(lua_State* L, T& t) { + pusher{}.push(L, std::addressof(t)); + } + + template>, Not>, Not>> = 0> + static void push(lua_State* L, T&& t) { + detail::push_userdata(L, std::move(t), userdata_traits::metatable); + } +}; + +template +struct pusher { + static void push(lua_State* L, T* obj) { + detail::push_userdata(L, obj, userdata_traits::metatable); + } }; template<> @@ -223,6 +228,13 @@ struct pusher { } }; +template<> +struct pusher { + static void push(lua_State* L, lightuserdata_t userdata) { + lua_pushlightuserdata(L, userdata); + } +}; + template<> struct pusher { static void push(lua_State* L, const char* str) { @@ -244,31 +256,33 @@ struct pusher { } }; -template -inline void push(lua_State* L, T&& t) { - pusher>::push(L, std::forward(t)); +inline void push(lua_State*) { + +} + +template +inline void push(lua_State* L, T&& t, Args&&... args) { + using swallow = char[]; + pusher>{}.push(L, std::forward(t)); + void(swallow{'\0', (pusher>{}.push(L, std::forward(args)), '\0')... }); +} + +template> +inline auto get(lua_State* L, int index = -1) -> decltype(getter{}.get(L, index)) { + return getter{}.get(L, index); } template -inline void push_user(lua_State* L, T& userdata, const char* metatablekey) { - T* pdatum = static_cast(lua_newuserdata(L, sizeof(T))); - T& datum = *pdatum; - datum = userdata; - if (metatablekey != nullptr) { - lua_getfield(L, LUA_REGISTRYINDEX, metatablekey); - lua_setmetatable(L, -2); - } -} - -template -inline void push_as_upvalues(lua_State* L, const std::array& data) { - for(auto&& i : data) { - push(L, i); - } +auto pop(lua_State* L) -> decltype(get(L)) { + typedef decltype(get(L)) ret_t; + ret_t r = get(L); + lua_pop(L, 1); + return r; } +namespace detail { template -inline int push_user(lua_State* L, T& item) { +inline int push_as_upvalues(lua_State* L, T& item) { typedef typename std::decay::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 +inline std::pair 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 data_t; + data_t voiddata{ {} }; + for (std::size_t i = 0, d = 0; d < sizeof(T); ++i, d += sizeof(void*)) { + voiddata[ i ] = get(L, index++); + } + return std::pair(*reinterpret_cast(static_cast(voiddata.data())), index); +} + template inline void push_tuple(lua_State* L, indices, T&& tuplen) { using swallow = char[1 + sizeof...(I)]; @@ -349,8 +375,8 @@ inline auto get_call(lua_State* L, int index, TFx&& fx, types t) -> dec } template -inline auto get_call(lua_State* L, TFx&& fx, types t) -> decltype(detail::ltr_get(L, 1, std::forward(fx), t, t)) { - return detail::ltr_get(L, 1, std::forward(fx), t, t); +inline auto get_call(lua_State* L, TFx&& fx, types t) -> decltype(get_call(L, 1, std::forward(fx), t)) { + return get_call(L, 1, std::forward(fx), t); } template @@ -363,17 +389,6 @@ inline auto pop_reverse_call(lua_State* L, TFx&& fx, types t) -> declty return detail::rtl_pop(L, std::forward(fx), t, reversed()); } -inline void push_args(lua_State*) { - -} - -template -inline void push_args(lua_State* L, Arg&& arg, Args&&... args) { - using swallow = char[]; - stack::push(L, std::forward(arg)); - void(swallow{'\0', (stack::push(L, std::forward(args)), '\0')... }); -} - inline call_syntax get_call_syntax(lua_State* L, const std::string& meta) { if (get(L, 1) == type::table) { if (luaL_newmetatable(L, meta.c_str()) == 0) { diff --git a/sol/table.hpp b/sol/table.hpp index cd885bc8..ddd7026a 100644 --- a/sol/table.hpp +++ b/sol/table.hpp @@ -28,18 +28,6 @@ #include "userdata.hpp" namespace sol { -namespace detail { -template -T* get_ptr(T& val) { - return std::addressof(val); -} - -template -T* get_ptr(T* val) { - return val; -} -} // detail - class table : public reference { friend class state; template @@ -108,18 +96,9 @@ public: } template - table& set_userdata(userdata& user) { - auto&& meta = userdata_traits::metatable; - luaL_newmetatable(state(), meta.c_str()); - for (std::size_t upvalues = 0; upvalues < user.functions.size(); ++upvalues) { - stack::push(state(), static_cast(user.functions[ upvalues ].get())); - } - luaL_setfuncs(state(), user.functiontable.data(), static_cast(user.functions.size())); - - lua_pushvalue(state(), -1); - lua_setfield(state(), -1, "__index"); - - lua_setglobal(state(), user.luaname.c_str()); + table& set_userdata(userdata& user) { + 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; @@ -261,4 +240,4 @@ private: }; } // sol -#endif // SOL_TABLE_HPP +#endif // SOL_TABLE_HPP diff --git a/sol/traits.hpp b/sol/traits.hpp index f4bbc29a..6d987af9 100644 --- a/sol/traits.hpp +++ b/sol/traits.hpp @@ -47,6 +47,12 @@ struct And : Bool {}; template struct And : If, Bool> {}; +template +struct Or : Bool {}; + +template +struct Or : If, Or, Bool> {}; + template using EnableIf = typename std::enable_if::value, int>::type; diff --git a/sol/types.hpp b/sol/types.hpp index 348a17fd..51185ac6 100644 --- a/sol/types.hpp +++ b/sol/types.hpp @@ -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) {} diff --git a/sol/userdata.hpp b/sol/userdata.hpp index d6c8af02..d0fb300c 100644 --- a/sol/userdata.hpp +++ b/sol/userdata.hpp @@ -24,7 +24,7 @@ #include "state.hpp" #include "function_types.hpp" -#include "demangle.hpp" +#include "userdata_traits.hpp" #include namespace sol { @@ -35,26 +35,15 @@ inline std::unique_ptr make_unique(Args&&... args) { } } // detail -template -struct userdata_traits { - static const std::string name; - static const std::string metatable; -}; - -template -const std::string userdata_traits::name = detail::demangle(typeid(T)); - -template -const std::string userdata_traits::metatable = std::string("sol.stateful.").append(name); - template class userdata { private: - friend table; std::string luaname; std::vector functionnames; - std::vector> functions; + std::vector> funcs; + std::vector> ptrfuncs; std::vector functiontable; + std::vector ptrfunctiontable; template struct constructor { @@ -89,7 +78,7 @@ private: T* obj = static_cast(udata); match_constructor(L, obj, syntax, argcount - static_cast(syntax), typename std::common_type::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::value, "Any registered function must be part of the class"); typedef typename std::decay::type function_type; functionnames.push_back(std::move(name)); - functions.emplace_back(detail::make_unique>(std::move(func))); + funcs.emplace_back(detail::make_unique>(std::move(func))); + ptrfuncs.emplace_back(detail::make_unique::type>>(std::move(func))); functiontable.push_back({ functionnames.back().c_str(), &base_function::userdata::call }); + ptrfunctiontable.push_back({ functionnames.back().c_str(), &base_function::userdata::call }); build_function_tables(std::forward(args)...); } @@ -130,22 +121,81 @@ public: template userdata(std::string name, constructors, 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)...); functionnames.push_back("new"); functiontable.push_back({ functionnames.back().c_str(), &constructor::construct }); functionnames.push_back("__gc"); functiontable.push_back({ functionnames.back().c_str(), &destructor::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&` and `std::vector*` work functiontable.push_back({ nullptr, nullptr }); + ptrfunctiontable.push_back({ nullptr, nullptr }); } template userdata(const char* name, constructors c, Args&&... args) : userdata(std::string(name), std::move(c), std::forward(args)...) {} + + const std::vector& function_names () const { + return functionnames; + } + + const std::vector>& functions () const { + return funcs; + } + + const std::vector>& reference_functions () const { + return ptrfuncs; + } + + const std::vector& function_table () const { + return functiontable; + } + + const std::vector& reference_function_table () const { + return ptrfunctiontable; + } + + const std::string& name () const { + return luaname; + } }; + +namespace stack { +template +struct pusher> { + static void push ( lua_State* L, userdata& user ) { + auto&& ptrmeta = userdata_traits::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(user.reference_functions()[ upvalues ].get())); + } + luaL_setfuncs(L, user.reference_function_table().data(), static_cast(user.reference_functions().size())); + + lua_pushvalue(L, -1); + lua_setfield(L, -1, "__index"); + + auto&& meta = userdata_traits::metatable; + luaL_newmetatable(L, meta.c_str()); + for (std::size_t upvalues = 0; upvalues < user.functions().size(); ++upvalues) { + stack::push(L, static_cast(user.functions()[ upvalues ].get())); + } + luaL_setfuncs(L, user.function_table().data(), static_cast(user.functions().size())); + + lua_pushvalue(L, -1); + lua_setfield(L, -1, "__index"); + } +}; +} // stack + } // sol #endif // SOL_USERDATA_HPP diff --git a/sol/userdata_traits.hpp b/sol/userdata_traits.hpp new file mode 100644 index 00000000..77dabcd5 --- /dev/null +++ b/sol/userdata_traits.hpp @@ -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 +struct userdata_traits { + static const std::string name; + static const std::string metatable; +}; + +template +const std::string userdata_traits::name = detail::demangle(typeid(T)); + +template +const std::string userdata_traits::metatable = std::string("sol.stateful.").append(detail::demangle(typeid(T))); + +} + +#endif // SOL_USERDATA_TRAITS_HPP