mirror of
https://github.com/ThePhD/sol2.git
synced 2024-03-22 13:10:44 +08:00
:l Stop finding bugs in my code -- unique_usertype_traits implemented, sol::overloaded work for static functions
This commit is contained in:
parent
e9ed1a62e1
commit
38d21827b1
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
42
docs/source/api/unique_usertype_traits.rst
Normal file
42
docs/source/api/unique_usertype_traits.rst
Normal 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.
|
|
@ -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.
|
||||||
|
|
|
@ -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!
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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));
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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> {
|
||||||
|
|
|
@ -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 {};
|
||||||
|
|
|
@ -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(...)`
|
||||||
|
|
20
tests.cpp
20
tests.cpp
|
@ -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);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user