:l Stop finding bugs in my code -- unique_usertype_traits implemented, sol::overloaded work for static functions

This commit is contained in:
ThePhD 2016-04-24 01:39:44 -04:00
parent e9ed1a62e1
commit 38d21827b1
16 changed files with 308 additions and 199 deletions

View File

@ -31,4 +31,5 @@ Browse the various function and classes :doc:`Sol<../index>` utilizes to make yo
usertype usertype
userdata userdata
usertype_memory usertype_memory
unique_usertype_traits
variadic_args variadic_args

View File

@ -137,16 +137,6 @@ special types
A tag type that, when used with :doc:`stack::get\<non_null\<T*>><stack>`, does not perform a ``nil`` check when attempting to retrieve the userdata pointer. A tag type that, when used with :doc:`stack::get\<non_null\<T*>><stack>`, does not perform a ``nil`` check when attempting to retrieve the userdata pointer.
.. code-block:: cpp
:caption: unique_usertype
:name: unique-usertype
template <typename T, typename Real>
struct unique_usertype {};
A tag type for alerting the framewok that a certain type is to be pushed as a special userdata with special deletion semantics. Is automatically applied to ``std::unique_ptr<T, D>`` and ``std::shared_ptr<T>``.
.. code-block:: cpp .. code-block:: cpp
:caption: type list :caption: type list
:name: type-list :name: type-list

View File

@ -0,0 +1,42 @@
unique_usertype_traits<T>
=========================
A trait for hooking special handles / pointers
----------------------------------------------
.. code-block:: cpp
:caption: unique_usertype
:name: unique-usertype
template <typename T>
struct unique_usertype_traits {
typedef T type;
typedef T actual_type;
static const bool value = false;
static bool is_null(const actual_type&) {...}
static type* get (const actual_type&) {...}
};
A traits type for alerting the library that a certain type is to be pushed as a special userdata with special deletion / destruction semantics. It is already defined for ``std::unique_ptr<T, D>`` and ``std::shared_ptr<T>``. You can specialize this to get ``unique_usertype_traits`` semantics with your code, for example with ``boost::shared_ptr<T>`` like so:
.. code-block:: cpp
namespace sol {
template <typename T>
struct unique_usertype_traits<boost::shared_ptr<T>> {
typedef T type;
typedef boost::shared_ptr<T> actual_type;
static const bool value = true;
static bool is_null(const actual_type& value) {
return value == nullptr;
}
static type* get (const actual_type& p) {
return p.get();
}
}
}
This will allow the framework to properly handle ``boost::shared_ptr<T>``, with ref-counting and all. The `type` is the type that lua and sol will interact with, and will allow you to pull out a non-owning reference / pointer to the data when you just ask for a plain `T*` or `T&` or `T` using the getter functions and properties of Sol.

View File

@ -1,6 +1,10 @@
usertype memory usertype memory
=============== ===============
.. note::
Sol does not take ownership of raw pointers, returned from functions or set through the ``set`` functions. Return a value, a ``std::unique_ptr``, a ``std::shared_ptr`` of some kind, or hook up the :doc:`unique usertypes traits<unique_usertype_traits>` to work for some specific handle structure you use (AKA, for ``boost::shared_ptr``).
The userdata generated by Sol has a specific layout, depending on how Sol recognizes userdata passed into it. All of the referred to metatable names are generated from :ref:`usertype_traits\<T><usertype-traits>` The userdata generated by Sol has a specific layout, depending on how Sol recognizes userdata passed into it. All of the referred to metatable names are generated from :ref:`usertype_traits\<T><usertype-traits>`
In general, we always insert a T* in the first `sizeof(T*)` bytes, so the any framework that pulls out those first bytes expecting a pointer will work. The rest of the data has some different alignments and contents based on what it's used for and how it's used. In general, we always insert a T* in the first `sizeof(T*)` bytes, so the any framework that pulls out those first bytes expecting a pointer will work. The rest of the data has some different alignments and contents based on what it's used for and how it's used.

View File

@ -20,4 +20,6 @@ If you're already using lua and you just want to use ``sol`` in some places, you
:doc:`sol::state_view<../api/state>` is exactly like ``sol::state``, but it doesn't manage the lifetime of a ``lua_State*``. Therefore, you get all the goodies that come with a ``sol::state`` without any of the ownership implications. Sol has no initialization components that need to deliberately remain alive for the duration of the program. It's entirely self-containing and uses lua's garbage collectors and various implementation techniques to require no state C++-side. After you do that, all of the power of `Sol` is available to you, and then some! :doc:`sol::state_view<../api/state>` is exactly like ``sol::state``, but it doesn't manage the lifetime of a ``lua_State*``. Therefore, you get all the goodies that come with a ``sol::state`` without any of the ownership implications. Sol has no initialization components that need to deliberately remain alive for the duration of the program. It's entirely self-containing and uses lua's garbage collectors and various implementation techniques to require no state C++-side. After you do that, all of the power of `Sol` is available to you, and then some!
Remember that Sol can be as lightweight as you want it: almost all of Sol's types take the ``lua_State*`` argument and then a second ``int index`` stack index argument, meaning you can use :doc:`tables<../api/table>`, :doc:`lua functions<../api/function>`, :doc:`coroutines<../api/coroutine>`, and other reference-derived objects that expose the proper constructor for your use. You can also set :doc:`usertypes<../api/usertype>` and other things you need without changing your entire architecture! Remember that Sol can be as lightweight as you want it: almost all of Sol's types take the ``lua_State*`` argument and then a second ``int index`` stack index argument, meaning you can use :doc:`tables<../api/table>`, :doc:`lua functions<../api/function>`, :doc:`coroutines<../api/coroutine>`, and other reference-derived objects that expose the proper constructor for your use. You can also set :doc:`usertypes<../api/usertype>` and other things you need without changing your entire architecture.
Note that you can also make non-standard pointer and reference types with custom reference counting and such also play nice with the system. See :doc:`unique_usertype_traits\<T><../api/unique_usertype_traits>` to see how!

View File

@ -149,11 +149,41 @@ inline decltype(auto) cleanup_key() {
template<typename T, typename Func, typename = void> template<typename T, typename Func, typename = void>
struct functor { struct functor {
typedef meta::bind_traits<Func> traits_type;
typedef typename traits_type::args_type args_type;
typedef typename traits_type::return_type return_type;
typedef std::conditional_t<std::is_pointer<Func>::value || std::is_class<Func>::value, Func, std::add_pointer_t<Func>> function_type;
static const std::size_t arity = traits_type::arity;
static const bool is_free = true;
function_type invocation;
template<typename... Args>
functor(Args&&... args): invocation(std::forward<Args>(args)...) {}
template<typename... Args>
void call(types<void>, Args&&... args) {
invocation(std::forward<Args>(args)...);
}
template<typename Ret, typename... Args>
Ret call(types<Ret>, Args&&... args) {
return invocation(std::forward<Args>(args)...);
}
template<typename... Args>
auto operator()(Args&&... args) -> decltype(std::declval<functor>().call(types<return_type>{}, std::forward<Args>(args)...)) {
return this->call(types<return_type>(), std::forward<Args>(args)...);
}
};
template<typename T, typename Func>
struct functor<T, Func, std::enable_if_t<!std::is_member_pointer<Func>::value && std::is_base_of<T, meta::Unqualified<typename meta::bind_traits<Func>::template arg_at<0>>>::value>> {
typedef meta::bind_traits<Func> traits_type; typedef meta::bind_traits<Func> traits_type;
typedef meta::pop_front_type_t<typename traits_type::args_type> args_type; typedef meta::pop_front_type_t<typename traits_type::args_type> args_type;
typedef typename traits_type::return_type return_type; typedef typename traits_type::return_type return_type;
typedef meta::tuple_element_t<0, typename traits_type::args_tuple_type> Arg0;
typedef std::conditional_t<std::is_pointer<Func>::value || std::is_class<Func>::value, Func, std::add_pointer_t<Func>> function_type; typedef std::conditional_t<std::is_pointer<Func>::value || std::is_class<Func>::value, Func, std::add_pointer_t<Func>> function_type;
static const std::size_t arity = traits_type::arity - 1;
static const bool is_free = false;
T* item; T* item;
function_type invocation; function_type invocation;
@ -186,6 +216,8 @@ struct functor<T, member_property<RSig, WSig>, C> {
typedef member_property<RSig, WSig> function_type; typedef member_property<RSig, WSig> function_type;
typedef meta::Not<std::is_void<RSig>> can_read; typedef meta::Not<std::is_void<RSig>> can_read;
typedef meta::Not<std::is_void<WSig>> can_write; typedef meta::Not<std::is_void<WSig>> can_write;
static const bool is_free = false;
T* item; T* item;
function_type invocation; function_type invocation;
@ -217,6 +249,7 @@ struct functor<T, Func, std::enable_if_t<std::is_member_object_pointer<Func>::va
static const std::size_t arity = traits_type::arity; static const std::size_t arity = traits_type::arity;
typedef std::true_type can_read; typedef std::true_type can_read;
typedef std::true_type can_write; typedef std::true_type can_write;
static const bool is_free = false;
T* item; T* item;
Func invocation; Func invocation;
@ -247,6 +280,7 @@ struct functor<T, Func, std::enable_if_t<std::is_member_function_pointer<Func>::
typedef typename traits_type::args_type args_type; typedef typename traits_type::args_type args_type;
typedef typename traits_type::return_type return_type; typedef typename traits_type::return_type return_type;
static const std::size_t arity = traits_type::arity; static const std::size_t arity = traits_type::arity;
static const bool is_free = false;
T* item; T* item;
Func invocation; Func invocation;

View File

@ -30,13 +30,16 @@ namespace sol {
namespace function_detail { namespace function_detail {
namespace internals { namespace internals {
template <typename T> template <typename T>
struct overload_traits : meta::bind_traits<T> {}; struct overload_traits : meta::bind_traits<T> {
static const std::size_t boost = 0;
};
template <typename T, typename Func, typename X> template <typename T, typename Func, typename X>
struct overload_traits<functor<T, Func, X>> { struct overload_traits<functor<T, Func, X>> {
typedef typename functor<T, Func, X>::args_type args_type; typedef typename functor<T, Func, X>::args_type args_type;
typedef typename functor<T, Func, X>::return_type return_type; typedef typename functor<T, Func, X>::return_type return_type;
static const std::size_t arity = functor<T, Func, X>::arity; static const std::size_t arity = functor<T, Func, X>::arity;
static const std::size_t boost = static_cast<std::size_t>(functor<T, Func, X>::is_free);
}; };
template <std::size_t... M, typename Match, typename... Args> template <std::size_t... M, typename Match, typename... Args>
@ -45,7 +48,7 @@ inline int overload_match_arity(types<>, std::index_sequence<>, std::index_seque
} }
template <typename Fx, typename... Fxs, std::size_t I, std::size_t... In, std::size_t... M, typename Match, typename... Args> template <typename Fx, typename... Fxs, std::size_t I, std::size_t... In, std::size_t... M, typename Match, typename... Args>
inline int overload_match_arity(types<Fx, Fxs...>, std::index_sequence<I, In...>, std::index_sequence<M...>, Match&& matchfx, lua_State* L, int fxarity, int start, Args&&... args) { inline int overload_match_arity(types<Fx, Fxs...>, std::index_sequence<I, In...>, std::index_sequence<M...>, Match&& matchfx, lua_State* L, int nfxarity, int start, Args&&... args) {
typedef overload_traits<meta::Unqualified<Fx>> traits; typedef overload_traits<meta::Unqualified<Fx>> traits;
typedef meta::tuple_types<typename traits::return_type> return_types; typedef meta::tuple_types<typename traits::return_type> return_types;
typedef typename traits::args_type args_type; typedef typename traits::args_type args_type;
@ -53,15 +56,16 @@ inline int overload_match_arity(types<Fx, Fxs...>, std::index_sequence<I, In...>
typedef meta::index_in<this_state, args_type> state_index; typedef meta::index_in<this_state, args_type> state_index;
typedef meta::index_in<variadic_args, args_type> va_pack_index; typedef meta::index_in<variadic_args, args_type> va_pack_index;
static const std::size_t arity = traits::arity - static_cast<std::size_t>(state_index::value != SIZE_MAX) - static_cast<std::size_t>(va_pack_index::value != SIZE_MAX); static const std::size_t arity = traits::arity - static_cast<std::size_t>(state_index::value != SIZE_MAX) - static_cast<std::size_t>(va_pack_index::value != SIZE_MAX);
int fxarity = traits::boost + nfxarity;
// compile-time eliminate any functions that we know ahead of time are of improper arity // compile-time eliminate any functions that we know ahead of time are of improper arity
if (meta::find_in_pack_v<index_value<arity>, index_value<M>...>::value) { if (meta::find_in_pack_v<index_value<arity>, index_value<M>...>::value) {
return overload_match_arity(types<Fxs...>(), std::index_sequence<In...>(), std::index_sequence<M...>(), std::forward<Match>(matchfx), L, fxarity, start, std::forward<Args>(args)...); return overload_match_arity(types<Fxs...>(), std::index_sequence<In...>(), std::index_sequence<M...>(), std::forward<Match>(matchfx), L, nfxarity, start, std::forward<Args>(args)...);
} }
if (arity != fxarity) { if (arity != fxarity) {
return overload_match_arity(types<Fxs...>(), std::index_sequence<In...>(), std::index_sequence<arity, M...>(), std::forward<Match>(matchfx), L, fxarity, start, std::forward<Args>(args)...); return overload_match_arity(types<Fxs...>(), std::index_sequence<In...>(), std::index_sequence<arity, M...>(), std::forward<Match>(matchfx), L, nfxarity, start, std::forward<Args>(args)...);
} }
if (!stack::stack_detail::check_types<true>().check(args_type(), args_indices(), L, start, no_panic)) { if (!stack::stack_detail::check_types<true>().check(args_type(), args_indices(), L, start - traits::boost, no_panic)) {
return overload_match_arity(types<Fxs...>(), std::index_sequence<In...>(), std::index_sequence<M...>(), std::forward<Match>(matchfx), L, fxarity, start, std::forward<Args>(args)...); return overload_match_arity(types<Fxs...>(), std::index_sequence<In...>(), std::index_sequence<M...>(), std::forward<Match>(matchfx), L, nfxarity, start, std::forward<Args>(args)...);
} }
return matchfx(types<Fx>(), index_value<I>(), return_types(), args_type(), L, fxarity, start, std::forward<Args>(args)...); return matchfx(types<Fx>(), index_value<I>(), return_types(), args_type(), L, fxarity, start, std::forward<Args>(args)...);
} }
@ -112,13 +116,19 @@ struct usertype_overloaded_function : base_function {
usertype_overloaded_function(std::tuple<Functions...> set) : overloads(std::move(set)) {} usertype_overloaded_function(std::tuple<Functions...> set) : overloads(std::move(set)) {}
template <typename Fx, std::size_t I, typename... R, typename... Args> template <typename Fx, std::size_t I, typename... R, typename... Args, meta::DisableIf<meta::Bool<Fx::is_free>> = 0>
int call(types<Fx>, index_value<I>, types<R...> r, types<Args...> a, lua_State* L, int, int start) { int call(types<Fx>, index_value<I>, types<R...> r, types<Args...> a, lua_State* L, int, int start) {
auto& func = std::get<I>(overloads); auto& func = std::get<I>(overloads);
func.item = detail::ptr(stack::get<T>(L, 1)); func.item = detail::ptr(stack::get<T>(L, 1));
return stack::call_into_lua<0, false>(r, a, L, start, func); return stack::call_into_lua<0, false>(r, a, L, start, func);
} }
template <typename Fx, std::size_t I, typename... R, typename... Args, meta::EnableIf<meta::Bool<Fx::is_free>> = 0>
int call(types<Fx>, index_value<I>, types<R...> r, types<Args...> a, lua_State* L, int, int start) {
auto& func = std::get<I>(overloads);
return stack::call_into_lua<0, false>(r, a, L, start - 1, func);
}
virtual int operator()(lua_State* L) override { virtual int operator()(lua_State* L) override {
auto mfx = [&](auto&&... args){ return this->call(std::forward<decltype(args)>(args)...); }; auto mfx = [&](auto&&... args){ return this->call(std::forward<decltype(args)>(args)...); };
return overload_match<functor<T, std::remove_pointer_t<std::decay_t<Functions>>>...>(mfx, L, 2); return overload_match<functor<T, std::remove_pointer_t<std::decay_t<Functions>>>...>(mfx, L, 2);

View File

@ -28,112 +28,112 @@
#include <cstdint> #include <cstdint>
namespace sol { namespace sol {
struct load_result : public proxy_base<load_result> { struct load_result : public proxy_base<load_result> {
private: private:
lua_State* L; lua_State* L;
int index; int index;
int returncount; int returncount;
int popcount; int popcount;
load_status err; load_status err;
template <typename T> template <typename T>
decltype(auto) tagged_get(types<sol::optional<T>>) const { decltype(auto) tagged_get(types<sol::optional<T>>) const {
if (!valid()) { if (!valid()) {
return sol::optional<T>(nullopt); return sol::optional<T>(nullopt);
} }
return stack::get<sol::optional<T>>(L, index); return stack::get<sol::optional<T>>(L, index);
} }
template <typename T> template <typename T>
decltype(auto) tagged_get(types<T>) const { decltype(auto) tagged_get(types<T>) const {
#ifdef SOL_CHECK_ARGUMENTS #ifdef SOL_CHECK_ARGUMENTS
if (!valid()) { if (!valid()) {
type_panic(L, index, type_of(L, index), type::none); type_panic(L, index, type_of(L, index), type::none);
} }
#endif // Check Argument Safety #endif // Check Argument Safety
return stack::get<T>(L, index); return stack::get<T>(L, index);
} }
sol::optional<sol::error> tagged_get(types<sol::optional<sol::error>>) const { sol::optional<sol::error> tagged_get(types<sol::optional<sol::error>>) const {
if (valid()) { if (valid()) {
return nullopt; return nullopt;
} }
return sol::error(detail::direct_error, stack::get<std::string>(L, index)); return sol::error(detail::direct_error, stack::get<std::string>(L, index));
} }
sol::error tagged_get(types<sol::error>) const { sol::error tagged_get(types<sol::error>) const {
#ifdef SOL_CHECK_ARGUMENTS #ifdef SOL_CHECK_ARGUMENTS
if (valid()) { if (valid()) {
type_panic(L, index, type_of(L, index), type::none); type_panic(L, index, type_of(L, index), type::none);
} }
#endif // Check Argument Safety #endif // Check Argument Safety
return sol::error(detail::direct_error, stack::get<std::string>(L, index)); return sol::error(detail::direct_error, stack::get<std::string>(L, index));
} }
public: public:
load_result() = default; load_result() = default;
load_result(lua_State* L, int index = -1, int returncount = 0, int popcount = 0, load_status err = load_status::ok) : L(L), index(index), returncount(returncount), popcount(popcount), err(err) { load_result(lua_State* L, int index = -1, int returncount = 0, int popcount = 0, load_status err = load_status::ok) : L(L), index(index), returncount(returncount), popcount(popcount), err(err) {
} }
load_result(const load_result&) = default; load_result(const load_result&) = default;
load_result& operator=(const load_result&) = default; load_result& operator=(const load_result&) = default;
load_result(load_result&& o) : L(o.L), index(o.index), returncount(o.returncount), popcount(o.popcount), err(o.err) { load_result(load_result&& o) : L(o.L), index(o.index), returncount(o.returncount), popcount(o.popcount), err(o.err) {
// Must be manual, otherwise destructor will screw us // Must be manual, otherwise destructor will screw us
// return count being 0 is enough to keep things clean // return count being 0 is enough to keep things clean
// but will be thorough // but will be thorough
o.L = nullptr; o.L = nullptr;
o.index = 0; o.index = 0;
o.returncount = 0; o.returncount = 0;
o.popcount = 0; o.popcount = 0;
o.err = load_status::syntax; o.err = load_status::syntax;
} }
load_result& operator=(load_result&& o) { load_result& operator=(load_result&& o) {
L = o.L; L = o.L;
index = o.index; index = o.index;
returncount = o.returncount; returncount = o.returncount;
popcount = o.popcount; popcount = o.popcount;
err = o.err; err = o.err;
// Must be manual, otherwise destructor will screw us // Must be manual, otherwise destructor will screw us
// return count being 0 is enough to keep things clean // return count being 0 is enough to keep things clean
// but will be thorough // but will be thorough
o.L = nullptr; o.L = nullptr;
o.index = 0; o.index = 0;
o.returncount = 0; o.returncount = 0;
o.popcount = 0; o.popcount = 0;
o.err = load_status::syntax; o.err = load_status::syntax;
return *this; return *this;
} }
load_status error() const { load_status error() const {
return err; return err;
} }
bool valid() const { bool valid() const {
return error() == load_status::ok; return error() == load_status::ok;
} }
template<typename T> template<typename T>
T get() const { T get() const {
return tagged_get(types<meta::Unqualified<T>>()); return tagged_get(types<meta::Unqualified<T>>());
} }
template<typename... Ret, typename... Args> template<typename... Ret, typename... Args>
decltype(auto) call(Args&&... args) { decltype(auto) call(Args&&... args) {
return get<function>().template call<Ret...>(std::forward<Args>(args)...); return get<function>().template call<Ret...>(std::forward<Args>(args)...);
} }
template<typename... Args> template<typename... Args>
decltype(auto) operator()(Args&&... args) { decltype(auto) operator()(Args&&... args) {
return call<>(std::forward<Args>(args)...); return call<>(std::forward<Args>(args)...);
} }
lua_State* lua_state() const { return L; }; lua_State* lua_state() const { return L; };
int stack_index() const { return index; }; int stack_index() const { return index; };
~load_result() { ~load_result() {
stack::remove(L, index, popcount); stack::remove(L, index, popcount);
} }
}; };
} // sol } // sol
#endif // SOL_LOAD_RESULT_HPP #endif // SOL_LOAD_RESULT_HPP

View File

@ -28,8 +28,12 @@ namespace sol {
template <typename... Functions> template <typename... Functions>
struct overload_set { struct overload_set {
std::tuple<Functions...> set; std::tuple<Functions...> set;
template <typename... Args> template <typename Arg, typename... Args, meta::DisableIf<std::is_same<overload_set, meta::Unqualified<Arg>>> = 0>
overload_set (Args&&... args) : set(std::forward<Args>(args)...) {} overload_set (Arg&& arg, Args&&... args) : set(std::forward<Arg>(arg), std::forward<Args>(args)...) {}
overload_set(const overload_set&) = default;
overload_set(overload_set&&) = default;
overload_set& operator=(const overload_set&) = default;
overload_set& operator=(overload_set&&) = default;
}; };
template <typename... Args> template <typename... Args>

View File

@ -266,7 +266,7 @@ struct checker<T, type::userdata, C> {
return true; return true;
if (stack_detail::check_metatable<U*>(L)) if (stack_detail::check_metatable<U*>(L))
return true; return true;
if (stack_detail::check_metatable<unique_usertype<U>>(L)) if (stack_detail::check_metatable<detail::unique_usertype<U>>(L))
return true; return true;
#ifndef SOL_NO_EXCEPTIONS #ifndef SOL_NO_EXCEPTIONS
lua_getfield(L, -1, &detail::base_class_check_key()[0]); lua_getfield(L, -1, &detail::base_class_check_key()[0]);
@ -308,27 +308,11 @@ struct checker<T, type::userdata, C> {
} }
}; };
template<typename T, typename Real, typename C> template<typename T>
struct checker<unique_usertype<T, Real>, type::userdata, C> { struct checker<T, type::userdata, std::enable_if_t<is_unique_usertype<T>::value>> {
template <typename Handler> template <typename Handler>
static bool check(lua_State* L, int index, Handler&& handler) { static bool check(lua_State* L, int index, Handler&& handler) {
return checker<T, type::userdata, C>{}.check(L, index, std::forward<Handler>(handler)); return checker<typename unique_usertype_traits<T>::type, type::userdata>{}.check(L, index, std::forward<Handler>(handler));
}
};
template<typename T, typename C>
struct checker<std::shared_ptr<T>, type::userdata, C> {
template <typename Handler>
static bool check(lua_State* L, int index, Handler&& handler) {
return checker<unique_usertype<T, std::shared_ptr<T>>, type::userdata, C>{}.check(L, index, std::forward<Handler>(handler));
}
};
template<typename T, typename D, typename C>
struct checker<std::unique_ptr<T, D>, type::userdata, C> {
template <typename Handler>
static bool check(lua_State* L, int index, Handler&& handler) {
return checker<unique_usertype<T, std::unique_ptr<T, D>>, type::userdata, C>{}.check(L, index, std::forward<Handler>(handler));
} }
}; };

View File

@ -221,16 +221,6 @@ struct getter<T*> {
} }
}; };
template<typename T, typename Real>
struct getter<unique_usertype<T, Real>> {
static Real& get(lua_State* L, int index = -1) {
T** pref = static_cast<T**>(lua_touserdata(L, index));
detail::special_destruct_func* fx = static_cast<detail::special_destruct_func*>(static_cast<void*>(pref + 1));
Real* mem = static_cast<Real*>(static_cast<void*>(fx + 1));
return *mem;
}
};
template<typename T> template<typename T>
struct getter<non_null<T*>> { struct getter<non_null<T*>> {
static T* get(lua_State* L, int index = -1) { static T* get(lua_State* L, int index = -1) {
@ -246,16 +236,15 @@ struct getter<T&> {
}; };
template<typename T> template<typename T>
struct getter<std::shared_ptr<T>> { struct getter<T, std::enable_if_t<is_unique_usertype<T>::value>> {
static std::shared_ptr<T>& get(lua_State* L, int index = -1) { typedef typename unique_usertype_traits<T>::type P;
return getter<unique_usertype<T, std::shared_ptr<T>>>::get(L, index); typedef typename unique_usertype_traits<T>::actual_type Real;
}
}; static Real& get(lua_State* L, int index = -1) {
P** pref = static_cast<P**>(lua_touserdata(L, index));
template<typename T, typename D> detail::special_destruct_func* fx = static_cast<detail::special_destruct_func*>(static_cast<void*>(pref + 1));
struct getter<std::unique_ptr<T, D>> { Real* mem = static_cast<Real*>(static_cast<void*>(fx + 1));
static std::unique_ptr<T, D>& get(lua_State* L, int index = -1) { return *mem;
return getter<unique_usertype<T, std::unique_ptr<T, D>>>::get(L, index);
} }
}; };

View File

@ -71,53 +71,39 @@ struct pusher<T*> {
} }
}; };
template<typename T, typename Real> template<typename T>
struct pusher<unique_usertype<T, Real>> { struct pusher<T, std::enable_if_t<is_unique_usertype<T>::value>> {
typedef typename unique_usertype_traits<T>::type P;
typedef typename unique_usertype_traits<T>::actual_type Real;
template <typename Arg, meta::EnableIf<std::is_base_of<Real, meta::Unqualified<Arg>>> = 0>
static int push(lua_State* L, Arg&& arg) {
if (unique_usertype_traits<T>::is_null(arg))
return stack::push(L, nil);
return push_deep(L, std::forward<Arg>(arg));
}
template <typename Arg0, typename Arg1, typename... Args>
static int push(lua_State* L, Arg0&& arg0, Arg0&& arg1, Args&&... args) {
return push_deep(L, std::forward<Arg0>(arg0), std::forward<Arg1>(arg1), std::forward<Args>(args)...);
}
template <typename... Args> template <typename... Args>
static int push(lua_State* L, Args&&... args) { static int push_deep(lua_State* L, Args&&... args) {
T** pref = static_cast<T**>(lua_newuserdata(L, sizeof(T*) + sizeof(detail::special_destruct_func) + sizeof(Real))); P** pref = static_cast<P**>(lua_newuserdata(L, sizeof(P*) + sizeof(detail::special_destruct_func) + sizeof(Real)));
detail::special_destruct_func* fx = static_cast<detail::special_destruct_func*>(static_cast<void*>(pref + 1)); detail::special_destruct_func* fx = static_cast<detail::special_destruct_func*>(static_cast<void*>(pref + 1));
Real* mem = static_cast<Real*>(static_cast<void*>(fx + 1)); Real* mem = static_cast<Real*>(static_cast<void*>(fx + 1));
*fx = detail::special_destruct<T, Real>; *fx = detail::special_destruct<P, Real>;
detail::default_construct::construct(mem, std::forward<Args>(args)...); detail::default_construct::construct(mem, std::forward<Args>(args)...);
*pref = std::addressof(detail::deref(*mem)); *pref = unique_usertype_traits<T>::get(*mem);
if (luaL_newmetatable(L, &usertype_traits<unique_usertype<T>>::metatable[0]) == 1) { if (luaL_newmetatable(L, &usertype_traits<detail::unique_usertype<P>>::metatable[0]) == 1) {
set_field(L, "__gc", detail::unique_destruct<T>); set_field(L, "__gc", detail::unique_destruct<P>);
} }
lua_setmetatable(L, -2); lua_setmetatable(L, -2);
return 1; return 1;
} }
}; };
template<typename T>
struct pusher<T, std::enable_if_t<is_unique_usertype<T>::value>> {
template <typename... Args>
static int push(lua_State* L, Args&&... args) {
typedef typename is_unique_usertype<T>::metatable_type meta_type;
return stack::push<unique_usertype<meta_type, T>>(L, std::forward<Args>(args)...);
}
};
template<typename T, typename D>
struct pusher<std::unique_ptr<T, D>> {
static int push(lua_State* L, std::unique_ptr<T, D> obj) {
if (obj == nullptr)
return stack::push(L, nil);
return stack::push<unique_usertype<T, std::unique_ptr<T, D>>>(L, std::move(obj));
}
};
template<typename T>
struct pusher<std::shared_ptr<T>> {
template <typename S>
static int push(lua_State* L, S&& s) {
if (s == nullptr)
return stack::push(L, nil);
return stack::push<unique_usertype<T, std::shared_ptr<T>>>(L, std::forward<S>(s));
}
};
template<typename T> template<typename T>
struct pusher<std::reference_wrapper<T>> { struct pusher<std::reference_wrapper<T>> {
static int push(lua_State* L, const std::reference_wrapper<T>& t) { static int push(lua_State* L, const std::reference_wrapper<T>& t) {

View File

@ -267,12 +267,16 @@ template <std::size_t I, typename T>
using void_tuple_element_t = typename void_tuple_element<I, T>::type; using void_tuple_element_t = typename void_tuple_element<I, T>::type;
template<typename Signature, bool b = has_deducible_signature<Signature>::value> template<typename Signature, bool b = has_deducible_signature<Signature>::value>
struct fx_traits; struct fx_traits {
static const bool is_member_function = false;
typedef std::tuple<> args_tuple_type;
typedef types<> args_type;
template<std::size_t i>
using arg_at = void_tuple_element_t<i, args_tuple_type>;
};
template<typename Signature> template<typename Signature>
struct fx_traits<Signature, true> : fx_traits<decltype(&Signature::operator()), false> { struct fx_traits<Signature, true> : fx_traits<decltype(&Signature::operator()), false> {};
};
template<typename T, typename R, typename... Args> template<typename T, typename R, typename... Args>
struct fx_traits<R(T::*)(Args...), false> { struct fx_traits<R(T::*)(Args...), false> {

View File

@ -84,6 +84,9 @@ inline int c_trampoline(lua_State* L, lua_CFunction f) {
} }
#endif // Exceptions vs. No Exceptions #endif // Exceptions vs. No Exceptions
struct empty { void operator()() {} }; struct empty { void operator()() {} };
template <typename T>
struct unique_usertype {};
} // detail } // detail
struct nil_t {}; struct nil_t {};
const nil_t nil {}; const nil_t nil {};
@ -94,8 +97,52 @@ inline bool operator!=(nil_t, nil_t) { return false; }
typedef std::remove_pointer_t<lua_CFunction> lua_r_CFunction; typedef std::remove_pointer_t<lua_CFunction> lua_r_CFunction;
template <typename T, typename = void> template <typename T>
struct unique_usertype {}; struct unique_usertype_traits {
typedef T type;
typedef T actual_type;
static const bool value = false;
template <typename U>
static bool is_null(U&&) {
return false;
}
template <typename U>
static auto get(U&& value) {
return std::addressof(detail::deref(value));
}
};
template <typename T>
struct unique_usertype_traits<std::shared_ptr<T>> {
typedef T type;
typedef std::shared_ptr<T> actual_type;
static const bool value = true;
static bool is_null(const actual_type& value) {
return value == nullptr;
}
static type* get(const actual_type& p) {
return p.get();
}
};
template <typename T, typename D>
struct unique_usertype_traits<std::unique_ptr<T, D>> {
typedef T type;
typedef std::unique_ptr<T, D> actual_type;
static const bool value = true;
static bool is_null(const actual_type& value) {
return value == nullptr;
}
static type* get(const actual_type& p) {
return p.get();
}
};
template <typename T> template <typename T>
struct non_null {}; struct non_null {};
@ -442,7 +489,7 @@ template <typename T>
struct is_proxy_primitive : is_lua_primitive<T> { }; struct is_proxy_primitive : is_lua_primitive<T> { };
template <typename T> template <typename T>
struct is_unique_usertype : std::false_type {}; struct is_unique_usertype : std::integral_constant<bool, unique_usertype_traits<T>::value> {};
template <typename T> template <typename T>
struct is_transparent_argument : std::false_type {}; struct is_transparent_argument : std::false_type {};

View File

@ -477,7 +477,7 @@ public:
// push pointer tables first, // push pointer tables first,
usertype_detail::push_metatable<T*, usertype_detail::stage::refmeta>(L, needsindexfunction, functions, functiontable, metafunctiontable, baseclasscheck, baseclasscast); usertype_detail::push_metatable<T*, usertype_detail::stage::refmeta>(L, needsindexfunction, functions, functiontable, metafunctiontable, baseclasscheck, baseclasscast);
lua_pop(L, 1); lua_pop(L, 1);
usertype_detail::push_metatable<unique_usertype<T>, usertype_detail::stage::uniquemeta>(L, needsindexfunction, functions, functiontable, metafunctiontable, baseclasscheck, baseclasscast); usertype_detail::push_metatable<detail::unique_usertype<T>, usertype_detail::stage::uniquemeta>(L, needsindexfunction, functions, functiontable, metafunctiontable, baseclasscheck, baseclasscast);
lua_pop(L, 1); lua_pop(L, 1);
// but leave the regular T table on last // but leave the regular T table on last
// so it can be linked to a type for usage with `.new(...)` or `:new(...)` // so it can be linked to a type for usage with `.new(...)` or `:new(...)`

View File

@ -665,11 +665,13 @@ TEST_CASE("usertype/unique-shared-ptr", "manage the conversion and use of unique
lua.set("sharedint", sharedint); lua.set("sharedint", sharedint);
std::unique_ptr<int64_t>& uniqueintref = lua["uniqueint"]; std::unique_ptr<int64_t>& uniqueintref = lua["uniqueint"];
std::shared_ptr<int64_t>& sharedintref = lua["sharedint"]; std::shared_ptr<int64_t>& sharedintref = lua["sharedint"];
int64_t* rawuniqueintref = lua["uniqueint"];
int64_t* rawsharedintref = lua["sharedint"];
int siusecount = sharedintref.use_count(); int siusecount = sharedintref.use_count();
REQUIRE(uniqueintref != nullptr); REQUIRE((uniqueintref.get() == rawuniqueintref && sharedintref.get() == rawsharedintref));
REQUIRE(sharedintref != nullptr); REQUIRE((uniqueintref != nullptr && sharedintref != nullptr && rawuniqueintref != nullptr && rawsharedintref != nullptr));
REQUIRE(unique_value == *uniqueintref.get()); REQUIRE((unique_value == *uniqueintref.get() && unique_value == *sharedintref.get()));
REQUIRE(unique_value == *sharedintref.get()); REQUIRE((unique_value == *rawuniqueintref && unique_value == *rawsharedintref));
REQUIRE(siusecount == sharedint.use_count()); REQUIRE(siusecount == sharedint.use_count());
std::shared_ptr<int64_t> moreref = sharedint; std::shared_ptr<int64_t> moreref = sharedint;
REQUIRE(unique_value == *moreref.get()); REQUIRE(unique_value == *moreref.get());
@ -910,6 +912,12 @@ TEST_CASE("usertype/readonly-and-static-functions", "Check if static functions c
void func() {} void func() {}
static void oh_boy() {}
static int oh_boy(std::string name) {
return name.length();
}
int operator()(int x) { int operator()(int x) {
return x; return x;
} }
@ -923,9 +931,13 @@ TEST_CASE("usertype/readonly-and-static-functions", "Check if static functions c
"something", something, "something", something,
"something2", [](int x, int y) { return x + y; }, "something2", [](int x, int y) { return x + y; },
"func", &bark::func, "func", &bark::func,
"oh_boy", sol::overload(sol::resolve<void()>(&bark::oh_boy), sol::resolve<int(std::string)>(&bark::oh_boy)),
sol::meta_function::call_function, &bark::operator() sol::meta_function::call_function, &bark::operator()
); );
lua.script("assert(bark.oh_boy('woo') == 3)");
lua.script("bark.oh_boy()");
bark b; bark b;
lua.set("b", &b); lua.set("b", &b);