mirror of
https://github.com/ThePhD/sol2.git
synced 2024-03-22 13:10:44 +08:00
Huge improvements to the library and fixes to compile in g++.
usertype now respects factory functions and does not make default constructors/destructors unless the compiler says its okay new and __gc functions can be overridden for usertypes to provide handle-like creation and deletion functions Overloading match fixes RAII improvements for all usertypes Added tests to make sure these features stay
This commit is contained in:
parent
b2b73db5cb
commit
019c7b037b
|
@ -44,9 +44,9 @@ private:
|
||||||
auto invoke( types<Ret...>, std::index_sequence<I...>, std::ptrdiff_t n ) const {
|
auto invoke( types<Ret...>, std::index_sequence<I...>, std::ptrdiff_t n ) const {
|
||||||
luacall( n, sizeof...( Ret ) );
|
luacall( n, sizeof...( Ret ) );
|
||||||
int stacksize = lua_gettop( lua_state( ) );
|
int stacksize = lua_gettop( lua_state( ) );
|
||||||
int firstreturn = std::max(1, stacksize - static_cast<int>(sizeof...(Ret)) + 1);
|
int firstreturn = std::max(1, stacksize - static_cast<int>(sizeof...(Ret)) + 1);
|
||||||
auto r = stack::get<std::tuple<Ret...>>( lua_state( ), firstreturn );
|
auto r = stack::get<std::tuple<Ret...>>( lua_state( ), firstreturn );
|
||||||
lua_pop(lua_state(), static_cast<int>(sizeof...(Ret)));
|
lua_pop(lua_state(), static_cast<int>(sizeof...(Ret)));
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -140,8 +140,8 @@ private:
|
||||||
luacall(n, sizeof...(Ret), h);
|
luacall(n, sizeof...(Ret), h);
|
||||||
int stacksize = lua_gettop(lua_state());
|
int stacksize = lua_gettop(lua_state());
|
||||||
int firstreturn = std::max(0, stacksize - static_cast<int>(sizeof...(Ret)) + 1);
|
int firstreturn = std::max(0, stacksize - static_cast<int>(sizeof...(Ret)) + 1);
|
||||||
auto r = stack::get<std::tuple<Ret...>>(lua_state(), firstreturn);
|
auto r = stack::get<std::tuple<Ret...>>(lua_state(), firstreturn);
|
||||||
lua_pop(lua_state(), static_cast<int>(sizeof...(Ret)));
|
lua_pop(lua_state(), static_cast<int>(sizeof...(Ret)));
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -323,7 +323,7 @@ struct pusher<function_sig<Sigs...>> {
|
||||||
int metapushed = luaL_newmetatable(L, metatablename);
|
int metapushed = luaL_newmetatable(L, metatablename);
|
||||||
if(metapushed == 1) {
|
if(metapushed == 1) {
|
||||||
lua_pushstring(L, "__gc");
|
lua_pushstring(L, "__gc");
|
||||||
stack::push<lua_CFunction>(L, detail::gc);
|
stack::push(L, detail::gc);
|
||||||
lua_settable(L, -3);
|
lua_settable(L, -3);
|
||||||
lua_pop(L, 1);
|
lua_pop(L, 1);
|
||||||
}
|
}
|
||||||
|
@ -365,7 +365,7 @@ struct pusher<overload_set<Functions...>> {
|
||||||
template<std::size_t... I, typename Set>
|
template<std::size_t... I, typename Set>
|
||||||
static int push(std::index_sequence<I...>, lua_State* L, Set&& set) {
|
static int push(std::index_sequence<I...>, lua_State* L, Set&& set) {
|
||||||
pusher<function_sig<>>{}.set_fx<Set>(L, std::make_unique<overloaded_function<Functions...>>(std::get<I>(set)...));
|
pusher<function_sig<>>{}.set_fx<Set>(L, std::make_unique<overloaded_function<Functions...>>(std::get<I>(set)...));
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Set>
|
template<typename Set>
|
||||||
|
|
|
@ -68,7 +68,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
~function_result() {
|
~function_result() {
|
||||||
lua_pop(L, returncount);
|
lua_pop(L, returncount);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -101,7 +101,7 @@ public:
|
||||||
L = o.L;
|
L = o.L;
|
||||||
index = o.index;
|
index = o.index;
|
||||||
returncount = o.returncount;
|
returncount = o.returncount;
|
||||||
popcount = o.popcount;
|
popcount = o.popcount;
|
||||||
error = o.error;
|
error = o.error;
|
||||||
// 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
|
||||||
|
|
|
@ -28,5 +28,6 @@
|
||||||
#include "function_types_member.hpp"
|
#include "function_types_member.hpp"
|
||||||
#include "function_types_usertype.hpp"
|
#include "function_types_usertype.hpp"
|
||||||
#include "function_types_overload.hpp"
|
#include "function_types_overload.hpp"
|
||||||
|
#include "function_types_allocator.hpp"
|
||||||
|
|
||||||
#endif // SOL_FUNCTION_TYPES_HPP
|
#endif // SOL_FUNCTION_TYPES_HPP
|
||||||
|
|
|
@ -22,9 +22,126 @@
|
||||||
#ifndef SOL_FUNCTION_TYPES_ALLOCATOR_HPP
|
#ifndef SOL_FUNCTION_TYPES_ALLOCATOR_HPP
|
||||||
#define SOL_FUNCTION_TYPES_ALLOCATOR_HPP
|
#define SOL_FUNCTION_TYPES_ALLOCATOR_HPP
|
||||||
|
|
||||||
#include "stack.hpp"
|
#include "raii.hpp"
|
||||||
|
#include "function_types_overload.hpp"
|
||||||
|
|
||||||
namespace sol {
|
namespace sol {
|
||||||
|
namespace detail {
|
||||||
|
template <typename T, typename List>
|
||||||
|
struct void_call;
|
||||||
|
|
||||||
|
template <typename T, typename... Args>
|
||||||
|
struct void_call<T, types<Args...>> {
|
||||||
|
static void call(Args... args) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct constructor_match {
|
||||||
|
T* obj;
|
||||||
|
|
||||||
|
constructor_match(T* obj) : obj(obj) {}
|
||||||
|
|
||||||
|
template <bool b, typename Fx, std::size_t I, typename... R, typename... Args>
|
||||||
|
int operator()(Bool<b>, types<Fx>, Index<I>, types<R...> r, types<Args...> a, lua_State* L, int, int start) const {
|
||||||
|
default_construct func{};
|
||||||
|
return stack::typed_call<b ? false : stack::stack_detail::default_check_arguments>(r, a, func, L, start, obj);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // detail
|
||||||
|
|
||||||
|
template <typename T, typename... TypeLists, typename Match>
|
||||||
|
inline int construct(Match&& matchfx, lua_State* L, int fxarity, int start) {
|
||||||
|
// use same overload resolution matching as all other parts of the framework
|
||||||
|
return overload_match_arity<decltype(detail::void_call<T, TypeLists>::call)...>(std::forward<Match>(matchfx), L, fxarity, start);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename... TypeLists>
|
||||||
|
inline int construct(lua_State* L) {
|
||||||
|
static const auto& meta = usertype_traits<T>::metatable;
|
||||||
|
call_syntax syntax = stack::get_call_syntax(L, meta);
|
||||||
|
int argcount = lua_gettop(L) - static_cast<int>(syntax);
|
||||||
|
|
||||||
|
T** pointerpointer = reinterpret_cast<T**>(lua_newuserdata(L, sizeof(T*) + sizeof(T)));
|
||||||
|
T*& referencepointer = *pointerpointer;
|
||||||
|
T* obj = reinterpret_cast<T*>(pointerpointer + 1);
|
||||||
|
referencepointer = obj;
|
||||||
|
reference userdataref(L, -1);
|
||||||
|
userdataref.pop();
|
||||||
|
|
||||||
|
construct<T, TypeLists...>(detail::constructor_match<T>(obj), L, argcount, 1 + static_cast<int>(syntax));
|
||||||
|
|
||||||
|
userdataref.push();
|
||||||
|
luaL_getmetatable(L, &meta[0]);
|
||||||
|
if (stack::get<type>(L) == type::nil) {
|
||||||
|
lua_pop(L, 1);
|
||||||
|
std::string err = "unable to get usertype metatable for ";
|
||||||
|
err += meta;
|
||||||
|
throw error(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
lua_setmetatable(L, -2);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline int destruct(lua_State* L) {
|
||||||
|
userdata udata = stack::get<userdata>(L, 1);
|
||||||
|
// The first sizeof(T*) bytes are the reference: the rest is
|
||||||
|
// the actual data itself (if there is a reference at all)
|
||||||
|
T** pobj = reinterpret_cast<T**>(udata.value);
|
||||||
|
T*& obj = *pobj;
|
||||||
|
std::allocator<T> alloc{};
|
||||||
|
alloc.destroy(obj);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename... Functions>
|
||||||
|
struct usertype_constructor_function : base_function {
|
||||||
|
typedef std::tuple<Functions...> overload_list;
|
||||||
|
typedef std::index_sequence_for<Functions...> indices;
|
||||||
|
overload_list overloads;
|
||||||
|
|
||||||
|
usertype_constructor_function(constructor_wrapper<Functions...> set) : usertype_constructor_function(indices(), set) {}
|
||||||
|
|
||||||
|
template <std::size_t... I>
|
||||||
|
usertype_constructor_function(std::index_sequence<I...>, constructor_wrapper<Functions...> set) : usertype_constructor_function(std::get<I>(set)...) {}
|
||||||
|
|
||||||
|
usertype_constructor_function(Functions... fxs) : overloads(fxs...) {}
|
||||||
|
|
||||||
|
template <bool b, typename Fx, std::size_t I, typename... R, typename... Args>
|
||||||
|
int call(Bool<b>, types<Fx>, Index<I>, types<R...> r, types<Args...> a, lua_State* L, int, int start) {
|
||||||
|
static const auto& meta = usertype_traits<T>::metatable;
|
||||||
|
T** pointerpointer = reinterpret_cast<T**>(lua_newuserdata(L, sizeof(T*) + sizeof(T)));
|
||||||
|
T*& referencepointer = *pointerpointer;
|
||||||
|
T* obj = reinterpret_cast<T*>(pointerpointer + 1);
|
||||||
|
referencepointer = obj;
|
||||||
|
reference userdataref(L, -1);
|
||||||
|
userdataref.pop();
|
||||||
|
|
||||||
|
auto& func = std::get<I>(overloads);
|
||||||
|
stack::typed_call<b ? false : stack::stack_detail::default_check_arguments>(r, a, func, L, start, detail::implicit_wrapper<T>(obj));
|
||||||
|
|
||||||
|
userdataref.push();
|
||||||
|
luaL_getmetatable(L, &meta[0]);
|
||||||
|
if (stack::get<type>(L) == type::nil) {
|
||||||
|
lua_pop(L, 1);
|
||||||
|
std::string err = "unable to get usertype metatable for ";
|
||||||
|
err += meta;
|
||||||
|
throw error(err);
|
||||||
|
}
|
||||||
|
lua_setmetatable(L, -2);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual int operator()(lua_State* L) override {
|
||||||
|
static const auto& meta = usertype_traits<T>::metatable;
|
||||||
|
call_syntax syntax = stack::get_call_syntax(L, meta);
|
||||||
|
int argcount = lua_gettop(L) - static_cast<int>(syntax);
|
||||||
|
auto mfx = [&](auto&&... args) { return this->call(std::forward<decltype(args)>(args)...); };
|
||||||
|
return construct<T, pop_front_type_t<function_args_t<Functions>>...>(mfx, L, argcount, 1 + static_cast<int>(syntax));
|
||||||
|
}
|
||||||
|
};
|
||||||
} // sol
|
} // sol
|
||||||
|
|
||||||
#endif // SOL_FUNCTION_TYPES_ALLOCATOR_HPP
|
#endif // SOL_FUNCTION_TYPES_ALLOCATOR_HPP
|
||||||
|
|
|
@ -31,6 +31,7 @@ struct ref_call_t {} const ref_call = ref_call_t{};
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct implicit_wrapper {
|
struct implicit_wrapper {
|
||||||
T& item;
|
T& item;
|
||||||
|
implicit_wrapper(T* item) : item(*item) {}
|
||||||
implicit_wrapper(T& item) : item(item) {}
|
implicit_wrapper(T& item) : item(item) {}
|
||||||
operator T& () {
|
operator T& () {
|
||||||
return item;
|
return item;
|
||||||
|
@ -49,13 +50,13 @@ function_packer<Sig, Args...> function_pack( Args&&... args ) {
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool check_types(types<>, std::index_sequence<>, lua_State*, int) {
|
inline bool check_types(types<>, std::index_sequence<>, lua_State*, int) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Arg, typename... Args, std::size_t I, std::size_t... In>
|
template <typename Arg, typename... Args, std::size_t I, std::size_t... In>
|
||||||
inline bool check_types(types<Arg, Args...>, std::index_sequence<I, In...>, lua_State* L, int start = 1) {
|
inline bool check_types(types<Arg, Args...>, std::index_sequence<I, In...>, lua_State* L, int start = 1) {
|
||||||
if (!stack::check<Arg>(L, start + I, no_panic))
|
if (!stack::check<Arg>(L, start + I, no_panic))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return check_types(types<Args...>(), std::index_sequence<In...>(), L, start);
|
return check_types(types<Args...>(), std::index_sequence<In...>(), L, start);
|
||||||
}
|
}
|
||||||
|
@ -129,7 +130,7 @@ struct functor<T, Func, std::enable_if_t<std::is_member_object_pointer<Func>::va
|
||||||
template<typename T, typename Func>
|
template<typename T, typename Func>
|
||||||
struct functor<T, Func, std::enable_if_t<std::is_function<Func>::value || std::is_class<Func>::value>> {
|
struct functor<T, Func, std::enable_if_t<std::is_function<Func>::value || std::is_class<Func>::value>> {
|
||||||
typedef callable_traits<Func> traits_type;
|
typedef callable_traits<Func> traits_type;
|
||||||
typedef remove_one_type<typename traits_type::args_type> args_type;
|
typedef 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 std::tuple_element_t<0, typename traits_type::args_tuple_type> Arg0;
|
typedef std::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;
|
||||||
|
@ -183,68 +184,68 @@ struct base_function {
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
static int base_call(lua_State* L, void* inheritancedata) {
|
static int base_call(lua_State* L, void* inheritancedata) {
|
||||||
if (inheritancedata == nullptr) {
|
if (inheritancedata == nullptr) {
|
||||||
throw error("call from Lua to C++ function has null data");
|
throw error("call from Lua to C++ function has null data");
|
||||||
}
|
}
|
||||||
|
|
||||||
base_function* pfx = static_cast<base_function*>(inheritancedata);
|
base_function* pfx = static_cast<base_function*>(inheritancedata);
|
||||||
base_function& fx = *pfx;
|
base_function& fx = *pfx;
|
||||||
int r = fx(L);
|
int r = fx(L);
|
||||||
return r;
|
return r;
|
||||||
}
|
|
||||||
|
|
||||||
static int base_gc(lua_State*, void* udata) {
|
|
||||||
if (udata == nullptr) {
|
|
||||||
throw error("call from lua to C++ gc function with null data");
|
|
||||||
}
|
|
||||||
|
|
||||||
base_function* ptr = static_cast<base_function*>(udata);
|
|
||||||
std::default_delete<base_function> dx{};
|
|
||||||
dx(ptr);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <std::size_t limit>
|
|
||||||
static void func_gc(std::true_type, lua_State*) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
template <std::size_t limit>
|
|
||||||
static void func_gc(std::false_type, lua_State* L) {
|
|
||||||
// Shut up clang tautological error without throwing out std::size_t
|
|
||||||
for (std::size_t i = 0; i < limit; ++i) {
|
|
||||||
upvalue up = stack::get<upvalue>(L, static_cast<int>(i + 1));
|
|
||||||
base_function* obj = static_cast<base_function*>(up.value);
|
|
||||||
std::allocator<base_function> alloc{};
|
|
||||||
alloc.destroy(obj);
|
|
||||||
alloc.deallocate(obj, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline int call(lua_State* L) {
|
|
||||||
void** pinheritancedata = static_cast<void**>(stack::get<upvalue>(L, 1).value);
|
|
||||||
return base_call(L, *pinheritancedata);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline int gc(lua_State* L) {
|
|
||||||
void** pudata = static_cast<void**>(stack::get<userdata>(L, 1).value);
|
|
||||||
return base_gc(L, *pudata);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<std::size_t I>
|
|
||||||
inline int usertype_call(lua_State* L) {
|
|
||||||
// Zero-based template parameter, but upvalues start at 1
|
|
||||||
return base_call(L, stack::get<upvalue>(L, I + 1));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<std::size_t I>
|
|
||||||
inline int usertype_gc(lua_State* L) {
|
|
||||||
func_gc<I>(Bool<(I < 1)>(), L);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int base_gc(lua_State*, void* udata) {
|
||||||
|
if (udata == nullptr) {
|
||||||
|
throw error("call from lua to C++ gc function with null data");
|
||||||
|
}
|
||||||
|
|
||||||
|
base_function* ptr = static_cast<base_function*>(udata);
|
||||||
|
std::default_delete<base_function> dx{};
|
||||||
|
dx(ptr);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <std::size_t limit>
|
||||||
|
static void func_gc(std::true_type, lua_State*) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
template <std::size_t limit>
|
||||||
|
static void func_gc(std::false_type, lua_State* L) {
|
||||||
|
for (std::size_t i = 0; i < limit; ++i) {
|
||||||
|
upvalue up = stack::get<upvalue>(L, static_cast<int>(i + 1));
|
||||||
|
if (up.value == nullptr)
|
||||||
|
continue;
|
||||||
|
base_function* obj = static_cast<base_function*>(up.value);
|
||||||
|
std::allocator<base_function> alloc{};
|
||||||
|
alloc.destroy(obj);
|
||||||
|
alloc.deallocate(obj, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int call(lua_State* L) {
|
||||||
|
void** pinheritancedata = static_cast<void**>(stack::get<upvalue>(L, 1).value);
|
||||||
|
return base_call(L, *pinheritancedata);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int gc(lua_State* L) {
|
||||||
|
void** pudata = static_cast<void**>(stack::get<userdata>(L, 1).value);
|
||||||
|
return base_gc(L, *pudata);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<std::size_t I>
|
||||||
|
inline int usertype_call(lua_State* L) {
|
||||||
|
// Zero-based template parameter, but upvalues start at 1
|
||||||
|
return base_call(L, stack::get<upvalue>(L, I + 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<std::size_t I>
|
||||||
|
inline int usertype_gc(lua_State* L) {
|
||||||
|
func_gc<I>(Bool<(I < 1)>(), L);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
} // detail
|
||||||
} // sol
|
} // sol
|
||||||
|
|
||||||
#endif // SOL_FUNCTION_TYPES_CORE_HPP
|
#endif // SOL_FUNCTION_TYPES_CORE_HPP
|
||||||
|
|
|
@ -28,45 +28,46 @@
|
||||||
|
|
||||||
namespace sol {
|
namespace sol {
|
||||||
namespace detail {
|
namespace detail {
|
||||||
template <std::size_t... M, typename Match>
|
template <std::size_t... M, typename Match, typename... Args>
|
||||||
inline int match_arity(types<>, std::index_sequence<>, std::index_sequence<M...>, std::ptrdiff_t, lua_State*, Match&&, int) {
|
inline int overload_match_arity(types<>, std::index_sequence<>, std::index_sequence<M...>, Match&&, lua_State*, int, int, Args&&...) {
|
||||||
throw error("no matching function call takes this number of arguments and the specified types");
|
throw error("no matching function call takes this number of arguments and the specified types");
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Fx, typename... Fxs, std::size_t I, std::size_t... In, std::size_t... M, typename Match>
|
template <typename Fx, typename... Fxs, std::size_t I, std::size_t... In, std::size_t... M, typename Match, typename... Args>
|
||||||
inline int match_arity(types<Fx, Fxs...>, std::index_sequence<I, In...>, std::index_sequence<M...>, std::ptrdiff_t fxarity, lua_State* L, Match&& matchfx, int start) {
|
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) {
|
||||||
typedef function_traits<Fx> traits;
|
typedef function_traits<Fx> traits;
|
||||||
typedef tuple_types<typename function_traits<Fx>::return_type> return_types;
|
typedef tuple_types<typename function_traits<Fx>::return_type> return_types;
|
||||||
typedef typename function_traits<Fx>::args_type args_type;
|
typedef typename function_traits<Fx>::args_type args_type;
|
||||||
typedef typename args_type::indices args_indices;
|
typedef typename args_type::indices args_indices;
|
||||||
// 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 (find_in_pack_v<Index<traits::arity>, Index<M>...>::value || traits::arity != fxarity) {
|
if (find_in_pack_v<Index<traits::arity>, Index<M>...>::value) {
|
||||||
return match_arity(types<Fxs...>(), std::index_sequence<In...>(), std::index_sequence<M...>(), fxarity, L, std::forward<Match>(matchfx), start);
|
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 (traits::arity != fxarity) {
|
if (traits::arity != fxarity) {
|
||||||
return match_arity(types<Fxs...>(), std::index_sequence<In...>(), std::index_sequence<traits::arity, M...>(), fxarity, L, std::forward<Match>(matchfx), start);
|
return overload_match_arity(types<Fxs...>(), std::index_sequence<In...>(), std::index_sequence<traits::arity, M...>(), std::forward<Match>(matchfx), L, fxarity, start, std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
if (!detail::check_types(args_type(), args_indices(), L, start)) {
|
if (sizeof...(Fxs) != 0 && !detail::check_types(args_type(), args_indices(), L, start)) {
|
||||||
return match_arity(types<Fxs...>(), std::index_sequence<In...>(), std::index_sequence<M...>(), fxarity, L, std::forward<Match>(matchfx), start);
|
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 matchfx(Index<I>(), fxarity, L, start);
|
return matchfx(Bool<sizeof...(Fxs) != 0>(), types<Fx>(), Index<I>(), return_types(), args_type(), L, fxarity, start, std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
} // detail
|
} // detail
|
||||||
|
|
||||||
template <typename... Functions, typename Match>
|
template <typename... Functions, typename Match, typename... Args>
|
||||||
inline int match(std::ptrdiff_t fxarity, lua_State* L, Match&& matchfx, int start = 1) {
|
inline int overload_match_arity(Match&& matchfx, lua_State* L, int fxarity, int start = 1, Args&&... args) {
|
||||||
return detail::match_arity(types<Functions...>(), std::index_sequence_for<Functions...>(), std::index_sequence<>(), fxarity, L, std::forward<Match>(matchfx), start);
|
return detail::overload_match_arity(types<Functions...>(), std::index_sequence_for<Functions...>(), std::index_sequence<>(), std::forward<Match>(matchfx), L, fxarity, start, std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename... Functions, typename Match>
|
template <typename... Functions, typename Match, typename... Args>
|
||||||
inline int match(lua_State* L, Match&& matchfx, int start = 1) {
|
inline int overload_match(Match&& matchfx, lua_State* L, int start = 1, Args&&... args) {
|
||||||
std::ptrdiff_t fxarity = lua_gettop(L) - (start - 1);
|
int fxarity = lua_gettop(L) - (start - 1);
|
||||||
return match<Functions...>(fxarity, L, std::forward<Match>(matchfx), start);
|
return overload_match_arity<Functions...>(std::forward<Match>(matchfx), L, fxarity, start, std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename... Functions>
|
template <typename... Functions>
|
||||||
struct overloaded_function : base_function {
|
struct overloaded_function : base_function {
|
||||||
typedef std::tuple<Functions...> overload_list;
|
typedef std::tuple<Functions...> overload_list;
|
||||||
|
typedef std::index_sequence_for<Functions...> indices;
|
||||||
overload_list overloads;
|
overload_list overloads;
|
||||||
|
|
||||||
overloaded_function(overload_set<Functions...> set)
|
overloaded_function(overload_set<Functions...> set)
|
||||||
|
@ -74,26 +75,22 @@ struct overloaded_function : base_function {
|
||||||
|
|
||||||
template <std::size_t... I>
|
template <std::size_t... I>
|
||||||
overloaded_function(std::index_sequence<I...>, overload_set<Functions...> set)
|
overloaded_function(std::index_sequence<I...>, overload_set<Functions...> set)
|
||||||
: overloaded_function(std::get<In>(set)...) {}
|
: overloaded_function(std::get<I>(set)...) {}
|
||||||
|
|
||||||
overloaded_function(Functions... fxs)
|
overloaded_function(Functions... fxs)
|
||||||
: overloads(fxs...) {
|
: overloads(fxs...) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <std::size_t I>
|
template <bool b, typename Fx, std::size_t I, typename... R, typename... Args>
|
||||||
int call(Index<I>, int, lua_State* L, int) {
|
int call(Bool<b>, types<Fx>, Index<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);
|
||||||
typedef Unqualified<decltype(func)> Fx;
|
return stack::typed_call<b ? false : stack::stack_detail::default_check_arguments>(r, a, func, L, start);
|
||||||
typedef function_traits<Fx> traits;
|
|
||||||
typedef tuple_types<typename function_traits<Fx>::return_type> return_types;
|
|
||||||
typedef typename function_traits<Fx>::args_type args_type;
|
|
||||||
return stack::typed_call<false>(return_types(), args_type(), func, L);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual int operator()(lua_State* L) override {
|
virtual int operator()(lua_State* L) override {
|
||||||
auto mfx = [&](auto&&... args){ return call(std::forward<decltype(args)>(args)...); };
|
auto mfx = [&](auto&&... args){ return this->call(std::forward<decltype(args)>(args)...); };
|
||||||
return match<Functions...>(L, mfx);
|
return overload_match<Functions...>(mfx, L);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -110,20 +107,16 @@ struct usertype_overloaded_function : base_function {
|
||||||
|
|
||||||
usertype_overloaded_function(Functions... fxs) : overloads(fxs...) {}
|
usertype_overloaded_function(Functions... fxs) : overloads(fxs...) {}
|
||||||
|
|
||||||
template <std::size_t I>
|
template <bool b,typename Fx, std::size_t I, typename... R, typename... Args>
|
||||||
int call(Index<I>, int, lua_State* L, int) {
|
int call(Bool<b>, types<Fx>, Index<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);
|
||||||
typedef Unqualified<decltype(func)> Fx;
|
|
||||||
typedef typename Fx::traits_type traits;
|
|
||||||
typedef tuple_types<typename Fx::return_type> return_types;
|
|
||||||
typedef typename Fx::args_type args_type;
|
|
||||||
func.item = ptr(stack::get<T>(L, 1));
|
func.item = ptr(stack::get<T>(L, 1));
|
||||||
return stack::typed_call<false>(return_types(), args_type(), func, L);
|
return stack::typed_call<b ? false : stack::stack_detail::default_check_arguments>(r, a, func, L, start);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual int operator()(lua_State* L) override {
|
virtual int operator()(lua_State* L) override {
|
||||||
auto mfx = [&](auto&&... args){ return call(std::forward<decltype(args)>(args)...); };
|
auto mfx = [&](auto&&... args){ return this->call(std::forward<decltype(args)>(args)...); };
|
||||||
return match<Functions...>(L, mfx, 2);
|
return overload_match<Functions...>(mfx, L, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -62,18 +62,17 @@ struct usertype_function_core : public base_function {
|
||||||
return stack::push(L, std::forward<Return>(r));
|
return stack::push(L, std::forward<Return>(r));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename... Args>
|
template<typename... Args, std::size_t Start>
|
||||||
int operator()(types<void> tr, types<Args...> ta, lua_State* L) {
|
int operator()(types<void> tr, types<Args...> ta, Index<Start>, lua_State* L) {
|
||||||
//static const std::size_t skew = static_cast<std::size_t>(std::is_member_object_pointer<function_type>::value);
|
stack::call(tr, ta, L, static_cast<int>(Start), fx);
|
||||||
stack::call(tr, ta, L, 0, fx);
|
|
||||||
int nargs = static_cast<int>(sizeof...(Args));
|
int nargs = static_cast<int>(sizeof...(Args));
|
||||||
lua_pop(L, nargs);
|
lua_pop(L, nargs);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename... Ret, typename... Args>
|
template<typename... Ret, typename... Args, std::size_t Start>
|
||||||
int operator()(types<Ret...> tr, types<Args...> ta, lua_State* L) {
|
int operator()(types<Ret...> tr, types<Args...> ta, Index<Start>, lua_State* L) {
|
||||||
decltype(auto) r = stack::call(tr, ta, L, 0, fx);
|
decltype(auto) r = stack::call(tr, ta, L, static_cast<int>(Start), fx);
|
||||||
int nargs = static_cast<int>(sizeof...(Args));
|
int nargs = static_cast<int>(sizeof...(Args));
|
||||||
lua_pop(L, nargs);
|
lua_pop(L, nargs);
|
||||||
int pushcount = push(L, std::forward<decltype(r)>(r));
|
int pushcount = push(L, std::forward<decltype(r)>(r));
|
||||||
|
@ -97,7 +96,7 @@ struct usertype_function : public usertype_function_core<Function, Tp> {
|
||||||
if(this->fx.item == nullptr) {
|
if(this->fx.item == nullptr) {
|
||||||
throw error("userdata for function call is null: are you using the wrong syntax? (use item:function/variable(...) syntax)");
|
throw error("userdata for function call is null: are you using the wrong syntax? (use item:function/variable(...) syntax)");
|
||||||
}
|
}
|
||||||
return static_cast<base_t&>(*this)(tuple_types<return_type>(), args_type(), L);
|
return static_cast<base_t&>(*this)(tuple_types<return_type>(), args_type(), Index<2>(), L);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual int operator()(lua_State* L) override {
|
virtual int operator()(lua_State* L) override {
|
||||||
|
@ -118,15 +117,15 @@ struct usertype_variable_function : public usertype_function_core<Function, Tp>
|
||||||
|
|
||||||
int prelude(lua_State* L) {
|
int prelude(lua_State* L) {
|
||||||
int argcount = lua_gettop(L);
|
int argcount = lua_gettop(L);
|
||||||
this->fx.item = stack::get<T*>(L, 1);
|
this->fx.item = stack::get<T*>(L, 1);
|
||||||
if(this->fx.item == nullptr) {
|
if(this->fx.item == nullptr) {
|
||||||
throw error("userdata for member variable is null");
|
throw error("userdata for member variable is null");
|
||||||
}
|
}
|
||||||
switch(argcount) {
|
switch(argcount) {
|
||||||
case 2:
|
case 2:
|
||||||
return static_cast<base_t&>(*this)(tuple_types<return_type>(), types<>(), L);
|
return static_cast<base_t&>(*this)(tuple_types<return_type>(), types<>(), Index<2>(), L);
|
||||||
case 3:
|
case 3:
|
||||||
return static_cast<base_t&>(*this)(tuple_types<void>(), args_type(), L);
|
return static_cast<base_t&>(*this)(tuple_types<void>(), args_type(), Index<3>(), L);
|
||||||
default:
|
default:
|
||||||
throw error("cannot get/set userdata member variable with inappropriate number of arguments");
|
throw error("cannot get/set userdata member variable with inappropriate number of arguments");
|
||||||
}
|
}
|
||||||
|
@ -150,15 +149,15 @@ struct usertype_indexing_function : base_function {
|
||||||
auto functionpair = functions.find(accessor);
|
auto functionpair = functions.find(accessor);
|
||||||
if (functionpair != functions.end()) {
|
if (functionpair != functions.end()) {
|
||||||
std::pair<bool, base_function*>& target = functionpair->second;
|
std::pair<bool, base_function*>& target = functionpair->second;
|
||||||
if (target.first) {
|
if (target.first) {
|
||||||
stack::push<upvalue>(L, target.second);
|
stack::push<upvalue>(L, target.second);
|
||||||
stack::push(L, c_closure(detail::usertype_call<0>, 1));
|
stack::push(L, c_closure(detail::usertype_call<0>, 1));
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
return (*target.second)(L);
|
return (*target.second)(L);
|
||||||
}
|
}
|
||||||
base_function& core = *original;
|
base_function& core = *original;
|
||||||
return core(L);
|
return core(L);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual int operator()(lua_State* L) override {
|
virtual int operator()(lua_State* L) override {
|
||||||
|
|
|
@ -43,7 +43,7 @@ public:
|
||||||
template<typename T>
|
template<typename T>
|
||||||
bool is() const {
|
bool is() const {
|
||||||
if (!reference::valid())
|
if (!reference::valid())
|
||||||
return false;
|
return false;
|
||||||
auto expected = type_of<T>();
|
auto expected = type_of<T>();
|
||||||
auto actual = get_type();
|
auto actual = get_type();
|
||||||
return (expected == actual) || (expected == type::poly);
|
return (expected == actual) || (expected == type::poly);
|
||||||
|
|
|
@ -25,15 +25,15 @@
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
namespace sol {
|
namespace sol {
|
||||||
template <typename... Functions>
|
template <typename... Functions>
|
||||||
struct overload_set : std::tuple<Functions...> {
|
struct overload_set : std::tuple<Functions...> {
|
||||||
using std::tuple<Functions...>::tuple;
|
using std::tuple<Functions...>::tuple;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
decltype(auto) overload(Args&&... args) {
|
decltype(auto) overload(Args&&... args) {
|
||||||
return overload_set<Args...>(std::forward<Args>(args)...);
|
return overload_set<Args...>(std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // SOL_OVERLOAD_HPP
|
#endif // SOL_OVERLOAD_HPP
|
84
sol/raii.hpp
Normal file
84
sol/raii.hpp
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
// The MIT License (MIT)
|
||||||
|
|
||||||
|
// Copyright (c) 2013-2016 Rapptz and contributors
|
||||||
|
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
// this software and associated documentation files (the "Software"), to deal in
|
||||||
|
// the Software without restriction, including without limitation the rights to
|
||||||
|
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
// the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
// subject to the following conditions:
|
||||||
|
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
#ifndef SOL_RAII_HPP
|
||||||
|
#define SOL_RAII_HPP
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include "traits.hpp"
|
||||||
|
|
||||||
|
namespace sol {
|
||||||
|
struct default_construct {
|
||||||
|
template<typename T, typename... Args>
|
||||||
|
void operator()(T&& obj, Args&&... args) const {
|
||||||
|
std::allocator<Unqualified<T>> alloc{};
|
||||||
|
alloc.construct(obj, std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct placement_construct {
|
||||||
|
T obj;
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
placement_construct( Args&&... args ) : obj(std::forward<Args>(args)...) {}
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
void operator()(Args&&... args) const {
|
||||||
|
default_construct{}(obj, std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
using constructors = sol::types<Args...>;
|
||||||
|
|
||||||
|
const auto default_constructor = constructors<types<>>{};
|
||||||
|
|
||||||
|
template <typename... Functions>
|
||||||
|
struct constructor_wrapper : std::tuple<Functions...> {
|
||||||
|
using std::tuple<Functions...>::tuple;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename... Functions>
|
||||||
|
constructor_wrapper<Functions...> constructor(Functions&&... functions) {
|
||||||
|
return constructor_wrapper<Functions...>(std::forward<Functions>(functions)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Function>
|
||||||
|
struct destructor_wrapper {
|
||||||
|
Function fx;
|
||||||
|
template <typename... Args>
|
||||||
|
destructor_wrapper(Args&&... args) : fx(std::forward<Args>(args)...) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct destructor_wrapper<void> {};
|
||||||
|
|
||||||
|
const destructor_wrapper<void> default_destructor{};
|
||||||
|
|
||||||
|
template <typename Fx>
|
||||||
|
inline destructor_wrapper<Fx> destructor(Fx&& fx) {
|
||||||
|
return destructor_wrapper<Fx>(std::forward<Fx>(fx));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // sol
|
||||||
|
|
||||||
|
#endif // SOL_RAII_HPP
|
119
sol/stack.hpp
119
sol/stack.hpp
|
@ -241,7 +241,7 @@ struct checker<T*, type::userdata, C> {
|
||||||
if (indextype == type::nil) {
|
if (indextype == type::nil) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return checker<T, type::userdata, C>{}.check(L, indextype, index, handler);
|
return checker<T, type::userdata, C>{}.check(L, indextype, index, handler);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -251,36 +251,36 @@ struct checker<T, type::userdata, C> {
|
||||||
static bool check (lua_State* L, type indextype, int index, const Handler& handler) {
|
static bool check (lua_State* L, type indextype, int index, const Handler& handler) {
|
||||||
if (indextype != type::userdata) {
|
if (indextype != type::userdata) {
|
||||||
handler(L, index, type::userdata, indextype);
|
handler(L, index, type::userdata, indextype);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (lua_getmetatable(L, index) == 0) {
|
if (lua_getmetatable(L, index) == 0) {
|
||||||
handler(L, index, type::userdata, indextype);
|
handler(L, index, type::userdata, indextype);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
luaL_getmetatable(L, &usertype_traits<T>::metatable[0]);
|
luaL_getmetatable(L, &usertype_traits<T>::metatable[0]);
|
||||||
const type expectedmetatabletype = get<type>(L);
|
const type expectedmetatabletype = get<type>(L);
|
||||||
if (expectedmetatabletype == type::nil) {
|
if (expectedmetatabletype == type::nil) {
|
||||||
lua_pop(L, 2);
|
lua_pop(L, 2);
|
||||||
handler(L, index, type::userdata, indextype);
|
handler(L, index, type::userdata, indextype);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
bool success = lua_rawequal(L, -1, -2) == 1;
|
bool success = lua_rawequal(L, -1, -2) == 1;
|
||||||
lua_pop(L, 2);
|
lua_pop(L, 2);
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Handler>
|
template <typename Handler>
|
||||||
static bool check (lua_State* L, int index, const Handler& handler) {
|
static bool check (lua_State* L, int index, const Handler& handler) {
|
||||||
const type indextype = type_of(L, index);
|
const type indextype = type_of(L, index);
|
||||||
return check(L, indextype, index, handler);
|
return check(L, indextype, index, handler);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename T, typename>
|
template<typename T, typename>
|
||||||
struct getter {
|
struct getter {
|
||||||
static T& get(lua_State* L, int index = -1) {
|
static T& get(lua_State* L, int index = -1) {
|
||||||
return getter<T&>{}.get(L, index);
|
return getter<T&>{}.get(L, index);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
|
@ -317,9 +317,9 @@ struct getter<T*> {
|
||||||
type t = type_of(L, index);
|
type t = type_of(L, index);
|
||||||
if (t == type::nil)
|
if (t == type::nil)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
void* udata = lua_touserdata(L, index);
|
void* udata = lua_touserdata(L, index);
|
||||||
T** obj = static_cast<T**>(udata);
|
T** obj = static_cast<T**>(udata);
|
||||||
return *obj;
|
return *obj;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -467,7 +467,7 @@ struct pusher<T, std::enable_if_t<std::is_floating_point<T>::value>> {
|
||||||
static int push(lua_State* L, const T& value) {
|
static int push(lua_State* L, const T& value) {
|
||||||
lua_pushnumber(L, value);
|
lua_pushnumber(L, value);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
|
@ -539,7 +539,7 @@ struct pusher<std::reference_wrapper<T>> {
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
struct pusher<bool> {
|
struct pusher<bool> {
|
||||||
static int push(lua_State* L, const bool& b) {
|
static int push(lua_State* L, bool b) {
|
||||||
lua_pushboolean(L, b);
|
lua_pushboolean(L, b);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -547,12 +547,20 @@ struct pusher<bool> {
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
struct pusher<nil_t> {
|
struct pusher<nil_t> {
|
||||||
static int push(lua_State* L, const nil_t&) {
|
static int push(lua_State* L, nil_t) {
|
||||||
lua_pushnil(L);
|
lua_pushnil(L);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template<>
|
||||||
|
struct pusher<std::remove_pointer_t<lua_CFunction>> {
|
||||||
|
static int push(lua_State* L, lua_CFunction func, int n = 0) {
|
||||||
|
lua_pushcclosure(L, func, n);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
struct pusher<lua_CFunction> {
|
struct pusher<lua_CFunction> {
|
||||||
static int push(lua_State* L, lua_CFunction func, int n = 0) {
|
static int push(lua_State* L, lua_CFunction func, int n = 0) {
|
||||||
|
@ -597,7 +605,7 @@ template<>
|
||||||
struct pusher<userdata> {
|
struct pusher<userdata> {
|
||||||
static int push(lua_State* L, userdata data) {
|
static int push(lua_State* L, userdata data) {
|
||||||
void** ud = static_cast<void**>(lua_newuserdata(L, sizeof(void*)));
|
void** ud = static_cast<void**>(lua_newuserdata(L, sizeof(void*)));
|
||||||
*ud = data.value;
|
*ud = data.value;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -648,10 +656,10 @@ struct field_getter<std::tuple<Args...>, b, C> {
|
||||||
template <std::size_t I0, std::size_t... I, typename Key>
|
template <std::size_t I0, std::size_t... I, typename Key>
|
||||||
void apply(std::index_sequence<I0, I...>, lua_State* L, Key&& key, int tableindex) {
|
void apply(std::index_sequence<I0, I...>, lua_State* L, Key&& key, int tableindex) {
|
||||||
get_field<b>(L, std::get<I0>(key), tableindex);
|
get_field<b>(L, std::get<I0>(key), tableindex);
|
||||||
detail::swallow{ (get_field(L, std::get<I>(key)), 0)... };
|
detail::swallow{ (get_field(L, std::get<I>(key)), 0)... };
|
||||||
reference saved(L, -1);
|
reference saved(L, -1);
|
||||||
lua_pop(L, static_cast<int>(sizeof...(I) + 1));
|
lua_pop(L, static_cast<int>(sizeof...(I) + 1));
|
||||||
saved.push();
|
saved.push();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Key>
|
template <typename Key>
|
||||||
|
@ -701,7 +709,7 @@ struct field_setter<T, true, std::enable_if_t<is_c_str<T>::value>> {
|
||||||
template <typename Key, typename Value>
|
template <typename Key, typename Value>
|
||||||
void set(lua_State* L, Key&& key, Value&& value, int = -2) {
|
void set(lua_State* L, Key&& key, Value&& value, int = -2) {
|
||||||
push(L, std::forward<Value>(value));
|
push(L, std::forward<Value>(value));
|
||||||
lua_setglobal(L, &key[0]);
|
lua_setglobal(L, &key[0]);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -759,10 +767,9 @@ template <bool b>
|
||||||
struct check_arguments {
|
struct check_arguments {
|
||||||
template <std::size_t I0, std::size_t... I, typename Arg0, typename... Args>
|
template <std::size_t I0, std::size_t... I, typename Arg0, typename... Args>
|
||||||
static bool check(types<Arg0, Args...>, std::index_sequence<I0, I...>, lua_State* L, int firstargument) {
|
static bool check(types<Arg0, Args...>, std::index_sequence<I0, I...>, lua_State* L, int firstargument) {
|
||||||
bool checks = true;
|
if (!stack::check<Arg0>(L, firstargument + I0))
|
||||||
if (!stack::check<Arg0>(L, firstargument + I0))
|
return false;
|
||||||
return false;
|
return check(types<Args...>(), std::index_sequence<I...>(), L, firstargument);
|
||||||
return check(types<Args...>(), std::index_sequence<I...>(), L, firstargument);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool check(types<>, std::index_sequence<>, lua_State*, int) {
|
static bool check(types<>, std::index_sequence<>, lua_State*, int) {
|
||||||
|
@ -780,24 +787,14 @@ struct check_arguments<false> {
|
||||||
|
|
||||||
template <bool checkargs = default_check_arguments, std::size_t... I, typename R, typename... Args, typename Fx, typename... FxArgs, typename = std::enable_if_t<!std::is_void<R>::value>>
|
template <bool checkargs = default_check_arguments, std::size_t... I, typename R, typename... Args, typename Fx, typename... FxArgs, typename = std::enable_if_t<!std::is_void<R>::value>>
|
||||||
inline R call(types<R>, types<Args...> ta, std::index_sequence<I...> tai, lua_State* L, int start, Fx&& fx, FxArgs&&... args) {
|
inline R call(types<R>, types<Args...> ta, std::index_sequence<I...> tai, lua_State* L, int start, Fx&& fx, FxArgs&&... args) {
|
||||||
const int stacksize = lua_gettop(L);
|
check_arguments<checkargs>{}.check(ta, tai, L, start);
|
||||||
const int firstargument = static_cast<int>(start + stacksize - std::max(sizeof...(Args)-1, static_cast<std::size_t>(0)));
|
return fx(std::forward<FxArgs>(args)..., stack::get<Args>(L, start + I)...);
|
||||||
|
|
||||||
check_arguments<checkargs>{}.check(ta, tai, L, firstargument);
|
|
||||||
|
|
||||||
return fx(std::forward<FxArgs>(args)..., stack::get<Args>(L, firstargument + I)...);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <bool checkargs = default_check_arguments, std::size_t... I, typename... Args, typename Fx, typename... FxArgs>
|
template <bool checkargs = default_check_arguments, std::size_t... I, typename... Args, typename Fx, typename... FxArgs>
|
||||||
inline void call(types<void>, types<Args...> ta, std::index_sequence<I...> tai, lua_State* L, int start, Fx&& fx, FxArgs&&... args) {
|
inline void call(types<void>, types<Args...> ta, std::index_sequence<I...> tai, lua_State* L, int start, Fx&& fx, FxArgs&&... args) {
|
||||||
const int stacksize = lua_gettop(L);
|
check_arguments<checkargs>{}.check(ta, tai, L, start);
|
||||||
const int firstargument = static_cast<int>(start + stacksize - std::max(sizeof...(Args)-1, static_cast<std::size_t>(0)));
|
fx(std::forward<FxArgs>(args)..., stack::get<Args>(L, start + I)...);
|
||||||
|
|
||||||
bool checks = check_arguments<checkargs>{}.check(ta, tai, L, firstargument);
|
|
||||||
if ( !checks )
|
|
||||||
throw error("Arguments not of the proper types for this function call");
|
|
||||||
|
|
||||||
fx(std::forward<FxArgs>(args)..., stack::get<Args>(L, firstargument + I)...);
|
|
||||||
}
|
}
|
||||||
} // stack_detail
|
} // stack_detail
|
||||||
|
|
||||||
|
@ -831,7 +828,12 @@ inline R call(types<R> tr, types<Args...> ta, lua_State* L, int start, Fx&& fx,
|
||||||
|
|
||||||
template <bool check_args = stack_detail::default_check_arguments, typename R, typename... Args, typename Fx, typename... FxArgs, typename = std::enable_if_t<!std::is_void<R>::value>>
|
template <bool check_args = stack_detail::default_check_arguments, typename R, typename... Args, typename Fx, typename... FxArgs, typename = std::enable_if_t<!std::is_void<R>::value>>
|
||||||
inline R call(types<R> tr, types<Args...> ta, lua_State* L, Fx&& fx, FxArgs&&... args) {
|
inline R call(types<R> tr, types<Args...> ta, lua_State* L, Fx&& fx, FxArgs&&... args) {
|
||||||
return call<check_args>(tr, ta, L, 0, std::forward<Fx>(fx), std::forward<FxArgs>(args)...);
|
return call<check_args>(tr, ta, L, 1, std::forward<Fx>(fx), std::forward<FxArgs>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <bool check_args = stack_detail::default_check_arguments, typename R, typename... Args, typename Fx, typename... FxArgs, typename = std::enable_if_t<!std::is_void<R>::value>>
|
||||||
|
inline R top_call(types<R> tr, types<Args...> ta, lua_State* L, Fx&& fx, FxArgs&&... args) {
|
||||||
|
return call<check_args>(tr, ta, L, static_cast<int>(lua_gettop(L) - sizeof...(Args)), std::forward<Fx>(fx), std::forward<FxArgs>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <bool check_args = stack_detail::default_check_arguments, typename... Args, typename Fx, typename... FxArgs>
|
template <bool check_args = stack_detail::default_check_arguments, typename... Args, typename Fx, typename... FxArgs>
|
||||||
|
@ -842,20 +844,25 @@ inline void call(types<void> tr, types<Args...> ta, lua_State* L, int start, Fx&
|
||||||
|
|
||||||
template <bool check_args = stack_detail::default_check_arguments, typename... Args, typename Fx, typename... FxArgs>
|
template <bool check_args = stack_detail::default_check_arguments, typename... Args, typename Fx, typename... FxArgs>
|
||||||
inline void call(types<void> tr, types<Args...> ta, lua_State* L, Fx&& fx, FxArgs&&... args) {
|
inline void call(types<void> tr, types<Args...> ta, lua_State* L, Fx&& fx, FxArgs&&... args) {
|
||||||
call<check_args>(tr, ta, L, 0, std::forward<Fx>(fx), std::forward<FxArgs>(args)...);
|
call<check_args>(tr, ta, L, 1, std::forward<Fx>(fx), std::forward<FxArgs>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<bool check_args = stack_detail::default_check_arguments, typename... Args, typename Fx>
|
template <bool check_args = stack_detail::default_check_arguments, typename... Args, typename Fx, typename... FxArgs>
|
||||||
inline int typed_call(types<void> tr, types<Args...> ta, Fx&& fx, lua_State* L, int start = 0) {
|
inline void top_call(types<void> tr, types<Args...> ta, lua_State* L, Fx&& fx, FxArgs&&... args) {
|
||||||
call<check_args>(tr, ta, L, start, fx);
|
call<check_args>(tr, ta, L, static_cast<int>(lua_gettop(L) - sizeof...(Args)), std::forward<Fx>(fx), std::forward<FxArgs>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<bool check_args = stack_detail::default_check_arguments, typename... Args, typename Fx, typename... FxArgs>
|
||||||
|
inline int typed_call(types<void> tr, types<Args...> ta, Fx&& fx, lua_State* L, int start = 1, FxArgs&&... fxargs) {
|
||||||
|
call<check_args>(tr, ta, L, start, fx, std::forward<FxArgs>(fxargs)...);
|
||||||
int nargs = static_cast<int>(sizeof...(Args));
|
int nargs = static_cast<int>(sizeof...(Args));
|
||||||
lua_pop(L, nargs);
|
lua_pop(L, nargs);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<bool check_args = stack_detail::default_check_arguments, typename... Ret, typename... Args, typename Fx>
|
template<bool check_args = stack_detail::default_check_arguments, typename Ret0, typename... Ret, typename... Args, typename Fx, typename... FxArgs, typename = std::enable_if_t<Not<std::is_void<Ret0>>::value>>
|
||||||
inline int typed_call(types<Ret...>, types<Args...> ta, Fx&& fx, lua_State* L, int start = 0) {
|
inline int typed_call(types<Ret0, Ret...>, types<Args...> ta, Fx&& fx, lua_State* L, int start = 1, FxArgs&&... fxargs) {
|
||||||
decltype(auto) r = call<check_args>(types<ReturnType<Ret...>>(), ta, L, start, fx);
|
decltype(auto) r = call<check_args>(types<return_type_t<Ret0, Ret...>>(), ta, L, start, fx, std::forward<FxArgs>(fxargs)...);
|
||||||
int nargs = static_cast<int>(sizeof...(Args));
|
int nargs = static_cast<int>(sizeof...(Args));
|
||||||
lua_pop(L, nargs);
|
lua_pop(L, nargs);
|
||||||
return push(L, std::forward<decltype(r)>(r));
|
return push(L, std::forward<decltype(r)>(r));
|
||||||
|
|
|
@ -177,9 +177,16 @@ public:
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Class, typename... CTor, typename... Args>
|
template<typename Class, typename... Args>
|
||||||
state_view& new_usertype(const std::string& name, Args&&... args) {
|
state_view& new_usertype(const std::string& name, Args&&... args) {
|
||||||
constructors<types<CTor...>> ctor{};
|
usertype<Class> utype(std::forward<Args>(args)...);
|
||||||
|
set_usertype(name, utype);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Class, typename CTor0, typename... CTor, typename... Args>
|
||||||
|
state_view& new_usertype(const std::string& name, Args&&... args) {
|
||||||
|
constructors<types<CTor0, CTor...>> ctor{};
|
||||||
return new_usertype<Class>(name, ctor, std::forward<Args>(args)...);
|
return new_usertype<Class>(name, ctor, std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -44,8 +44,8 @@ class table_core : public reference {
|
||||||
auto tuple_get( types<Ret...>, std::index_sequence<I...>, Keys&& keys ) const
|
auto tuple_get( types<Ret...>, std::index_sequence<I...>, Keys&& keys ) const
|
||||||
-> decltype(stack::pop<std::tuple<Ret...>>(nullptr)){
|
-> decltype(stack::pop<std::tuple<Ret...>>(nullptr)){
|
||||||
auto pp = stack::push_popper<is_global<decltype(std::get<I>(keys))...>::value>(*this);
|
auto pp = stack::push_popper<is_global<decltype(std::get<I>(keys))...>::value>(*this);
|
||||||
int tableindex = lua_gettop(lua_state());
|
int tableindex = lua_gettop(lua_state());
|
||||||
detail::swallow{ ( stack::get_field<top_level>(lua_state(), std::get<I>(keys), tableindex), 0)... };
|
detail::swallow{ ( stack::get_field<top_level>(lua_state(), std::get<I>(keys), tableindex), 0)... };
|
||||||
return stack::pop<std::tuple<Ret...>>( lua_state() );
|
return stack::pop<std::tuple<Ret...>>( lua_state() );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,7 +71,7 @@ class table_core : public reference {
|
||||||
template <bool global, typename T, typename Key, typename... Keys>
|
template <bool global, typename T, typename Key, typename... Keys>
|
||||||
decltype(auto) traverse_get_deep( Key&& key, Keys&&... keys ) const {
|
decltype(auto) traverse_get_deep( Key&& key, Keys&&... keys ) const {
|
||||||
stack::get_field<global>( lua_state( ), std::forward<Key>( key ) );
|
stack::get_field<global>( lua_state( ), std::forward<Key>( key ) );
|
||||||
return traverse_get_deep<false, T>(std::forward<Keys>(keys)...);
|
return traverse_get_deep<false, T>(std::forward<Keys>(keys)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <bool global, typename Key, typename Value>
|
template <bool global, typename Key, typename Value>
|
||||||
|
@ -105,16 +105,16 @@ public:
|
||||||
template <typename T, typename... Keys>
|
template <typename T, typename... Keys>
|
||||||
decltype(auto) traverse_get( Keys&&... keys ) const {
|
decltype(auto) traverse_get( Keys&&... keys ) const {
|
||||||
auto pp = stack::push_popper<is_global<Keys...>::value>(*this);
|
auto pp = stack::push_popper<is_global<Keys...>::value>(*this);
|
||||||
struct clean { lua_State* L; clean(lua_State* L) : L(L) {} ~clean() { lua_pop(L, static_cast<int>(sizeof...(Keys))); } } c(lua_state());
|
struct clean { lua_State* L; clean(lua_State* L) : L(L) {} ~clean() { lua_pop(L, static_cast<int>(sizeof...(Keys))); } } c(lua_state());
|
||||||
return traverse_get_deep<top_level, T>(std::forward<Keys>(keys)...);
|
return traverse_get_deep<top_level, T>(std::forward<Keys>(keys)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename... Keys>
|
template <typename... Keys>
|
||||||
table_core& traverse_set( Keys&&... keys ) {
|
table_core& traverse_set( Keys&&... keys ) {
|
||||||
auto pp = stack::push_popper<is_global<Keys...>::value>(*this);
|
auto pp = stack::push_popper<is_global<Keys...>::value>(*this);
|
||||||
traverse_set_deep<top_level>(std::forward<Keys>(keys)...);
|
traverse_set_deep<top_level>(std::forward<Keys>(keys)...);
|
||||||
lua_pop(lua_state(), static_cast<int>(sizeof...(Keys)-2));
|
lua_pop(lua_state(), static_cast<int>(sizeof...(Keys)-2));
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename... Args>
|
template<typename... Args>
|
||||||
|
|
|
@ -60,7 +60,7 @@ struct remove_member_pointer<R T::*> {
|
||||||
|
|
||||||
template<typename R, typename T>
|
template<typename R, typename T>
|
||||||
struct remove_member_pointer<R T::* const> {
|
struct remove_member_pointer<R T::* const> {
|
||||||
typedef R type;
|
typedef R type;
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
|
@ -125,6 +125,15 @@ struct find_in_pack_v : Bool<false> { };
|
||||||
template<typename V, typename Vs1, typename... Vs>
|
template<typename V, typename Vs1, typename... Vs>
|
||||||
struct find_in_pack_v<V, Vs1, Vs...> : Or<Bool<(V::value == Vs1::value)>, find_in_pack_v<V, Vs...>> { };
|
struct find_in_pack_v<V, Vs1, Vs...> : Or<Bool<(V::value == Vs1::value)>, find_in_pack_v<V, Vs...>> { };
|
||||||
|
|
||||||
|
template<std::size_t I, typename... Args>
|
||||||
|
struct at_in_pack {};
|
||||||
|
|
||||||
|
template<std::size_t I, typename... Args>
|
||||||
|
using at_in_pack_t = typename at_in_pack<I, Args...>::type;
|
||||||
|
|
||||||
|
template<std::size_t I, typename Arg, typename... Args>
|
||||||
|
struct at_in_pack<I, Arg, Args...> : std::conditional<I == 0, Arg, at_in_pack_t<I - 1, Args...>> {};
|
||||||
|
|
||||||
template<typename... Args>
|
template<typename... Args>
|
||||||
struct return_type {
|
struct return_type {
|
||||||
typedef std::tuple<Args...> type;
|
typedef std::tuple<Args...> type;
|
||||||
|
@ -143,12 +152,6 @@ struct return_type<> {
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
using return_type_t = typename return_type<Args...>::type;
|
using return_type_t = typename return_type<Args...>::type;
|
||||||
|
|
||||||
template <typename Empty, typename... Args>
|
|
||||||
using ReturnTypeOr = typename std::conditional<(sizeof...(Args) < 1), Empty, return_type_t<Args...>>::type;
|
|
||||||
|
|
||||||
template <typename... Args>
|
|
||||||
using ReturnType = ReturnTypeOr<void, Args...>;
|
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
template<typename T, bool isclass = std::is_class<Unqualified<T>>::value>
|
template<typename T, bool isclass = std::is_class<Unqualified<T>>::value>
|
||||||
|
@ -377,17 +380,17 @@ T& deref(const std::shared_ptr<T>& item) {
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
inline T* ptr(T& val) {
|
inline T* ptr(T& val) {
|
||||||
return std::addressof(val);
|
return std::addressof(val);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
inline T* ptr(std::reference_wrapper<T> val) {
|
inline T* ptr(std::reference_wrapper<T> val) {
|
||||||
return std::addressof(val.get());
|
return std::addressof(val.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
inline T* ptr(T* val) {
|
inline T* ptr(T* val) {
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
|
@ -30,15 +30,6 @@ template<typename... Args>
|
||||||
struct types { typedef std::index_sequence_for<Args...> indices; static constexpr std::size_t size() { return sizeof...(Args); } };
|
struct types { typedef std::index_sequence_for<Args...> indices; static constexpr std::size_t size() { return sizeof...(Args); } };
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
template<typename Arg>
|
|
||||||
struct chop_one : types<> {};
|
|
||||||
|
|
||||||
template<typename Arg0, typename Arg1, typename... Args>
|
|
||||||
struct chop_one<types<Arg0, Arg1, Args...>> : types<Arg1, Args...> {};
|
|
||||||
|
|
||||||
template<typename Arg, typename... Args>
|
|
||||||
struct chop_one<types<Arg, Args...>> : types<Args...> {};
|
|
||||||
|
|
||||||
template<typename... Args>
|
template<typename... Args>
|
||||||
struct tuple_types_ { typedef types<Args...> type; };
|
struct tuple_types_ { typedef types<Args...> type; };
|
||||||
|
|
||||||
|
@ -53,12 +44,15 @@ template<typename... Args>
|
||||||
using tuple_types = typename detail::tuple_types_<Args...>::type;
|
using tuple_types = typename detail::tuple_types_<Args...>::type;
|
||||||
|
|
||||||
template<typename Arg>
|
template<typename Arg>
|
||||||
struct remove_one_type : detail::chop_one<Arg> {};
|
struct pop_front_type;
|
||||||
|
|
||||||
|
template<typename Arg>
|
||||||
|
using pop_front_type_t = typename pop_front_type<Arg>::type;
|
||||||
|
|
||||||
|
template<typename Arg, typename... Args>
|
||||||
|
struct pop_front_type<types<Arg, Args...>> { typedef types<Args...> type; };
|
||||||
|
|
||||||
template<typename... Args>
|
|
||||||
using constructors = sol::types<Args...>;
|
|
||||||
|
|
||||||
const auto default_constructor = constructors<types<>>{};
|
|
||||||
|
|
||||||
} // sol
|
} // sol
|
||||||
|
|
||||||
|
|
|
@ -57,9 +57,9 @@ struct userdata {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct c_closure {
|
struct c_closure {
|
||||||
lua_CFunction c_function;
|
lua_CFunction c_function;
|
||||||
int upvalues;
|
int upvalues;
|
||||||
c_closure(lua_CFunction f, int upvalues = 0) : c_function(f), upvalues(upvalues) {}
|
c_closure(lua_CFunction f, int upvalues = 0) : c_function(f), upvalues(upvalues) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class call_syntax {
|
enum class call_syntax {
|
||||||
|
|
645
sol/usertype.hpp
645
sol/usertype.hpp
|
@ -25,7 +25,7 @@
|
||||||
#include "state.hpp"
|
#include "state.hpp"
|
||||||
#include "function_types.hpp"
|
#include "function_types.hpp"
|
||||||
#include "usertype_traits.hpp"
|
#include "usertype_traits.hpp"
|
||||||
#include "default_construct.hpp"
|
#include "raii.hpp"
|
||||||
#include "deprecate.hpp"
|
#include "deprecate.hpp"
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <array>
|
#include <array>
|
||||||
|
@ -33,334 +33,379 @@
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
namespace sol {
|
namespace sol {
|
||||||
const std::array<std::string, 2> meta_variable_names = { {
|
const std::array<std::string, 2> meta_variable_names = { {
|
||||||
"__index",
|
"__index",
|
||||||
"__newindex",
|
"__newindex",
|
||||||
} };
|
} };
|
||||||
|
|
||||||
const std::array<std::string, 21> meta_function_names = { {
|
const std::array<std::string, 21> meta_function_names = { {
|
||||||
"new",
|
"new",
|
||||||
"__index",
|
"__index",
|
||||||
"__newindex",
|
"__newindex",
|
||||||
"__mode",
|
"__mode",
|
||||||
"__call",
|
"__call",
|
||||||
"__metatable",
|
"__metatable",
|
||||||
"__tostring",
|
"__tostring",
|
||||||
"__len",
|
"__len",
|
||||||
"__unm",
|
"__unm",
|
||||||
"__add",
|
"__add",
|
||||||
"__sub",
|
"__sub",
|
||||||
"__mul",
|
"__mul",
|
||||||
"__div",
|
"__div",
|
||||||
"__mod",
|
"__mod",
|
||||||
"__pow",
|
"__pow",
|
||||||
"__concat",
|
"__concat",
|
||||||
"__eq",
|
"__eq",
|
||||||
"__lt",
|
"__lt",
|
||||||
"__le",
|
"__le",
|
||||||
"__gc",
|
"__gc",
|
||||||
"__call",
|
"__call",
|
||||||
} };
|
} };
|
||||||
|
|
||||||
enum class meta_function {
|
enum class meta_function {
|
||||||
construct,
|
construct,
|
||||||
index,
|
index,
|
||||||
new_index,
|
new_index,
|
||||||
mode,
|
mode,
|
||||||
call,
|
call,
|
||||||
metatable,
|
metatable,
|
||||||
to_string,
|
to_string,
|
||||||
length,
|
length,
|
||||||
unary_minus,
|
unary_minus,
|
||||||
addition,
|
addition,
|
||||||
subtraction,
|
subtraction,
|
||||||
multiplication,
|
multiplication,
|
||||||
division,
|
division,
|
||||||
modulus,
|
modulus,
|
||||||
power_of,
|
power_of,
|
||||||
involution = power_of,
|
involution = power_of,
|
||||||
concatenation,
|
concatenation,
|
||||||
equal_to,
|
equal_to,
|
||||||
less_than,
|
less_than,
|
||||||
less_than_or_equal_to,
|
less_than_or_equal_to,
|
||||||
garbage_collect,
|
garbage_collect,
|
||||||
call_function,
|
call_function,
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace usertype_detail {
|
namespace usertype_detail {
|
||||||
template<typename T, typename Funcs, typename FuncTable, typename MetaFuncTable>
|
struct add_destructor_tag {};
|
||||||
inline void push_metatable(lua_State* L, Funcs&& funcs, FuncTable&&, MetaFuncTable&& metafunctable) {
|
struct check_destructor_tag {};
|
||||||
luaL_newmetatable(L, &usertype_traits<T>::metatable[0]);
|
struct verified_tag {} const verified {};
|
||||||
int metatableindex = lua_gettop(L);
|
|
||||||
if (funcs.size() < 1 && metafunctable.size() < 2) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Metamethods directly on the metatable itself
|
|
||||||
int metaup = stack::stack_detail::push_upvalues(L, funcs);
|
|
||||||
if (std::is_pointer<T>::value) {
|
|
||||||
// Insert nullptr before new/gc methods for pointer types;
|
|
||||||
// prevents calling new/GC on pointer-based tables.
|
|
||||||
luaL_Reg& oldref = metafunctable[metafunctable.size() - 3];
|
|
||||||
luaL_Reg old = oldref;
|
|
||||||
oldref = { nullptr, nullptr };
|
|
||||||
luaL_setfuncs(L, metafunctable.data(), metaup);
|
|
||||||
oldref = old;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
luaL_setfuncs(L, metafunctable.data(), metaup);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T, typename Functions>
|
template <typename T>
|
||||||
inline void set_global_deleter(lua_State* L, lua_CFunction cleanup, Functions&& metafunctions) {
|
struct is_constructor : std::false_type {};
|
||||||
// Automatic deleter table -- stays alive until lua VM dies
|
|
||||||
// even if the user calls collectgarbage(), weirdly enough
|
|
||||||
lua_createtable(L, 0, 0);
|
|
||||||
lua_createtable(L, 0, 1);
|
|
||||||
int up = stack::stack_detail::push_upvalues<true>(L, metafunctions);
|
|
||||||
lua_pushcclosure(L, cleanup, up);
|
|
||||||
lua_setfield(L, -2, "__gc");
|
|
||||||
lua_setmetatable(L, -2);
|
|
||||||
// gctable name by default has ♻ part of it
|
|
||||||
lua_setglobal(L, &usertype_traits<T>::gc_table[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T, typename... Args>
|
template <typename... Args>
|
||||||
static void do_constructor(lua_State* L, T* obj, call_syntax syntax, int, types<Args...>) {
|
struct is_constructor<constructors<Args...>> : std::true_type {};
|
||||||
default_construct fx{};
|
|
||||||
stack::call(types<void>(), types<Args...>(), L, -1 + static_cast<int>(syntax), fx, obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
template <typename... Args>
|
||||||
static void match_constructor(lua_State*, T*, call_syntax, int) {
|
struct is_constructor<constructor_wrapper<Args...>> : std::true_type {};
|
||||||
throw error("No matching constructor for the arguments provided");
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T, typename ...CArgs, typename... Args>
|
template <typename... Args>
|
||||||
static void match_constructor(lua_State* L, T* obj, call_syntax syntax, int argcount, types<CArgs...> t, Args&&... args) {
|
using has_constructor = Or<is_constructor<Unqualified<Args>>...>;
|
||||||
if (argcount == sizeof...(CArgs)) {
|
|
||||||
do_constructor<T>(L, obj, syntax, argcount, t);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
match_constructor<T>(L, obj, syntax, argcount, std::forward<Args>(args)...);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
template <typename T>
|
||||||
class usertype {
|
struct is_destructor : std::false_type {};
|
||||||
private:
|
|
||||||
typedef std::map<std::string, std::pair<bool, base_function*>> function_map_t;
|
|
||||||
std::vector<std::string> functionnames;
|
|
||||||
std::vector<std::unique_ptr<base_function>> functions;
|
|
||||||
std::vector<luaL_Reg> functiontable;
|
|
||||||
std::vector<luaL_Reg> metafunctiontable;
|
|
||||||
base_function* indexfunc;
|
|
||||||
base_function* newindexfunc;
|
|
||||||
function_map_t indexwrapper, newindexwrapper;
|
|
||||||
base_function* constructfunc;
|
|
||||||
base_function* destructfunc;
|
|
||||||
lua_CFunction functiongc;
|
|
||||||
|
|
||||||
template<typename... TTypes>
|
template <typename Fx>
|
||||||
struct constructor {
|
struct is_destructor<destructor_wrapper<Fx>> : std::true_type {};
|
||||||
static int construct(lua_State* L) {
|
|
||||||
const auto& meta = usertype_traits<T>::metatable;
|
|
||||||
call_syntax syntax = stack::get_call_syntax(L, meta);
|
|
||||||
int argcount = lua_gettop(L);
|
|
||||||
|
|
||||||
T** pointerpointer = reinterpret_cast<T**>(lua_newuserdata(L, sizeof(T*) + sizeof(T)));
|
template <typename... Args>
|
||||||
T*& referencepointer = *pointerpointer;
|
using has_destructor = Or<is_destructor<Unqualified<Args>>...>;
|
||||||
T* obj = reinterpret_cast<T*>(pointerpointer + 1);
|
|
||||||
referencepointer = obj;
|
|
||||||
int userdataindex = lua_gettop(L);
|
|
||||||
usertype_detail::match_constructor(L, obj, syntax, argcount - static_cast<int>(syntax), identity_t<TTypes>()...);
|
|
||||||
|
|
||||||
if (luaL_newmetatable(L, &meta[0]) == 1) {
|
template<typename T, bool refmeta, typename Funcs, typename FuncTable, typename MetaFuncTable>
|
||||||
lua_pop(L, 1);
|
inline void push_metatable(lua_State* L, bool needsindexfunction, Funcs&& funcs, FuncTable&& functable, MetaFuncTable&& metafunctable) {
|
||||||
std::string err = "unable to get usertype metatable for ";
|
static const auto& gcname = meta_function_names[static_cast<int>(meta_function::garbage_collect)];
|
||||||
err += meta;
|
luaL_newmetatable(L, &usertype_traits<T>::metatable[0]);
|
||||||
throw error(err);
|
int metatableindex = lua_gettop(L);
|
||||||
}
|
if (funcs.size() < 1 && metafunctable.size() < 2) {
|
||||||
lua_setmetatable(L, userdataindex);
|
return;
|
||||||
|
}
|
||||||
|
// Metamethods directly on the metatable itself
|
||||||
|
int metaup = stack::stack_detail::push_upvalues(L, funcs);
|
||||||
|
if (refmeta && gcname == metafunctable[metafunctable.size()-2].name) {
|
||||||
|
// We can just "clip" out the __gc function,
|
||||||
|
// which we always put as the last entry in the meta function table.
|
||||||
|
luaL_Reg& target = metafunctable[metafunctable.size() - 2];
|
||||||
|
luaL_Reg old = target;
|
||||||
|
target = { nullptr, nullptr };
|
||||||
|
luaL_setfuncs(L, metafunctable.data(), metaup);
|
||||||
|
target = old;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Otherwise, just slap it in there.
|
||||||
|
luaL_setfuncs(L, metafunctable.data(), metaup);
|
||||||
|
}
|
||||||
|
if (needsindexfunction) {
|
||||||
|
// We don't need to do anything more
|
||||||
|
// since we've already bound the __index field using
|
||||||
|
// setfuncs above...
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Otherwise, we use quick, fast table indexing for methods
|
||||||
|
// gives us performance boost in calling them
|
||||||
|
lua_createtable(L, 0, functable.size());
|
||||||
|
int up = stack::stack_detail::push_upvalues(L, funcs);
|
||||||
|
luaL_setfuncs(L, functable.data(), up);
|
||||||
|
lua_setfield(L, metatableindex, "__index");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
return 1;
|
template <typename T, typename Functions>
|
||||||
}
|
inline void set_global_deleter(lua_State* L, lua_CFunction cleanup, Functions&& functions) {
|
||||||
};
|
// Automatic deleter table -- stays alive until lua VM dies
|
||||||
|
// even if the user calls collectgarbage(), weirdly enough
|
||||||
|
lua_createtable(L, 0, 0); // global table that sits at toplevel
|
||||||
|
lua_createtable(L, 0, 1); // metatable for the global table
|
||||||
|
int up = stack::stack_detail::push_upvalues<true>(L, functions);
|
||||||
|
stack::set_field(L, "__gc", c_closure(cleanup, up));
|
||||||
|
lua_setmetatable(L, -2);
|
||||||
|
// gctable name by default has ♻ part of it
|
||||||
|
lua_setglobal(L, &usertype_traits<T>::gc_table[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static int destruct(lua_State* L) {
|
template<typename T>
|
||||||
userdata udata = stack::get<userdata>(L, 1);
|
class usertype {
|
||||||
// The first sizeof(T*) bytes are the reference: the rest is
|
private:
|
||||||
// the actual data itself (if there is a reference at all)
|
typedef std::map<std::string, std::pair<bool, base_function*>> function_map_t;
|
||||||
T** pobj = reinterpret_cast<T**>(udata.value);
|
std::vector<std::string> functionnames;
|
||||||
T*& obj = *pobj;
|
std::vector<std::unique_ptr<base_function>> functions;
|
||||||
std::allocator<T> alloc{};
|
std::vector<luaL_Reg> functiontable;
|
||||||
alloc.destroy(obj);
|
std::vector<luaL_Reg> metafunctiontable;
|
||||||
return 0;
|
base_function* indexfunc;
|
||||||
}
|
base_function* newindexfunc;
|
||||||
|
function_map_t indexwrapper, newindexwrapper;
|
||||||
|
lua_CFunction constructfunc;
|
||||||
|
const char* destructfuncname;
|
||||||
|
lua_CFunction destructfunc;
|
||||||
|
lua_CFunction functiongcfunc;
|
||||||
|
bool needsindexfunction;
|
||||||
|
|
||||||
template<typename... Functions>
|
template<typename... Functions>
|
||||||
std::unique_ptr<base_function> make_function(const std::string&, overload_set<Functions...> func) {
|
std::unique_ptr<base_function> make_function(const std::string&, overload_set<Functions...> func) {
|
||||||
return std::make_unique<usertype_overloaded_function<T, Functions...>>(func);
|
return std::make_unique<usertype_overloaded_function<T, Functions...>>(std::move(func));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Arg, typename... Args, typename Ret>
|
template<typename... Functions>
|
||||||
std::unique_ptr<base_function> make_function(const std::string&, Ret(*func)(Arg, Args...)) {
|
std::unique_ptr<base_function> make_function(const std::string&, constructor_wrapper<Functions...> func) {
|
||||||
typedef Unqualified<std::remove_pointer_t<Arg>> Argu;
|
return std::make_unique<usertype_constructor_function<T, Functions...>>(std::move(func));
|
||||||
static_assert(std::is_base_of<Argu, T>::value, "Any non-member-function must have a first argument which is covariant with the desired userdata type.");
|
}
|
||||||
typedef std::decay_t<decltype(func)> function_type;
|
|
||||||
return std::make_unique<usertype_function<function_type, T>>(func);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Base, typename Ret>
|
template<typename Arg, typename... Args, typename Ret>
|
||||||
std::unique_ptr<base_function> make_variable_function(std::true_type, const std::string&, Ret Base::* func) {
|
std::unique_ptr<base_function> make_function(const std::string&, Ret(*func)(Arg, Args...)) {
|
||||||
static_assert(std::is_base_of<Base, T>::value, "Any registered function must be part of the class");
|
typedef Unqualified<std::remove_pointer_t<Arg>> Argu;
|
||||||
typedef std::decay_t<decltype(func)> function_type;
|
static_assert(std::is_base_of<Argu, T>::value, "Any non-member-function must have a first argument which is covariant with the desired userdata type.");
|
||||||
return std::make_unique<usertype_variable_function<function_type, T>>(func);
|
typedef std::decay_t<decltype(func)> function_type;
|
||||||
}
|
return std::make_unique<usertype_function<function_type, T>>(func);
|
||||||
|
}
|
||||||
|
|
||||||
template<typename Base, typename Ret>
|
template<typename Base, typename Ret>
|
||||||
std::unique_ptr<base_function> make_variable_function(std::false_type, const std::string&, Ret Base::* func) {
|
std::unique_ptr<base_function> make_variable_function(std::true_type, const std::string&, Ret Base::* func) {
|
||||||
static_assert(std::is_base_of<Base, T>::value, "Any registered function must be part of the class");
|
static_assert(std::is_base_of<Base, T>::value, "Any registered function must be part of the class");
|
||||||
typedef std::decay_t<decltype(func)> function_type;
|
typedef std::decay_t<decltype(func)> function_type;
|
||||||
return std::make_unique<usertype_function<function_type, T>>(func);
|
return std::make_unique<usertype_variable_function<function_type, T>>(func);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Base, typename Ret>
|
template<typename Base, typename Ret>
|
||||||
std::unique_ptr<base_function> make_function(const std::string& name, Ret Base::* func) {
|
std::unique_ptr<base_function> make_variable_function(std::false_type, const std::string&, Ret Base::* func) {
|
||||||
typedef std::decay_t<decltype(func)> function_type;
|
static_assert(std::is_base_of<Base, T>::value, "Any registered function must be part of the class");
|
||||||
return make_variable_function(std::is_member_object_pointer<function_type>(), name, func);
|
typedef std::decay_t<decltype(func)> function_type;
|
||||||
}
|
return std::make_unique<usertype_function<function_type, T>>(func);
|
||||||
|
}
|
||||||
|
|
||||||
template<typename Fx>
|
template<typename Base, typename Ret>
|
||||||
std::unique_ptr<base_function> make_function(const std::string&, Fx&& func) {
|
std::unique_ptr<base_function> make_function(const std::string& name, Ret Base::* func) {
|
||||||
typedef Unqualified<Fx> Fxu;
|
typedef std::decay_t<decltype(func)> function_type;
|
||||||
typedef std::tuple_element_t<0, typename function_traits<Fxu>::args_tuple_type> Arg0;
|
return make_variable_function(std::is_member_object_pointer<function_type>(), name, func);
|
||||||
typedef Unqualified<std::remove_pointer_t<Arg0>> Argu;
|
}
|
||||||
static_assert(std::is_base_of<Argu, T>::value, "Any non-member-function must have a first argument which is covariant with the desired usertype.");
|
|
||||||
typedef std::decay_t<Fxu> function_type;
|
|
||||||
return std::make_unique<usertype_function<function_type, T>>(func);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<std::size_t N, typename Fx>
|
template<typename Fx>
|
||||||
void build_function(std::string funcname, Fx&& func) {
|
std::unique_ptr<base_function> make_function(const std::string&, Fx&& func) {
|
||||||
typedef std::is_member_object_pointer<Unqualified<Fx>> is_variable;
|
typedef Unqualified<Fx> Fxu;
|
||||||
typedef std::decay_t<Fx> function_type;
|
typedef std::tuple_element_t<0, typename function_traits<Fxu>::args_tuple_type> Arg0;
|
||||||
functionnames.push_back(std::move(funcname));
|
typedef Unqualified<std::remove_pointer_t<Arg0>> Argu;
|
||||||
std::string& name = functionnames.back();
|
static_assert(std::is_base_of<Argu, T>::value, "Any non-member-function must have a first argument which is covariant with the desired usertype.");
|
||||||
auto baseptr = make_function(name, std::forward<Fx>(func));
|
typedef std::decay_t<Fxu> function_type;
|
||||||
functions.emplace_back(std::move(baseptr));
|
return std::make_unique<usertype_function<function_type, T>>(func);
|
||||||
if (is_variable::value) {
|
}
|
||||||
indexwrapper.insert({ name, { false, functions.back().get() } });
|
|
||||||
newindexwrapper.insert({ name, { false, functions.back().get() } });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
auto metamethodfind = std::find(meta_function_names.begin(), meta_function_names.end(), name);
|
|
||||||
if (metamethodfind != meta_function_names.end()) {
|
|
||||||
meta_function metafunction = static_cast<meta_function>(metamethodfind - meta_function_names.begin());
|
|
||||||
switch (metafunction) {
|
|
||||||
case meta_function::index:
|
|
||||||
indexfunc = functions.back().get();
|
|
||||||
break;
|
|
||||||
case meta_function::new_index:
|
|
||||||
newindexfunc = functions.back().get();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
metafunctiontable.push_back({ name.c_str(), detail::usertype_call<N> });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
indexwrapper.insert({ name, { true, functions.back().get() } });
|
|
||||||
functiontable.push_back({ name.c_str(), detail::usertype_call<N> });
|
|
||||||
}
|
|
||||||
|
|
||||||
template<std::size_t N, typename Fx, typename... Args>
|
template<std::size_t N, typename... Args>
|
||||||
void build_function_tables(std::string funcname, Fx&& func, Args&&... args) {
|
void build_function(std::string funcname, constructors<Args...>) {
|
||||||
build_function<N>(std::move(funcname), std::forward<Fx>(func));
|
functionnames.push_back(std::move(funcname));
|
||||||
build_function_tables<N + 1>(std::forward<Args>(args)...);
|
std::string& name = functionnames.back();
|
||||||
}
|
// Insert bubble to keep with compile-time argument count (simpler and cheaper to do)
|
||||||
|
functions.push_back(nullptr);
|
||||||
|
constructfunc = construct<T, Args...>;
|
||||||
|
metafunctiontable.push_back({ functionnames.back().c_str(), constructfunc });
|
||||||
|
}
|
||||||
|
|
||||||
template<std::size_t N, typename Fx, typename... Args>
|
template<std::size_t N>
|
||||||
void build_function_tables(meta_function metafunc, Fx&& func, Args&&... args) {
|
void build_function(std::string funcname, destructor_wrapper<void>) {
|
||||||
std::size_t idx = static_cast<std::size_t>(metafunc);
|
auto metamethodfind = std::find(meta_function_names.begin(), meta_function_names.end(), funcname);
|
||||||
const std::string& funcname = meta_function_names[idx];
|
if (metamethodfind == meta_function_names.end())
|
||||||
build_function_tables<N>(funcname, std::forward<Fx>(func), std::forward<Args>(args)...);
|
throw error("cannot set destructor to anything but the metamethod \"__gc\"");
|
||||||
}
|
meta_function metafunction = static_cast<meta_function>(metamethodfind - meta_function_names.begin());
|
||||||
|
if (metafunction != meta_function::garbage_collect)
|
||||||
|
throw error("cannot set destructor to anything but the metamethod \"__gc\"");
|
||||||
|
|
||||||
|
functionnames.push_back(std::move(funcname));
|
||||||
|
std::string& name = functionnames.back();
|
||||||
|
destructfunc = destruct<T>;
|
||||||
|
destructfuncname = name.c_str();
|
||||||
|
// Insert bubble to stay with the compile-time count
|
||||||
|
functions.push_back(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
template<std::size_t N>
|
template<std::size_t N, typename Fx>
|
||||||
void build_function_tables() {
|
void build_function(std::string funcname, destructor_wrapper<Fx> dx) {
|
||||||
int variableend = 0;
|
auto metamethodfind = std::find(meta_function_names.begin(), meta_function_names.end(), funcname);
|
||||||
if (!indexwrapper.empty()) {
|
if (metamethodfind == meta_function_names.end())
|
||||||
functions.push_back(std::make_unique<usertype_indexing_function>("__index", indexfunc, std::move(indexwrapper)));
|
throw error("cannot set destructor to anything but the metamethod \"__gc\"");
|
||||||
metafunctiontable.push_back({ "__index", detail::usertype_call<N> });
|
meta_function metafunction = static_cast<meta_function>(metamethodfind - meta_function_names.begin());
|
||||||
++variableend;
|
if (metafunction != meta_function::garbage_collect)
|
||||||
}
|
throw error("cannot set destructor to anything but the metamethod \"__gc\"");
|
||||||
if (!newindexwrapper.empty()) {
|
|
||||||
functions.push_back(std::make_unique<usertype_indexing_function>("__newindex", newindexfunc, std::move(newindexwrapper)));
|
|
||||||
metafunctiontable.push_back({ "__newindex", indexwrapper.empty() ? detail::usertype_call<N> : detail::usertype_call<N + 1> });
|
|
||||||
++variableend;
|
|
||||||
}
|
|
||||||
switch (variableend) {
|
|
||||||
case 2:
|
|
||||||
functiongc = detail::usertype_gc<N + 2>;
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
functiongc = detail::usertype_gc<N + 1>;
|
|
||||||
break;
|
|
||||||
case 0:
|
|
||||||
functiongc = detail::usertype_gc<N + 0>;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
functionnames.push_back(std::move(funcname));
|
||||||
template<typename... Args>
|
std::string& name = functionnames.back();
|
||||||
usertype(Args&&... args) : usertype(default_constructor, std::forward<Args>(args)...) {}
|
auto baseptr = make_function(name, std::move(dx.fx));
|
||||||
|
functions.emplace_back(std::move(baseptr));
|
||||||
|
destructfunc = detail::usertype_call<N>;
|
||||||
|
destructfuncname = name.c_str();
|
||||||
|
}
|
||||||
|
|
||||||
template<typename... Args, typename... CArgs>
|
template<std::size_t N, typename Fx>
|
||||||
usertype(constructors<CArgs...>, Args&&... args)
|
void build_function(std::string funcname, Fx&& func) {
|
||||||
: indexfunc(nullptr), newindexfunc(nullptr), constructfunc(nullptr), destructfunc(nullptr), functiongc(nullptr) {
|
typedef std::is_member_object_pointer<Unqualified<Fx>> is_variable;
|
||||||
functionnames.reserve(sizeof...(args)+3);
|
functionnames.push_back(std::move(funcname));
|
||||||
functiontable.reserve(sizeof...(args)+3);
|
std::string& name = functionnames.back();
|
||||||
metafunctiontable.reserve(sizeof...(args)+3);
|
auto baseptr = make_function(name, std::forward<Fx>(func));
|
||||||
|
functions.emplace_back(std::move(baseptr));
|
||||||
|
auto metamethodfind = std::find(meta_function_names.begin(), meta_function_names.end(), name);
|
||||||
|
if (metamethodfind != meta_function_names.end()) {
|
||||||
|
metafunctiontable.push_back({ name.c_str(), detail::usertype_call<N> });
|
||||||
|
meta_function metafunction = static_cast<meta_function>(metamethodfind - meta_function_names.begin());
|
||||||
|
switch (metafunction) {
|
||||||
|
case meta_function::garbage_collect:
|
||||||
|
destructfuncname = name.c_str();
|
||||||
|
destructfunc = detail::usertype_call<N>;
|
||||||
|
return;
|
||||||
|
case meta_function::index:
|
||||||
|
indexfunc = functions.back().get();
|
||||||
|
needsindexfunction = true;
|
||||||
|
break;
|
||||||
|
case meta_function::new_index:
|
||||||
|
newindexfunc = functions.back().get();
|
||||||
|
break;
|
||||||
|
case meta_function::construct:
|
||||||
|
constructfunc = detail::usertype_call<N>;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (is_variable::value) {
|
||||||
|
needsindexfunction = true;
|
||||||
|
indexwrapper.insert({ name, { false, functions.back().get() } });
|
||||||
|
newindexwrapper.insert({ name, { false, functions.back().get() } });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
indexwrapper.insert({ name, { true, functions.back().get() } });
|
||||||
|
functiontable.push_back({ name.c_str(), detail::usertype_call<N> });
|
||||||
|
}
|
||||||
|
|
||||||
build_function_tables<0>(std::forward<Args>(args)...);
|
template<std::size_t N, typename Fx, typename... Args>
|
||||||
|
void build_function_tables(std::string funcname, Fx&& func, Args&&... args) {
|
||||||
functionnames.push_back("new");
|
build_function<N>(std::move(funcname), std::forward<Fx>(func));
|
||||||
metafunctiontable.push_back({ functionnames.back().c_str(), &constructor<CArgs...>::construct });
|
build_function_tables<N + 1>(std::forward<Args>(args)...);
|
||||||
functionnames.push_back("__gc");
|
}
|
||||||
metafunctiontable.push_back({ functionnames.back().c_str(), destruct });
|
|
||||||
|
|
||||||
// ptr_functions does not participate in garbage collection/new,
|
template<std::size_t N, typename Fx, typename... Args>
|
||||||
// as all pointered types are considered
|
void build_function_tables(meta_function metafunc, Fx&& func, Args&&... args) {
|
||||||
// to be references. This makes returns of
|
std::size_t idx = static_cast<std::size_t>(metafunc);
|
||||||
// `std::vector<int>&` and `std::vector<int>*` work
|
const std::string& funcname = meta_function_names[idx];
|
||||||
metafunctiontable.push_back({ nullptr, nullptr });
|
build_function_tables<N>(funcname, std::forward<Fx>(func), std::forward<Args>(args)...);
|
||||||
functiontable.push_back({ nullptr, nullptr });
|
}
|
||||||
}
|
|
||||||
|
|
||||||
int push(lua_State* L) {
|
template<std::size_t N>
|
||||||
// push pointer tables first,
|
void build_function_tables() {
|
||||||
usertype_detail::push_metatable<T*>(L, functions, functiontable, metafunctiontable);
|
int variableend = 0;
|
||||||
lua_pop(L, 1);
|
if (!indexwrapper.empty()) {
|
||||||
// but leave the regular T table on last
|
functions.push_back(std::make_unique<usertype_indexing_function>("__index", indexfunc, std::move(indexwrapper)));
|
||||||
// so it can be linked to a type for usage with `.new(...)` or `:new(...)`
|
metafunctiontable.push_back({ "__index", detail::usertype_call<N> });
|
||||||
usertype_detail::push_metatable<T>(L, functions, functiontable, metafunctiontable);
|
++variableend;
|
||||||
// Make sure to drop a table in the global namespace to properly destroy the pushed functions
|
}
|
||||||
// at some later point in life
|
if (!newindexwrapper.empty()) {
|
||||||
usertype_detail::set_global_deleter<T>(L, functiongc, functions);
|
functions.push_back(std::make_unique<usertype_indexing_function>("__newindex", newindexfunc, std::move(newindexwrapper)));
|
||||||
return 1;
|
metafunctiontable.push_back({ "__newindex", indexwrapper.empty() ? detail::usertype_call<N> : detail::usertype_call<N + 1> });
|
||||||
}
|
++variableend;
|
||||||
};
|
}
|
||||||
|
if (destructfunc != nullptr) {
|
||||||
|
metafunctiontable.push_back({ destructfuncname, destructfunc });
|
||||||
|
}
|
||||||
|
switch (variableend) {
|
||||||
|
case 2:
|
||||||
|
functiongcfunc = detail::usertype_gc<N + 2>;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
functiongcfunc = detail::usertype_gc<N + 1>;
|
||||||
|
break;
|
||||||
|
case 0:
|
||||||
|
functiongcfunc = detail::usertype_gc<N + 0>;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
namespace stack {
|
template<typename... Args>
|
||||||
template<typename T>
|
usertype(usertype_detail::verified_tag, Args&&... args) : indexfunc(nullptr), newindexfunc(nullptr), constructfunc(nullptr), destructfunc(nullptr), functiongcfunc(nullptr), needsindexfunction(false) {
|
||||||
struct pusher<usertype<T>> {
|
functionnames.reserve(sizeof...(args)+3);
|
||||||
static int push(lua_State* L, usertype<T>& user) {
|
functiontable.reserve(sizeof...(args)+3);
|
||||||
return user.push(L);
|
metafunctiontable.reserve(sizeof...(args)+3);
|
||||||
}
|
|
||||||
};
|
build_function_tables<0>(std::forward<Args>(args)...);
|
||||||
} // stack
|
metafunctiontable.push_back({ nullptr, nullptr });
|
||||||
|
functiontable.push_back({ nullptr, nullptr });
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
usertype(usertype_detail::add_destructor_tag, Args&&... args) : usertype(usertype_detail::verified, "__gc", default_destructor, std::forward<Args>(args)...) {}
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
usertype(usertype_detail::check_destructor_tag, Args&&... args) : usertype(If<And<std::is_destructible<T>, Not<usertype_detail::has_destructor<Args...>>>, usertype_detail::add_destructor_tag, usertype_detail::verified_tag>(), std::forward<Args>(args)...) {}
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
usertype(Args&&... args) : usertype(If<And<std::is_default_constructible<T>, Not<usertype_detail::has_constructor<Args...>>>, decltype(default_constructor), usertype_detail::check_destructor_tag>(), std::forward<Args>(args)...) {}
|
||||||
|
|
||||||
|
template<typename... Args, typename... CArgs>
|
||||||
|
usertype(constructors<CArgs...> constructorlist, Args&&... args) : usertype(usertype_detail::verified, "new", constructorlist, "__gc", default_destructor, std::forward<Args>(args)...) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int push(lua_State* L) {
|
||||||
|
// push pointer tables first,
|
||||||
|
usertype_detail::push_metatable<T*, true>(L, needsindexfunction, functions, functiontable, metafunctiontable);
|
||||||
|
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(...)`
|
||||||
|
usertype_detail::push_metatable<T, false>(L, needsindexfunction, functions, functiontable, metafunctiontable);
|
||||||
|
// Make sure to drop a table in the global namespace to properly destroy the pushed functions
|
||||||
|
// at some later point in life
|
||||||
|
usertype_detail::set_global_deleter<T>(L, functiongcfunc, functions);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace stack {
|
||||||
|
template<typename T>
|
||||||
|
struct pusher<usertype<T>> {
|
||||||
|
static int push(lua_State* L, usertype<T>& user) {
|
||||||
|
return user.push(L);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // stack
|
||||||
} // sol
|
} // sol
|
||||||
|
|
||||||
#endif // SOL_USERTYPE_HPP
|
#endif // SOL_USERTYPE_HPP
|
||||||
|
|
149
tests.cpp
149
tests.cpp
|
@ -224,49 +224,84 @@ struct giver {
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct factory_test {
|
||||||
|
private:
|
||||||
|
factory_test() { a = true_a; }
|
||||||
|
~factory_test() { a = 0; }
|
||||||
|
public:
|
||||||
|
static int num_saved;
|
||||||
|
static int num_killed;
|
||||||
|
|
||||||
|
struct deleter {
|
||||||
|
void operator()(factory_test* f) {
|
||||||
|
f->~factory_test();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static const int true_a = 156;
|
||||||
|
int a;
|
||||||
|
|
||||||
|
static std::unique_ptr<factory_test, deleter> make() {
|
||||||
|
return std::unique_ptr<factory_test, deleter>( new factory_test(), deleter());
|
||||||
|
}
|
||||||
|
|
||||||
|
static void save(factory_test& f) {
|
||||||
|
new(&f)factory_test();
|
||||||
|
++num_saved;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void kill(factory_test& f) {
|
||||||
|
f.~factory_test();
|
||||||
|
++num_killed;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
int factory_test::num_saved = 0;
|
||||||
|
int factory_test::num_killed = 0;
|
||||||
|
|
||||||
TEST_CASE("table/traversal", "ensure that we can chain requests and tunnel down into a value if we desire") {
|
TEST_CASE("table/traversal", "ensure that we can chain requests and tunnel down into a value if we desire") {
|
||||||
|
|
||||||
sol::state lua;
|
sol::state lua;
|
||||||
int begintop = 0, endtop = 0;
|
int begintop = 0, endtop = 0;
|
||||||
|
|
||||||
lua.script("t1 = {t2 = {t3 = 24}};");
|
lua.script("t1 = {t2 = {t3 = 24}};");
|
||||||
{
|
{
|
||||||
stack_guard g(lua.lua_state(), begintop, endtop);
|
stack_guard g(lua.lua_state(), begintop, endtop);
|
||||||
int traversex24 = lua.traverse_get<int>("t1", "t2", "t3");
|
int traversex24 = lua.traverse_get<int>("t1", "t2", "t3");
|
||||||
REQUIRE(traversex24 == 24);
|
REQUIRE(traversex24 == 24);
|
||||||
} REQUIRE(begintop == endtop);
|
} REQUIRE(begintop == endtop);
|
||||||
|
|
||||||
{
|
{
|
||||||
stack_guard g(lua.lua_state(), begintop, endtop);
|
stack_guard g(lua.lua_state(), begintop, endtop);
|
||||||
int x24 = lua["t1"]["t2"]["t3"];
|
int x24 = lua["t1"]["t2"]["t3"];
|
||||||
REQUIRE(x24 == 24);
|
REQUIRE(x24 == 24);
|
||||||
} REQUIRE(begintop == endtop);
|
} REQUIRE(begintop == endtop);
|
||||||
|
|
||||||
{
|
{
|
||||||
stack_guard g(lua.lua_state(), begintop, endtop);
|
stack_guard g(lua.lua_state(), begintop, endtop);
|
||||||
lua["t1"]["t2"]["t3"] = 64;
|
lua["t1"]["t2"]["t3"] = 64;
|
||||||
int traversex64 = lua.traverse_get<int>("t1", "t2", "t3");
|
int traversex64 = lua.traverse_get<int>("t1", "t2", "t3");
|
||||||
REQUIRE(traversex64 == 64);
|
REQUIRE(traversex64 == 64);
|
||||||
} REQUIRE(begintop == endtop);
|
} REQUIRE(begintop == endtop);
|
||||||
|
|
||||||
{
|
{
|
||||||
stack_guard g(lua.lua_state(), begintop, endtop);
|
stack_guard g(lua.lua_state(), begintop, endtop);
|
||||||
int x64 = lua["t1"]["t2"]["t3"];
|
int x64 = lua["t1"]["t2"]["t3"];
|
||||||
REQUIRE(x64 == 64);
|
REQUIRE(x64 == 64);
|
||||||
} REQUIRE(begintop == endtop);
|
} REQUIRE(begintop == endtop);
|
||||||
|
|
||||||
{
|
{
|
||||||
stack_guard g(lua.lua_state(), begintop, endtop);
|
stack_guard g(lua.lua_state(), begintop, endtop);
|
||||||
lua.traverse_set("t1", "t2", "t3", 13);
|
lua.traverse_set("t1", "t2", "t3", 13);
|
||||||
int traversex13 = lua.traverse_get<int>("t1", "t2", "t3");
|
int traversex13 = lua.traverse_get<int>("t1", "t2", "t3");
|
||||||
REQUIRE(traversex13 == 13);
|
REQUIRE(traversex13 == 13);
|
||||||
} REQUIRE(begintop == endtop);
|
} REQUIRE(begintop == endtop);
|
||||||
|
|
||||||
{
|
{
|
||||||
stack_guard g(lua.lua_state(), begintop, endtop);
|
stack_guard g(lua.lua_state(), begintop, endtop);
|
||||||
int x13 = lua["t1"]["t2"]["t3"];
|
int x13 = lua["t1"]["t2"]["t3"];
|
||||||
REQUIRE(x13 == 13);
|
REQUIRE(x13 == 13);
|
||||||
} REQUIRE(begintop == endtop);
|
} REQUIRE(begintop == endtop);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("simple/set", "Check if the set works properly.") {
|
TEST_CASE("simple/set", "Check if the set works properly.") {
|
||||||
|
@ -475,15 +510,15 @@ TEST_CASE("advanced/call_referenced_obj", "A C++ object is passed by pointer/ref
|
||||||
|
|
||||||
int x = 0;
|
int x = 0;
|
||||||
auto objx = [&](int new_x) {
|
auto objx = [&](int new_x) {
|
||||||
x = new_x;
|
x = new_x;
|
||||||
return 0;
|
return 0;
|
||||||
};
|
};
|
||||||
lua.set_function("set_x", std::ref(objx));
|
lua.set_function("set_x", std::ref(objx));
|
||||||
|
|
||||||
int y = 0;
|
int y = 0;
|
||||||
auto objy = [&](int new_y) {
|
auto objy = [&](int new_y) {
|
||||||
y = new_y;
|
y = new_y;
|
||||||
return std::tuple<int, int>(0, 0);
|
return std::tuple<int, int>(0, 0);
|
||||||
};
|
};
|
||||||
lua.set_function("set_y", &decltype(objy)::operator(), std::ref(objy));
|
lua.set_function("set_y", &decltype(objy)::operator(), std::ref(objy));
|
||||||
|
|
||||||
|
@ -715,8 +750,8 @@ TEST_CASE("tables/operator[]", "Check if operator[] retrieval and setting works
|
||||||
auto assert1 = [](const sol::table& t) {
|
auto assert1 = [](const sol::table& t) {
|
||||||
std::string a = t["foo"];
|
std::string a = t["foo"];
|
||||||
double b = t["bar"];
|
double b = t["bar"];
|
||||||
REQUIRE(a == "goodbye");
|
REQUIRE(a == "goodbye");
|
||||||
REQUIRE(b == 20.4);
|
REQUIRE(b == 20.4);
|
||||||
};
|
};
|
||||||
|
|
||||||
REQUIRE_NOTHROW(assert1(lua.global()));
|
REQUIRE_NOTHROW(assert1(lua.global()));
|
||||||
|
@ -1307,6 +1342,36 @@ func(1,2,3)
|
||||||
REQUIRE_THROWS(lua.script("func(1,2,'meow')"));
|
REQUIRE_THROWS(lua.script("func(1,2,'meow')"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("usertype/private constructible", "Check to make sure special snowflake types from Enterprise thingamahjongs work properly.") {
|
||||||
|
int numsaved = factory_test::num_saved;
|
||||||
|
int numkilled = factory_test::num_killed;
|
||||||
|
{
|
||||||
|
sol::state lua;
|
||||||
|
lua.open_libraries(sol::lib::base);
|
||||||
|
|
||||||
|
lua.new_usertype<factory_test>("factory_test",
|
||||||
|
"new", sol::constructor(factory_test::save),
|
||||||
|
"__gc", sol::destructor(factory_test::kill),
|
||||||
|
"a", &factory_test::a
|
||||||
|
);
|
||||||
|
|
||||||
|
std::unique_ptr<factory_test, factory_test::deleter> f = factory_test::make();
|
||||||
|
lua.set("true_a", factory_test::true_a, "f", f.get());
|
||||||
|
REQUIRE_NOTHROW(lua.script(R"(
|
||||||
|
assert(f.a == true_a)
|
||||||
|
)"));
|
||||||
|
|
||||||
|
REQUIRE_NOTHROW(lua.script(R"(
|
||||||
|
local fresh_f = factory_test:new()
|
||||||
|
assert(fresh_f.a == true_a)
|
||||||
|
)"));
|
||||||
|
}
|
||||||
|
int expectednumsaved = numsaved + 1;
|
||||||
|
int expectednumkilled = numkilled + 1;
|
||||||
|
REQUIRE(expectednumsaved == factory_test::num_saved);
|
||||||
|
REQUIRE(expectednumkilled == factory_test::num_killed);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE("usertype/overloading", "Check if overloading works properly for usertypes") {
|
TEST_CASE("usertype/overloading", "Check if overloading works properly for usertypes") {
|
||||||
struct woof {
|
struct woof {
|
||||||
int var;
|
int var;
|
||||||
|
@ -1364,7 +1429,7 @@ TEST_CASE("issues/stack-overflow", "make sure various operations repeated don't
|
||||||
REQUIRE_NOTHROW(
|
REQUIRE_NOTHROW(
|
||||||
for (int i = 0; i < 1000000; ++i) {
|
for (int i = 0; i < 1000000; ++i) {
|
||||||
int result = t[0];
|
int result = t[0];
|
||||||
t.size();
|
t.size();
|
||||||
if (result != expected)
|
if (result != expected)
|
||||||
throw std::logic_error("RIP");
|
throw std::logic_error("RIP");
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user