: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
userdata
usertype_memory
unique_usertype_traits
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.
.. 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
:caption: 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
===============
.. 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>`
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!
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>
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::pop_front_type_t<typename traits_type::args_type> args_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;
static const std::size_t arity = traits_type::arity - 1;
static const bool is_free = false;
T* item;
function_type invocation;
@ -186,6 +216,8 @@ struct functor<T, member_property<RSig, WSig>, C> {
typedef member_property<RSig, WSig> function_type;
typedef meta::Not<std::is_void<RSig>> can_read;
typedef meta::Not<std::is_void<WSig>> can_write;
static const bool is_free = false;
T* item;
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;
typedef std::true_type can_read;
typedef std::true_type can_write;
static const bool is_free = false;
T* item;
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::return_type return_type;
static const std::size_t arity = traits_type::arity;
static const bool is_free = false;
T* item;
Func invocation;

View File

@ -30,13 +30,16 @@ namespace sol {
namespace function_detail {
namespace internals {
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>
struct overload_traits<functor<T, Func, X>> {
typedef typename functor<T, Func, X>::args_type args_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 boost = static_cast<std::size_t>(functor<T, Func, X>::is_free);
};
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>
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 meta::tuple_types<typename traits::return_type> return_types;
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<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);
int fxarity = traits::boost + nfxarity;
// 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) {
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) {
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)) {
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)...);
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, nfxarity, 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)) {}
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) {
auto& func = std::get<I>(overloads);
func.item = detail::ptr(stack::get<T>(L, 1));
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 {
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);

View File

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

View File

@ -266,7 +266,7 @@ struct checker<T, type::userdata, C> {
return true;
if (stack_detail::check_metatable<U*>(L))
return true;
if (stack_detail::check_metatable<unique_usertype<U>>(L))
if (stack_detail::check_metatable<detail::unique_usertype<U>>(L))
return true;
#ifndef SOL_NO_EXCEPTIONS
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>
struct checker<unique_usertype<T, Real>, type::userdata, C> {
template<typename T>
struct checker<T, type::userdata, std::enable_if_t<is_unique_usertype<T>::value>> {
template <typename Handler>
static bool check(lua_State* L, int index, Handler&& handler) {
return checker<T, type::userdata, C>{}.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));
return checker<typename unique_usertype_traits<T>::type, type::userdata>{}.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>
struct getter<non_null<T*>> {
static T* get(lua_State* L, int index = -1) {
@ -246,16 +236,15 @@ struct getter<T&> {
};
template<typename T>
struct getter<std::shared_ptr<T>> {
static std::shared_ptr<T>& get(lua_State* L, int index = -1) {
return getter<unique_usertype<T, std::shared_ptr<T>>>::get(L, index);
}
};
struct getter<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 T, typename D>
struct getter<std::unique_ptr<T, D>> {
static std::unique_ptr<T, D>& get(lua_State* L, int index = -1) {
return getter<unique_usertype<T, std::unique_ptr<T, D>>>::get(L, index);
static Real& get(lua_State* L, int index = -1) {
P** pref = static_cast<P**>(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;
}
};

View File

@ -71,53 +71,39 @@ struct pusher<T*> {
}
};
template<typename T, typename Real>
struct pusher<unique_usertype<T, Real>> {
template<typename T>
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>
static int push(lua_State* L, Args&&... args) {
T** pref = static_cast<T**>(lua_newuserdata(L, sizeof(T*) + sizeof(detail::special_destruct_func) + sizeof(Real)));
static int push_deep(lua_State* L, Args&&... args) {
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));
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)...);
*pref = std::addressof(detail::deref(*mem));
if (luaL_newmetatable(L, &usertype_traits<unique_usertype<T>>::metatable[0]) == 1) {
set_field(L, "__gc", detail::unique_destruct<T>);
*pref = unique_usertype_traits<T>::get(*mem);
if (luaL_newmetatable(L, &usertype_traits<detail::unique_usertype<P>>::metatable[0]) == 1) {
set_field(L, "__gc", detail::unique_destruct<P>);
}
lua_setmetatable(L, -2);
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>
struct pusher<std::reference_wrapper<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;
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>
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>
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
struct empty { void operator()() {} };
template <typename T>
struct unique_usertype {};
} // detail
struct nil_t {};
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;
template <typename T, typename = void>
struct unique_usertype {};
template <typename T>
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>
struct non_null {};
@ -442,7 +489,7 @@ template <typename T>
struct is_proxy_primitive : is_lua_primitive<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>
struct is_transparent_argument : std::false_type {};

View File

@ -477,7 +477,7 @@ public:
// push pointer tables first,
usertype_detail::push_metatable<T*, usertype_detail::stage::refmeta>(L, needsindexfunction, functions, functiontable, metafunctiontable, baseclasscheck, baseclasscast);
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);
// but leave the regular T table on last
// 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);
std::unique_ptr<int64_t>& uniqueintref = lua["uniqueint"];
std::shared_ptr<int64_t>& sharedintref = lua["sharedint"];
int64_t* rawuniqueintref = lua["uniqueint"];
int64_t* rawsharedintref = lua["sharedint"];
int siusecount = sharedintref.use_count();
REQUIRE(uniqueintref != nullptr);
REQUIRE(sharedintref != nullptr);
REQUIRE(unique_value == *uniqueintref.get());
REQUIRE(unique_value == *sharedintref.get());
REQUIRE((uniqueintref.get() == rawuniqueintref && sharedintref.get() == rawsharedintref));
REQUIRE((uniqueintref != nullptr && sharedintref != nullptr && rawuniqueintref != nullptr && rawsharedintref != nullptr));
REQUIRE((unique_value == *uniqueintref.get() && unique_value == *sharedintref.get()));
REQUIRE((unique_value == *rawuniqueintref && unique_value == *rawsharedintref));
REQUIRE(siusecount == sharedint.use_count());
std::shared_ptr<int64_t> moreref = sharedint;
REQUIRE(unique_value == *moreref.get());
@ -910,6 +912,12 @@ TEST_CASE("usertype/readonly-and-static-functions", "Check if static functions c
void func() {}
static void oh_boy() {}
static int oh_boy(std::string name) {
return name.length();
}
int operator()(int x) {
return x;
}
@ -923,9 +931,13 @@ TEST_CASE("usertype/readonly-and-static-functions", "Check if static functions c
"something", something,
"something2", [](int x, int y) { return x + y; },
"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()
);
lua.script("assert(bark.oh_boy('woo') == 3)");
lua.script("bark.oh_boy()");
bark b;
lua.set("b", &b);