mirror of
https://github.com/ThePhD/sol2.git
synced 2024-03-22 13:10:44 +08:00
This is hard....
This commit is contained in:
parent
8a7f4e6586
commit
0ee92c0142
|
@ -24,6 +24,7 @@
|
|||
|
||||
#include "function_types_core.hpp"
|
||||
#include "function_types_static.hpp"
|
||||
#include "function_types_allocator.hpp"
|
||||
#include "function_types_member.hpp"
|
||||
#include "function_types_usertype.hpp"
|
||||
#include "function_types_overload.hpp"
|
||||
|
|
30
sol/function_types_allocator.hpp
Normal file
30
sol/function_types_allocator.hpp
Normal file
|
@ -0,0 +1,30 @@
|
|||
// 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_FUNCTION_TYPES_ALLOCATOR_HPP
|
||||
#define SOL_FUNCTION_TYPES_ALLOCATOR_HPP
|
||||
|
||||
#include "stack.hpp"
|
||||
|
||||
namespace sol {
|
||||
} // sol
|
||||
|
||||
#endif // SOL_FUNCTION_TYPES_ALLOCATOR_HPP
|
|
@ -24,7 +24,6 @@
|
|||
|
||||
#include "stack.hpp"
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace sol {
|
||||
namespace detail {
|
||||
|
@ -187,17 +186,6 @@ struct base_function {
|
|||
return r;
|
||||
}
|
||||
|
||||
static int ref_base_call(lua_State* L, void* inheritancedata) {
|
||||
if(inheritancedata == nullptr) {
|
||||
throw error("call from Lua to C++ function has null data");
|
||||
}
|
||||
|
||||
base_function* pfx = static_cast<base_function*>(inheritancedata);
|
||||
base_function& fx = *pfx;
|
||||
int r = fx(L, detail::ref_call);
|
||||
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");
|
||||
|
@ -223,11 +211,7 @@ struct base_function {
|
|||
struct usertype {
|
||||
static int call(lua_State* L) {
|
||||
// Zero-based template parameter, but upvalues start at 1
|
||||
return ref_base_call(L, stack::get<upvalue>(L, I + 1));
|
||||
}
|
||||
|
||||
static int ref_call(lua_State* L) {
|
||||
return ref_base_call(L, stack::get<upvalue>(L, I + 1));
|
||||
return base_call(L, stack::get<upvalue>(L, I + 1));
|
||||
}
|
||||
|
||||
template <std::size_t limit>
|
||||
|
@ -257,10 +241,6 @@ struct base_function {
|
|||
throw error("failure to call specialized wrapped C++ function from Lua");
|
||||
}
|
||||
|
||||
virtual int operator()(lua_State*, detail::ref_call_t) {
|
||||
throw error("failure to call reference specialized wrapped C++ function from Lua");
|
||||
}
|
||||
|
||||
virtual ~base_function() {}
|
||||
};
|
||||
|
||||
|
|
|
@ -54,10 +54,6 @@ struct functor_function : public base_function {
|
|||
virtual int operator()(lua_State* L) override {
|
||||
return (*this)(types<return_type>(), args_type(), L);
|
||||
}
|
||||
|
||||
virtual int operator()(lua_State* L, detail::ref_call_t) override {
|
||||
return (*this)(types<return_type>(), args_type(), L);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Function, typename T>
|
||||
|
@ -85,10 +81,6 @@ struct member_function : public base_function {
|
|||
virtual int operator()(lua_State* L) override {
|
||||
return stack::typed_call(tuple_types<return_type>(), args_types(), fx, L);
|
||||
}
|
||||
|
||||
virtual int operator()(lua_State* L, detail::ref_call_t) override {
|
||||
return (*this)(L);
|
||||
}
|
||||
};
|
||||
} // sol
|
||||
|
||||
|
|
|
@ -76,10 +76,6 @@ struct overloaded_function : base_function {
|
|||
virtual int operator()(lua_State* L) override {
|
||||
return match_arity(L);
|
||||
}
|
||||
|
||||
virtual int operator()(lua_State* L, detail::ref_call_t) override {
|
||||
return match_arity(L);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename... Functions>
|
||||
|
@ -133,80 +129,6 @@ struct usertype_overloaded_function : base_function {
|
|||
virtual int operator()(lua_State* L) override {
|
||||
return match_arity(L);
|
||||
}
|
||||
|
||||
virtual int operator()(lua_State* L, detail::ref_call_t) override {
|
||||
return match_arity(L);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename... Functions, typename T>
|
||||
struct usertype_indexing_function<overload_set<Functions...>, T> : base_function {
|
||||
typedef std::tuple<std::pair<int, detail::functor<T, Functions>>...> overloads_t;
|
||||
overloads_t overloads;
|
||||
std::string name;
|
||||
std::unordered_map<std::string, std::pair<std::unique_ptr<base_function>, bool>> functions;
|
||||
|
||||
usertype_indexing_function(std::string name, overload_set<Functions...> set)
|
||||
: usertype_indexing_function(std::index_sequence_for<Functions...>(), std::move(name), set) {}
|
||||
|
||||
template <std::size_t... In>
|
||||
usertype_indexing_function(std::index_sequence<In...>, std::string name, overload_set<Functions...> set)
|
||||
: usertype_indexing_function(std::move(name), std::get<In>(set)...) {}
|
||||
|
||||
usertype_indexing_function(std::string name, Functions... fxs)
|
||||
: overloads({static_cast<int>(function_traits<Functions>::arity), fxs}...), name(std::move(name)) {}
|
||||
|
||||
int match_arity(std::index_sequence<>, lua_State*, std::ptrdiff_t) {
|
||||
throw error("no matching function call takes this number of arguments");
|
||||
}
|
||||
|
||||
template <std::size_t I, std::size_t... In>
|
||||
int match_arity(std::index_sequence<I, In...>, lua_State* L, std::ptrdiff_t x ) {
|
||||
// TODO:
|
||||
// propogate changes from above down here too when they get figured out
|
||||
auto& package = std::get<I>(overloads);
|
||||
auto arity = package.first;
|
||||
if (arity != x) {
|
||||
return match_arity(std::index_sequence<In...>(), L, x);
|
||||
}
|
||||
auto& func = package.second;
|
||||
typedef Unqualified<decltype(func)> fx_t;
|
||||
typedef tuple_types<typename fx_t::return_type> return_type;
|
||||
typedef typename fx_t::args_type args_type;
|
||||
typedef typename args_type::indices args_indices;
|
||||
if (!detail::check_types(args_type(), args_indices(), L, 2)) {
|
||||
return match_arity(std::index_sequence<In...>(), L, x);
|
||||
}
|
||||
func.item = ptr(stack::get<T>(L, 1));
|
||||
return stack::typed_call<false>(return_type(), args_type(), func, L);
|
||||
}
|
||||
|
||||
int match_arity(lua_State* L) {
|
||||
std::ptrdiff_t x = lua_gettop(L) - 1;
|
||||
return match_arity(std::make_index_sequence<std::tuple_size<overloads_t>::value>(), L, x);
|
||||
}
|
||||
|
||||
int prelude(lua_State* L) {
|
||||
std::string accessor = stack::get<std::string>(L, 1 - lua_gettop(L));
|
||||
auto function = functions.find(accessor);
|
||||
if(function != functions.end()) {
|
||||
if(function->second.second) {
|
||||
stack::push<upvalue>(L, function->second.first.get());
|
||||
stack::push(L, &base_function::usertype<0>::ref_call, 1);
|
||||
return 1;
|
||||
}
|
||||
return (*function->second.first)(L);
|
||||
}
|
||||
return match_arity(L);
|
||||
}
|
||||
|
||||
virtual int operator()(lua_State* L) override {
|
||||
return prelude(L);
|
||||
}
|
||||
|
||||
virtual int operator()(lua_State* L, detail::ref_call_t) override {
|
||||
return prelude(L);
|
||||
}
|
||||
};
|
||||
} // sol
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
|
||||
#include "overload.hpp"
|
||||
#include "function_types_core.hpp"
|
||||
#include <map>
|
||||
|
||||
namespace sol {
|
||||
template<typename Function, typename Tp>
|
||||
|
@ -102,10 +103,6 @@ struct usertype_function : public usertype_function_core<Function, Tp> {
|
|||
virtual int operator()(lua_State* L) override {
|
||||
return prelude(L);
|
||||
}
|
||||
|
||||
virtual int operator()(lua_State* L, detail::ref_call_t) override {
|
||||
return prelude(L);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Function, typename Tp>
|
||||
|
@ -124,7 +121,6 @@ struct usertype_variable_function : public usertype_function_core<Function, Tp>
|
|||
if(this->fx.item == nullptr) {
|
||||
throw error("userdata for member variable is null");
|
||||
}
|
||||
|
||||
int argcount = lua_gettop(L);
|
||||
switch(argcount) {
|
||||
case 2:
|
||||
|
@ -134,57 +130,34 @@ struct usertype_variable_function : public usertype_function_core<Function, Tp>
|
|||
default:
|
||||
throw error("cannot get/set userdata member variable with inappropriate number of arguments");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
virtual int operator()(lua_State* L) override {
|
||||
return prelude(L);
|
||||
}
|
||||
|
||||
virtual int operator()(lua_State* L, detail::ref_call_t) override {
|
||||
return prelude(L);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Function, typename Tp>
|
||||
struct usertype_indexing_function : public usertype_function_core<Function, Tp> {
|
||||
typedef usertype_function_core<Function, Tp> base_t;
|
||||
typedef std::remove_pointer_t<Tp> T;
|
||||
typedef typename base_t::traits_type traits_type;
|
||||
typedef typename base_t::args_type args_type;
|
||||
typedef typename base_t::return_type return_type;
|
||||
|
||||
struct usertype_indexing_function : base_function {
|
||||
std::string name;
|
||||
std::unordered_map<std::string, std::pair<std::unique_ptr<base_function>, bool>> functions;
|
||||
base_function* original;
|
||||
std::map<std::string, base_function*> functions;
|
||||
|
||||
template<typename... Args>
|
||||
usertype_indexing_function(std::string name, Args&&... args): base_t(std::forward<Args>(args)...), name(std::move(name)) {}
|
||||
usertype_indexing_function(std::string name, base_function* original, Args&&... args): name(std::move(name)), original(original), functions(std::forward<Args>(args)...) {}
|
||||
|
||||
int prelude(lua_State* L) {
|
||||
std::string accessor = stack::get<std::string>(L, 1 - lua_gettop(L));
|
||||
const char* accessor = stack::get<const char*>(L, 1 - lua_gettop(L));
|
||||
auto function = functions.find(accessor);
|
||||
if(function != functions.end()) {
|
||||
if(function->second.second) {
|
||||
stack::push<upvalue>(L, function->second.first.get());
|
||||
stack::push(L, &base_function::usertype<0>::ref_call, 1);
|
||||
return 1;
|
||||
}
|
||||
return (*function->second.first)(L);
|
||||
if (function != functions.end()) {
|
||||
return (*function->second)(L);
|
||||
}
|
||||
if (!this->fx.check()) {
|
||||
throw error("invalid indexing \"" + accessor + "\" on type: " + name);
|
||||
}
|
||||
this->fx.item = ptr(stack::get<T>(L, 1));
|
||||
return static_cast<base_t&>(*this)(tuple_types<return_type>(), args_type(), L);
|
||||
base_function& core = *original;
|
||||
return core(L);
|
||||
}
|
||||
|
||||
virtual int operator()(lua_State* L) override {
|
||||
return prelude(L);
|
||||
}
|
||||
|
||||
virtual int operator()(lua_State* L, detail::ref_call_t) override {
|
||||
return prelude(L);
|
||||
}
|
||||
};
|
||||
} // sol
|
||||
|
||||
|
|
|
@ -42,32 +42,21 @@ public:
|
|||
|
||||
template<typename T>
|
||||
bool is() const {
|
||||
if (!reference::valid())
|
||||
return false;
|
||||
auto expected = type_of<T>();
|
||||
auto actual = get_type();
|
||||
return (expected == actual) || (expected == type::poly);
|
||||
}
|
||||
|
||||
bool valid() const {
|
||||
if (!reference::valid())
|
||||
return false;
|
||||
return !this->is<nil_t>();
|
||||
}
|
||||
|
||||
operator const char* () const {
|
||||
return this->as<const char*>();
|
||||
}
|
||||
|
||||
template<typename T, EnableIf<Not<std::is_same<Unqualified<T>, const char*>>, Not<std::is_same<Unqualified<T>, char>>, Not<std::is_same<Unqualified<T>, std::string>>, Not<std::is_same<Unqualified<T>, std::initializer_list<char>>>> = 0>
|
||||
operator T () const {
|
||||
return this->as<T>();
|
||||
}
|
||||
|
||||
template<typename... Ret, typename... Args>
|
||||
decltype(auto) call( Args&&... args ) {
|
||||
return this->as<function>()(types<Ret...>(), std::forward<Args>( args )...);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
function_result operator()( Args&&... args ) {
|
||||
return this->as<function>()(std::forward<Args>( args )...);
|
||||
explicit operator bool() {
|
||||
return valid();
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -100,6 +100,10 @@ public:
|
|||
return 1;
|
||||
}
|
||||
|
||||
void pop(int n = 1) const noexcept {
|
||||
lua_pop(lua_state( ), n);
|
||||
}
|
||||
|
||||
int get_index() const {
|
||||
return ref;
|
||||
}
|
||||
|
|
|
@ -124,6 +124,21 @@ true;
|
|||
false;
|
||||
#endif
|
||||
|
||||
template<bool releasemem = false, typename TCont>
|
||||
static int push_upvalues(lua_State* L, TCont&& cont) {
|
||||
int n = 0;
|
||||
for(auto& c : cont) {
|
||||
if(releasemem) {
|
||||
stack::push<upvalue>(L, c.release());
|
||||
}
|
||||
else {
|
||||
stack::push<upvalue>(L, c.get());
|
||||
}
|
||||
++n;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
struct userdata_pusher {
|
||||
template <typename Key, typename... Args>
|
||||
|
@ -140,7 +155,7 @@ struct userdata_pusher {
|
|||
referencereference = allocationtarget;
|
||||
std::allocator<T> alloc{};
|
||||
alloc.construct(allocationtarget, std::forward<Args>(args)...);
|
||||
luaL_getmetatable(L, std::addressof(metatablekey[0]));
|
||||
luaL_getmetatable(L, &metatablekey[0]);
|
||||
lua_setmetatable(L, -2);
|
||||
}
|
||||
};
|
||||
|
@ -152,7 +167,7 @@ struct userdata_pusher<T*> {
|
|||
T** pdatum = static_cast<T**>(lua_newuserdata(L, sizeof(T*)));
|
||||
std::allocator<T*> alloc{};
|
||||
alloc.construct(pdatum, std::forward<Args>(args)...);
|
||||
luaL_getmetatable(L, std::addressof(metatablekey[0]));
|
||||
luaL_getmetatable(L, &metatablekey[0]);
|
||||
lua_setmetatable(L, -2);
|
||||
}
|
||||
};
|
||||
|
@ -361,6 +376,20 @@ struct getter<nil_t> {
|
|||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct getter<lua_CFunction> {
|
||||
static lua_CFunction get(lua_State* L, int index = -1) {
|
||||
return lua_tocfunction(L, index);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct getter<c_closure> {
|
||||
static c_closure get(lua_State* L, int index = -1) {
|
||||
return c_closure(lua_tocfunction(L, index), -1);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct getter<userdata> {
|
||||
static userdata get(lua_State* L, int index = -1) {
|
||||
|
@ -529,6 +558,14 @@ struct pusher<lua_CFunction> {
|
|||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct pusher<c_closure> {
|
||||
static int push(lua_State* L, c_closure closure) {
|
||||
lua_pushcclosure(L, closure.c_function, closure.upvalues);
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct pusher<void*> {
|
||||
static int push(lua_State* L, void* userdata) {
|
||||
|
@ -636,6 +673,16 @@ struct field_getter<T, false, std::enable_if_t<is_c_str<T>::value>> {
|
|||
}
|
||||
};
|
||||
|
||||
#if SOL_LUA_VERSION >= 503
|
||||
template <typename T>
|
||||
struct field_getter<T, false, std::enable_if_t<std::is_integral<T>::value>> {
|
||||
template <typename Key>
|
||||
void get(lua_State* L, Key&& key, int tableindex = -1) {
|
||||
lua_geti(L, tableindex, static_cast<lua_Integer>(key));
|
||||
}
|
||||
};
|
||||
#endif // Lua 5.3.x
|
||||
|
||||
template <typename T, bool, typename>
|
||||
struct field_setter {
|
||||
template <typename Key, typename Value>
|
||||
|
@ -664,6 +711,17 @@ struct field_setter<T, false, std::enable_if_t<is_c_str<T>::value>> {
|
|||
}
|
||||
};
|
||||
|
||||
#if SOL_LUA_VERSION >= 503
|
||||
template <typename T>
|
||||
struct field_setter<T, false, std::enable_if_t<std::is_integral<T>::value>> {
|
||||
template <typename Key, typename Value>
|
||||
void set(lua_State* L, Key&& key, Value&& value, int tableindex = -2) {
|
||||
push(L, std::forward<Value>(value));
|
||||
lua_seti(L, tableindex, static_cast<lua_Integer>(key));
|
||||
}
|
||||
};
|
||||
#endif // Lua 5.3.x
|
||||
|
||||
namespace stack_detail {
|
||||
template<typename T>
|
||||
inline int push_as_upvalues(lua_State* L, T& item) {
|
||||
|
@ -675,7 +733,7 @@ inline int push_as_upvalues(lua_State* L, T& item) {
|
|||
typedef std::array<void*, data_t_count> data_t;
|
||||
|
||||
data_t data{{}};
|
||||
std::memcpy(std::addressof(data[0]), std::addressof(item), itemsize);
|
||||
std::memcpy(&data[0], std::addressof(item), itemsize);
|
||||
int pushcount = 0;
|
||||
for(auto&& v : data) {
|
||||
pushcount += push(L, upvalue(v));
|
||||
|
@ -699,9 +757,9 @@ struct check_arguments {
|
|||
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) {
|
||||
bool checks = true;
|
||||
stack::check<Arg0>(L, firstargument + I0);
|
||||
(void)detail::swallow{(checks &= stack::check<Args>(L, firstargument + I))...};
|
||||
return checks;
|
||||
if (!stack::check<Arg0>(L, firstargument + I0))
|
||||
return false;
|
||||
return check(types<Args...>(), std::index_sequence<I...>(), L, firstargument);
|
||||
}
|
||||
|
||||
static bool check(types<>, std::index_sequence<>, lua_State*, int) {
|
||||
|
|
|
@ -165,10 +165,6 @@ public:
|
|||
return proxy<table_core, T>( *this, std::forward<T>( key ) );
|
||||
}
|
||||
|
||||
void pop( int n = 1 ) const noexcept {
|
||||
lua_pop( lua_state( ), n );
|
||||
}
|
||||
|
||||
template<typename... Args, typename R, typename Key>
|
||||
table_core& set_function( Key&& key, R fun_ptr( Args... ) ) {
|
||||
set_resolved_function( std::forward<Key>( key ), fun_ptr );
|
||||
|
|
|
@ -56,6 +56,12 @@ struct userdata {
|
|||
operator void*() const { return value; }
|
||||
};
|
||||
|
||||
struct c_closure {
|
||||
lua_CFunction c_function;
|
||||
int upvalues;
|
||||
c_closure(lua_CFunction f, int upvalues = 0) : c_function(f), upvalues(upvalues) {}
|
||||
};
|
||||
|
||||
enum class call_syntax {
|
||||
dot = 0,
|
||||
colon = 1
|
||||
|
@ -158,6 +164,9 @@ struct lua_type_of<object> : std::integral_constant<type, type::poly> {};
|
|||
template <>
|
||||
struct lua_type_of<light_userdata> : std::integral_constant<type, type::lightuserdata> {};
|
||||
|
||||
template <>
|
||||
struct lua_type_of<lua_CFunction> : std::integral_constant<type, type::function> {};
|
||||
|
||||
template <>
|
||||
struct lua_type_of<function> : std::integral_constant<type, type::function> {};
|
||||
|
||||
|
@ -173,21 +182,8 @@ struct lua_type_of<T*> : std::integral_constant<type, type::userdata> {};
|
|||
template <typename T>
|
||||
struct lua_type_of<T, std::enable_if_t<std::is_arithmetic<T>::value>> : std::integral_constant<type, type::number> {};
|
||||
|
||||
template<typename T>
|
||||
inline type type_of() {
|
||||
return lua_type_of<Unqualified<T>>::value;
|
||||
}
|
||||
|
||||
inline type type_of(lua_State* L, int index) {
|
||||
return static_cast<type>(lua_type(L, index));
|
||||
}
|
||||
|
||||
// All enumerations are given and taken from lua
|
||||
// as numbers as well
|
||||
template <typename T>
|
||||
struct lua_type_of<T, std::enable_if_t<std::is_enum<T>::value>> : std::integral_constant<type, type::number> {
|
||||
|
||||
};
|
||||
struct lua_type_of<T, std::enable_if_t<std::is_enum<T>::value>> : std::integral_constant<type, type::number> {};
|
||||
|
||||
template <typename T>
|
||||
struct is_lua_primitive : std::integral_constant<bool, type::userdata != lua_type_of<Unqualified<T>>::value> { };
|
||||
|
@ -200,6 +196,15 @@ struct is_proxy_primitive<std::reference_wrapper<T>> : std::true_type { };
|
|||
|
||||
template <typename... Args>
|
||||
struct is_proxy_primitive<std::tuple<Args...>> : std::true_type { };
|
||||
|
||||
template<typename T>
|
||||
inline type type_of() {
|
||||
return lua_type_of<Unqualified<T>>::value;
|
||||
}
|
||||
|
||||
inline type type_of(lua_State* L, int index) {
|
||||
return static_cast<type>(lua_type(L, index));
|
||||
}
|
||||
} // sol
|
||||
|
||||
#endif // SOL_TYPES_HPP
|
||||
|
|
651
sol/usertype.hpp
651
sol/usertype.hpp
|
@ -30,375 +30,350 @@
|
|||
#include <vector>
|
||||
#include <array>
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
|
||||
namespace sol {
|
||||
const std::array<std::string, 2> meta_variable_names = {{
|
||||
"__index",
|
||||
"__newindex"
|
||||
}};
|
||||
const std::array<std::string, 2> meta_variable_names = { {
|
||||
"__index",
|
||||
"__newindex",
|
||||
} };
|
||||
|
||||
const std::array<std::string, 19> meta_function_names = {{
|
||||
"__index",
|
||||
"__newindex",
|
||||
"__mode",
|
||||
"__call",
|
||||
"__metatable",
|
||||
"__tostring",
|
||||
"__len",
|
||||
"__unm",
|
||||
"__add",
|
||||
"__sub",
|
||||
"__mul",
|
||||
"__div",
|
||||
"__mod",
|
||||
"__pow",
|
||||
"__concat",
|
||||
"__eq",
|
||||
"__lt",
|
||||
"__le",
|
||||
"__gc",
|
||||
}};
|
||||
const std::array<std::string, 21> meta_function_names = { {
|
||||
"new",
|
||||
"__index",
|
||||
"__newindex",
|
||||
"__mode",
|
||||
"__call",
|
||||
"__metatable",
|
||||
"__tostring",
|
||||
"__len",
|
||||
"__unm",
|
||||
"__add",
|
||||
"__sub",
|
||||
"__mul",
|
||||
"__div",
|
||||
"__mod",
|
||||
"__pow",
|
||||
"__concat",
|
||||
"__eq",
|
||||
"__lt",
|
||||
"__le",
|
||||
"__gc",
|
||||
"__call",
|
||||
} };
|
||||
|
||||
enum class meta_function {
|
||||
index,
|
||||
new_index,
|
||||
mode,
|
||||
call,
|
||||
metatable,
|
||||
to_string,
|
||||
length,
|
||||
unary_minus,
|
||||
addition,
|
||||
subtraction,
|
||||
multiplication,
|
||||
division,
|
||||
modulus,
|
||||
power_of,
|
||||
involution = power_of,
|
||||
concatenation,
|
||||
equal_to,
|
||||
less_than,
|
||||
less_than_or_equal_to,
|
||||
};
|
||||
enum class meta_function {
|
||||
construct,
|
||||
index,
|
||||
new_index,
|
||||
mode,
|
||||
call,
|
||||
metatable,
|
||||
to_string,
|
||||
length,
|
||||
unary_minus,
|
||||
addition,
|
||||
subtraction,
|
||||
multiplication,
|
||||
division,
|
||||
modulus,
|
||||
power_of,
|
||||
involution = power_of,
|
||||
concatenation,
|
||||
equal_to,
|
||||
less_than,
|
||||
less_than_or_equal_to,
|
||||
garbage_collect,
|
||||
call_function,
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class usertype {
|
||||
private:
|
||||
typedef std::unordered_map<std::string, std::pair<std::unique_ptr<base_function>, bool>> function_map_t;
|
||||
function_map_t indexmetafunctions, newindexmetafunctions;
|
||||
std::vector<std::string> functionnames;
|
||||
std::vector<std::unique_ptr<base_function>> metafunctions;
|
||||
std::vector<luaL_Reg> metafunctiontable;
|
||||
std::vector<luaL_Reg> ptrmetafunctiontable;
|
||||
lua_CFunction cleanup;
|
||||
namespace usertype_detail {
|
||||
template<typename T, typename Funcs, typename FuncTable, typename MetaFuncTable, typename VarFuncTable>
|
||||
inline void push_metatable(lua_State* L, Funcs&& funcs, FuncTable&& functable, MetaFuncTable&& metafunctable, VarFuncTable&& varfunctable) {
|
||||
luaL_newmetatable(L, &usertype_traits<T>::metatable[0]);
|
||||
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;
|
||||
luaL_Reg cutoff = { nullptr, nullptr };
|
||||
oldref = cutoff;
|
||||
luaL_setfuncs(L, metafunctable.data(), metaup);
|
||||
oldref = old;
|
||||
}
|
||||
else {
|
||||
luaL_setfuncs(L, metafunctable.data(), metaup);
|
||||
}
|
||||
// Functions accessed by regular calls are put into a table
|
||||
// that get put into the metatable.__index metamethod
|
||||
lua_createtable(L, 0, functable.size());
|
||||
int functableindex = lua_gettop(L);
|
||||
int up = stack::stack_detail::push_upvalues(L, funcs);
|
||||
luaL_setfuncs(L, functable.data(), up);
|
||||
|
||||
// Also, set the variable indexer that's inside of the metatable's index
|
||||
luaL_newmetatable(L, &usertype_traits<T>::variable_metatable[0]);
|
||||
int varup = stack::stack_detail::push_upvalues(L, funcs);
|
||||
luaL_setfuncs(L, varfunctable.data(), varup);
|
||||
lua_setmetatable(L, functableindex);
|
||||
|
||||
lua_setfield(L, metatableindex, "__index");
|
||||
}
|
||||
|
||||
template<typename... TTypes>
|
||||
struct constructor {
|
||||
template<typename... Args>
|
||||
static void do_constructor(lua_State* L, T* obj, call_syntax syntax, int, types<Args...>) {
|
||||
default_construct fx{};
|
||||
stack::call(types<void>(), types<Args...>(), L, -1 + static_cast<int>(syntax), fx, obj);
|
||||
}
|
||||
template <typename T, typename Functions>
|
||||
inline void set_global_deleter(lua_State* L, lua_CFunction cleanup, Functions&& metafunctions) {
|
||||
// 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]);
|
||||
}
|
||||
}
|
||||
|
||||
static void match_constructor(lua_State*, T*, call_syntax, int) {
|
||||
throw error("No matching constructor for the arguments provided");
|
||||
}
|
||||
template<typename T>
|
||||
class usertype {
|
||||
private:
|
||||
typedef std::map<std::string, 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;
|
||||
std::array<luaL_Reg, 3> variablefunctiontable;
|
||||
base_function* indexfunc;
|
||||
base_function* newindexfunc;
|
||||
function_map_t indexwrapper, newindexwrapper;
|
||||
lua_CFunction constructfunc;
|
||||
lua_CFunction destructfunc;
|
||||
|
||||
template<typename ...CArgs, typename... Args>
|
||||
static void match_constructor(lua_State* L, T* obj, call_syntax syntax, int argcount, types<CArgs...> t, Args&&... args) {
|
||||
if(argcount == sizeof...(CArgs)) {
|
||||
do_constructor(L, obj, syntax, argcount, t);
|
||||
return;
|
||||
}
|
||||
match_constructor(L, obj, syntax, argcount, std::forward<Args>(args)...);
|
||||
}
|
||||
template<typename... TTypes>
|
||||
struct constructor {
|
||||
template<typename... Args>
|
||||
static void do_constructor(lua_State* L, T* obj, call_syntax syntax, int, types<Args...>) {
|
||||
default_construct fx{};
|
||||
stack::call(types<void>(), types<Args...>(), L, -1 + static_cast<int>(syntax), fx, obj);
|
||||
}
|
||||
|
||||
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);
|
||||
static void match_constructor(lua_State*, T*, call_syntax, int) {
|
||||
throw error("No matching constructor for the arguments provided");
|
||||
}
|
||||
|
||||
T** referencepointer = reinterpret_cast<T**>(lua_newuserdata(L, sizeof(T*) + sizeof(T)));
|
||||
T*& referencereference = *referencepointer;
|
||||
T* obj = reinterpret_cast<T*>(referencepointer + 1);
|
||||
referencereference = obj;
|
||||
match_constructor(L, obj, syntax, argcount - static_cast<int>(syntax), identity_t<TTypes>()...);
|
||||
template<typename ...CArgs, typename... Args>
|
||||
static void match_constructor(lua_State* L, T* obj, call_syntax syntax, int argcount, types<CArgs...> t, Args&&... args) {
|
||||
if (argcount == sizeof...(CArgs)) {
|
||||
do_constructor(L, obj, syntax, argcount, t);
|
||||
return;
|
||||
}
|
||||
match_constructor(L, obj, syntax, argcount, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
if(luaL_newmetatable(L, std::addressof(meta[0])) == 1) {
|
||||
lua_pop(L, 1);
|
||||
std::string err = "Unable to get usertype metatable for ";
|
||||
err += meta;
|
||||
throw error(err);
|
||||
}
|
||||
lua_setmetatable(L, -2);
|
||||
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);
|
||||
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
T** pointerpointer = reinterpret_cast<T**>(lua_newuserdata(L, sizeof(T*) + sizeof(T)));
|
||||
T*& referencepointer = *pointerpointer;
|
||||
T* obj = reinterpret_cast<T*>(pointerpointer + 1);
|
||||
referencepointer = obj;
|
||||
match_constructor(L, obj, syntax, argcount - static_cast<int>(syntax), identity_t<TTypes>()...);
|
||||
|
||||
static 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;
|
||||
}
|
||||
if (luaL_newmetatable(L, &meta[0]) == 1) {
|
||||
lua_pop(L, 1);
|
||||
std::string err = "Unable to get usertype metatable for ";
|
||||
err += meta;
|
||||
throw error(err);
|
||||
}
|
||||
lua_setmetatable(L, -2);
|
||||
|
||||
template<std::size_t N>
|
||||
void build_function_tables(function_map_t*& index, function_map_t*& newindex) {
|
||||
int extracount = 0;
|
||||
if(!indexmetafunctions.empty()) {
|
||||
if(index == nullptr) {
|
||||
auto idxptr = std::make_unique<usertype_indexing_function<void (T::*)(), T>>("__index", nullptr);
|
||||
index = &(idxptr->functions);
|
||||
functionnames.emplace_back("__index");
|
||||
metafunctions.emplace_back(std::move(idxptr));
|
||||
std::string& name = functionnames.back();
|
||||
metafunctiontable.push_back({ name.c_str(), &base_function::usertype<N>::call });
|
||||
ptrmetafunctiontable.push_back({ name.c_str(), &base_function::usertype<N>::ref_call });
|
||||
++extracount;
|
||||
}
|
||||
auto& idx = *index;
|
||||
for(auto&& namedfunc : indexmetafunctions) {
|
||||
idx.emplace(std::move(namedfunc.first), std::move(namedfunc.second));
|
||||
}
|
||||
}
|
||||
if(!newindexmetafunctions.empty()) {
|
||||
if(newindex == nullptr) {
|
||||
auto idxptr = std::make_unique<usertype_indexing_function<void (T::*)(), T>>("__newindex", nullptr);
|
||||
newindex = &(idxptr->functions);
|
||||
functionnames.emplace_back("__newindex");
|
||||
metafunctions.emplace_back(std::move(idxptr));
|
||||
std::string& name = functionnames.back();
|
||||
if(extracount > 0) {
|
||||
metafunctiontable.push_back({ name.c_str(), &base_function::usertype<N + 1>::call });
|
||||
ptrmetafunctiontable.push_back({ name.c_str(), &base_function::usertype<N + 1>::ref_call });
|
||||
}
|
||||
else {
|
||||
metafunctiontable.push_back({ name.c_str(), &base_function::usertype<N>::call });
|
||||
ptrmetafunctiontable.push_back({ name.c_str(), &base_function::usertype<N>::ref_call });
|
||||
}
|
||||
++extracount;
|
||||
}
|
||||
auto& idx = *newindex;
|
||||
for(auto&& namedfunc : newindexmetafunctions) {
|
||||
idx.emplace(std::move(namedfunc.first), std::move(namedfunc.second));
|
||||
}
|
||||
}
|
||||
switch(extracount) {
|
||||
case 2:
|
||||
cleanup = &base_function::usertype<N + 2>::gc;
|
||||
break;
|
||||
case 1:
|
||||
cleanup = &base_function::usertype<N + 1>::gc;
|
||||
break;
|
||||
case 0:
|
||||
default:
|
||||
cleanup = &base_function::usertype<N + 0>::gc;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
|
||||
template<std::size_t N, typename Base, typename Ret>
|
||||
bool build_function(std::true_type, function_map_t*&, function_map_t*&, std::string funcname, Ret Base::* func) {
|
||||
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;
|
||||
indexmetafunctions.emplace(funcname, std::make_pair(std::make_unique<usertype_variable_function<function_type, T>>(func), false));
|
||||
newindexmetafunctions.emplace(funcname, std::make_pair(std::make_unique<usertype_variable_function<function_type, T>>(func), false));
|
||||
return false;
|
||||
}
|
||||
static 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... Functions>
|
||||
std::unique_ptr<base_function> make_function(const std::string&, overload_set<Functions...> func) {
|
||||
return std::make_unique<usertype_overloaded_function<T, Functions...>>(func);
|
||||
}
|
||||
template<typename... Functions>
|
||||
std::unique_ptr<base_function> make_function(const std::string&, overload_set<Functions...> func) {
|
||||
return std::make_unique<usertype_overloaded_function<T, Functions...>>(func);
|
||||
}
|
||||
|
||||
template<typename Arg, typename... Args, typename Ret>
|
||||
std::unique_ptr<base_function> make_function(const std::string&, Ret(*func)(Arg, Args...)) {
|
||||
typedef Unqualified<std::remove_pointer_t<Arg>> 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 userdata type.");
|
||||
typedef std::decay_t<decltype(func)> function_type;
|
||||
return std::make_unique<usertype_function<function_type, T>>(func);
|
||||
}
|
||||
template<typename Arg, typename... Args, typename Ret>
|
||||
std::unique_ptr<base_function> make_function(const std::string&, Ret(*func)(Arg, Args...)) {
|
||||
typedef Unqualified<std::remove_pointer_t<Arg>> 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 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>
|
||||
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");
|
||||
typedef std::decay_t<decltype(func)> function_type;
|
||||
return std::make_unique<usertype_variable_function<function_type, T>>(func);
|
||||
}
|
||||
template<typename Base, typename Ret>
|
||||
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");
|
||||
typedef std::decay_t<decltype(func)> function_type;
|
||||
return std::make_unique<usertype_variable_function<function_type, T>>(func);
|
||||
}
|
||||
|
||||
template<typename Base, typename Ret>
|
||||
std::unique_ptr<base_function> make_variable_function(std::false_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");
|
||||
typedef std::decay_t<decltype(func)> function_type;
|
||||
return std::make_unique<usertype_function<function_type, T>>(func);
|
||||
}
|
||||
template<typename Base, typename Ret>
|
||||
std::unique_ptr<base_function> make_variable_function(std::false_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");
|
||||
typedef std::decay_t<decltype(func)> function_type;
|
||||
return std::make_unique<usertype_function<function_type, T>>(func);
|
||||
}
|
||||
|
||||
template<typename Base, typename Ret>
|
||||
std::unique_ptr<base_function> make_function(const std::string& name, Ret Base::* func) {
|
||||
typedef std::decay_t<decltype(func)> function_type;
|
||||
return make_variable_function(std::is_member_object_pointer<function_type>(), name, func);
|
||||
}
|
||||
template<typename Base, typename Ret>
|
||||
std::unique_ptr<base_function> make_function(const std::string& name, Ret Base::* func) {
|
||||
typedef std::decay_t<decltype(func)> function_type;
|
||||
return make_variable_function(std::is_member_object_pointer<function_type>(), name, func);
|
||||
}
|
||||
|
||||
template<typename Fx>
|
||||
std::unique_ptr<base_function> make_function(const std::string&, Fx&& func) {
|
||||
typedef Unqualified<Fx> Fxu;
|
||||
typedef std::tuple_element_t<0, typename function_traits<Fxu>::args_tuple_type> Arg0;
|
||||
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<typename Fx>
|
||||
std::unique_ptr<base_function> make_function(const std::string&, Fx&& func) {
|
||||
typedef Unqualified<Fx> Fxu;
|
||||
typedef std::tuple_element_t<0, typename function_traits<Fxu>::args_tuple_type> Arg0;
|
||||
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>
|
||||
bool build_function(std::false_type, function_map_t*& index, function_map_t*& newindex, std::string funcname, Fx&& func) {
|
||||
typedef std::decay_t<Fx> function_type;
|
||||
auto metamethod = std::find(meta_function_names.begin(), meta_function_names.end(), funcname);
|
||||
if(metamethod != meta_function_names.end()) {
|
||||
functionnames.push_back(std::move(funcname));
|
||||
std::string& name = functionnames.back();
|
||||
auto indexmetamethod = std::find(meta_variable_names.begin(), meta_variable_names.end(), name);
|
||||
std::unique_ptr<base_function> baseptr(nullptr);
|
||||
if(indexmetamethod != meta_variable_names.end()) {
|
||||
auto idxptr = std::make_unique<usertype_indexing_function<function_type, T>>(name, func);
|
||||
std::ptrdiff_t idxvalue = std::distance(meta_variable_names.begin(), indexmetamethod);
|
||||
switch(idxvalue) {
|
||||
case 0:
|
||||
index = &(idxptr->functions);
|
||||
break;
|
||||
case 1:
|
||||
newindex = &(idxptr->functions);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
baseptr = std::move(idxptr);
|
||||
}
|
||||
else {
|
||||
baseptr = make_function(funcname, std::forward<Fx>(func));
|
||||
}
|
||||
metafunctions.emplace_back(std::move(baseptr));
|
||||
metafunctiontable.push_back( { name.c_str(), &base_function::usertype<N>::call } );
|
||||
ptrmetafunctiontable.push_back( { name.c_str(), &base_function::usertype<N>::ref_call } );
|
||||
return true;
|
||||
}
|
||||
indexmetafunctions.emplace(funcname, std::make_pair(make_function(funcname, std::forward<Fx>(func)), true));
|
||||
return false;
|
||||
}
|
||||
template<std::size_t N, typename Fx>
|
||||
void build_function(std::string funcname, Fx&& func) {
|
||||
typedef std::is_member_object_pointer<Unqualified<Fx>> is_variable;
|
||||
typedef std::decay_t<Fx> function_type;
|
||||
functionnames.push_back(std::move(funcname));
|
||||
std::string& name = functionnames.back();
|
||||
auto baseptr = make_function(name, std::forward<Fx>(func));
|
||||
functions.emplace_back(std::move(baseptr));
|
||||
if (is_variable::value) {
|
||||
indexwrapper.insert({ name, functions.back().get() });
|
||||
newindexwrapper.insert({ name, 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(), &base_function::usertype<N>::call });
|
||||
return;
|
||||
}
|
||||
functiontable.push_back({ name.c_str(), &base_function::usertype<N>::call });
|
||||
}
|
||||
|
||||
template<std::size_t N, typename Fx, typename... Args>
|
||||
void build_function_tables(function_map_t*& index, function_map_t*& newindex, std::string funcname, Fx&& func, Args&&... args) {
|
||||
typedef std::is_member_object_pointer<Unqualified<Fx>> is_variable;
|
||||
static const std::size_t V = static_cast<std::size_t>(!is_variable::value);
|
||||
if(build_function<N>(is_variable(), index, newindex, std::move(funcname), std::forward<Fx>(func))) {
|
||||
build_function_tables<N + V>(index, newindex, std::forward<Args>(args)...);
|
||||
}
|
||||
else {
|
||||
build_function_tables<N>(index, newindex, std::forward<Args>(args)...);
|
||||
}
|
||||
}
|
||||
template<std::size_t N, typename Fx, typename... Args>
|
||||
void build_function_tables(std::string funcname, Fx&& func, Args&&... args) {
|
||||
build_function<N>(std::move(funcname), std::forward<Fx>(func));
|
||||
build_function_tables<N + 1>(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<std::size_t N, typename Base, typename Ret, typename... Args>
|
||||
void build_function_tables(function_map_t*& index, function_map_t*& newindex, meta_function metafunc, Ret Base::* func, Args&&... args) {
|
||||
std::size_t idx = static_cast<std::size_t>(metafunc);
|
||||
const std::string& funcname = meta_function_names[idx];
|
||||
build_function_tables<N>(index, newindex, funcname, std::move(func), std::forward<Args>(args)...);
|
||||
}
|
||||
template<std::size_t N, typename Fx, typename... Args>
|
||||
void build_function_tables(meta_function metafunc, Fx&& func, Args&&... args) {
|
||||
std::size_t idx = static_cast<std::size_t>(metafunc);
|
||||
const std::string& funcname = meta_function_names[idx];
|
||||
build_function_tables<N>(funcname, std::forward<Fx>(func), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
public:
|
||||
template<typename... Args>
|
||||
usertype(Args&&... args): usertype(default_constructor, std::forward<Args>(args)...) {}
|
||||
template<std::size_t N>
|
||||
void build_function_tables() {
|
||||
int variableend = 0;
|
||||
if (!indexwrapper.empty()) {
|
||||
functions.push_back(std::make_unique<usertype_indexing_function>("__index", indexfunc, std::move(indexwrapper)));
|
||||
variablefunctiontable[0] = { "__index", &base_function::usertype<N>::call };
|
||||
++variableend;
|
||||
}
|
||||
if (!newindexwrapper.empty()) {
|
||||
functions.push_back(std::make_unique<usertype_indexing_function>("__newindex", newindexfunc, std::move(newindexwrapper)));
|
||||
variablefunctiontable[variableend] = { "__newindex", indexwrapper.empty() ? &base_function::usertype<N>::call : &base_function::usertype<N + 1>::call };
|
||||
++variableend;
|
||||
}
|
||||
variablefunctiontable[variableend] = { nullptr, nullptr };
|
||||
switch (variableend) {
|
||||
case 2:
|
||||
destructfunc = &base_function::usertype<N + 2>::gc;
|
||||
break;
|
||||
case 1:
|
||||
destructfunc = &base_function::usertype<N + 1>::gc;
|
||||
break;
|
||||
case 0:
|
||||
destructfunc = &base_function::usertype<N + 0>::gc;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename... Args, typename... CArgs>
|
||||
usertype(constructors<CArgs...>, Args&&... args) {
|
||||
functionnames.reserve(sizeof...(args) + 2);
|
||||
metafunctiontable.reserve(sizeof...(args));
|
||||
ptrmetafunctiontable.reserve(sizeof...(args));
|
||||
public:
|
||||
template<typename... Args>
|
||||
usertype(Args&&... args) : usertype(default_constructor, std::forward<Args>(args)...) {}
|
||||
|
||||
function_map_t* index = nullptr;
|
||||
function_map_t* newindex = nullptr;
|
||||
build_function_tables<0>(index, newindex, std::forward<Args>(args)...);
|
||||
indexmetafunctions.clear();
|
||||
newindexmetafunctions.clear();
|
||||
functionnames.push_back("new");
|
||||
metafunctiontable.push_back({ functionnames.back().c_str(), &constructor<CArgs...>::construct });
|
||||
functionnames.push_back("__gc");
|
||||
metafunctiontable.push_back({ functionnames.back().c_str(), destruct });
|
||||
// ptr_functions does not participate in garbage collection/new,
|
||||
// as all pointered types are considered
|
||||
// to be references. This makes returns of
|
||||
// `std::vector<int>&` and `std::vector<int>*` work
|
||||
template<typename... Args, typename... CArgs>
|
||||
usertype(constructors<CArgs...>, Args&&... args)
|
||||
: indexfunc(nullptr), newindexfunc(nullptr), constructfunc(nullptr), destructfunc(nullptr) {
|
||||
functionnames.reserve(sizeof...(args)+2);
|
||||
functiontable.reserve(sizeof...(args));
|
||||
metafunctiontable.reserve(sizeof...(args));
|
||||
|
||||
metafunctiontable.push_back({ nullptr, nullptr });
|
||||
ptrmetafunctiontable.push_back({ nullptr, nullptr });
|
||||
}
|
||||
build_function_tables<0>(std::forward<Args>(args)...);
|
||||
|
||||
functionnames.push_back("new");
|
||||
metafunctiontable.push_back({ functionnames.back().c_str(), &constructor<CArgs...>::construct });
|
||||
functionnames.push_back("__gc");
|
||||
metafunctiontable.push_back({ functionnames.back().c_str(), destruct });
|
||||
|
||||
int push(lua_State* L) {
|
||||
// push pointer tables first,
|
||||
// but leave the regular T table on last
|
||||
// so it can be linked to a type for usage with `.new(...)` or `:new(...)`
|
||||
push_metatable(L, usertype_traits<T*>::metatable,
|
||||
metafunctions, ptrmetafunctiontable);
|
||||
lua_pop(L, 1);
|
||||
// ptr_functions does not participate in garbage collection/new,
|
||||
// as all pointered types are considered
|
||||
// to be references. This makes returns of
|
||||
// `std::vector<int>&` and `std::vector<int>*` work
|
||||
functiontable.push_back({ nullptr, nullptr });
|
||||
metafunctiontable.push_back({ nullptr, nullptr });
|
||||
}
|
||||
|
||||
push_metatable(L, usertype_traits<T>::metatable,
|
||||
metafunctions, metafunctiontable);
|
||||
set_global_deleter(L);
|
||||
return 1;
|
||||
}
|
||||
int push(lua_State* L) {
|
||||
// push pointer tables first,
|
||||
usertype_detail::push_metatable<T*>(L, functions, functiontable, metafunctiontable, variablefunctiontable);
|
||||
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>(L, functions, functiontable, metafunctiontable, variablefunctiontable);
|
||||
// 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, destructfunc, functions);
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
template<typename Meta, typename MetaFuncs, typename MetaFuncTable>
|
||||
static void push_metatable(lua_State* L, Meta&& metakey, MetaFuncs&& metafuncs, MetaFuncTable&& metafunctable) {
|
||||
luaL_newmetatable(L, std::addressof(metakey[0]));
|
||||
if(metafunctable.size() > 1) {
|
||||
// regular functions accessed through __index semantics
|
||||
int up = push_upvalues(L, metafuncs);
|
||||
luaL_setfuncs(L, metafunctable.data(), up);
|
||||
}
|
||||
}
|
||||
|
||||
void set_global_deleter(lua_State* L) {
|
||||
// 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 = 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, std::addressof(usertype_traits<T>::gctable[0]));
|
||||
}
|
||||
|
||||
template<bool release = false, typename TCont>
|
||||
static int push_upvalues(lua_State* L, TCont&& cont) {
|
||||
int n = 0;
|
||||
for(auto& c : cont) {
|
||||
if(release) {
|
||||
stack::push<upvalue>(L, c.release());
|
||||
}
|
||||
else {
|
||||
stack::push<upvalue>(L, c.get());
|
||||
}
|
||||
++n;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
};
|
||||
|
||||
namespace stack {
|
||||
template<typename T>
|
||||
struct pusher<usertype<T>> {
|
||||
static int push(lua_State* L, usertype<T>& user) {
|
||||
return user.push(L);
|
||||
}
|
||||
};
|
||||
} // stack
|
||||
namespace stack {
|
||||
template<typename T>
|
||||
struct pusher<usertype<T>> {
|
||||
static int push(lua_State* L, usertype<T>& user) {
|
||||
return user.push(L);
|
||||
}
|
||||
};
|
||||
} // stack
|
||||
} // sol
|
||||
|
||||
#endif // SOL_USERTYPE_HPP
|
||||
|
|
|
@ -30,7 +30,8 @@ template<typename T>
|
|||
struct usertype_traits {
|
||||
static const std::string name;
|
||||
static const std::string metatable;
|
||||
static const std::string gctable;
|
||||
static const std::string variable_metatable;
|
||||
static const std::string gc_table;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
|
@ -40,7 +41,10 @@ template<typename T>
|
|||
const std::string usertype_traits<T>::metatable = std::string("sol.").append(detail::demangle(typeid(T)));
|
||||
|
||||
template<typename T>
|
||||
const std::string usertype_traits<T>::gctable = std::string("sol.").append(detail::demangle(typeid(T))).append(".\xE2\x99\xBB");
|
||||
const std::string usertype_traits<T>::variable_metatable = std::string("sol.").append(detail::demangle(typeid(T))).append(".variables");
|
||||
|
||||
template<typename T>
|
||||
const std::string usertype_traits<T>::gc_table = std::string("sol.").append(detail::demangle(typeid(T))).append(".\xE2\x99\xBB");
|
||||
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user