mirror of
https://github.com/ThePhD/sol2.git
synced 2024-03-22 13:10:44 +08:00
userdata member variables are now supported
userdata now performs lookup based on tables userdata now has reduced number of vector tables userdata garbage collection improved debug.hpp - new header for debugging problems with stack, mostly for internal use
This commit is contained in:
parent
30c1f8c1b8
commit
368d78d463
53
sol/debug.hpp
Normal file
53
sol/debug.hpp
Normal file
|
@ -0,0 +1,53 @@
|
|||
// The MIT License (MIT)
|
||||
|
||||
// Copyright (c) 2013 Danny Y., Rapptz
|
||||
|
||||
// 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_DEBUG_HPP
|
||||
#define SOL_DEBUG_HPP
|
||||
|
||||
#include "stack.hpp"
|
||||
#include <iostream>
|
||||
|
||||
namespace sol {
|
||||
namespace debug {
|
||||
|
||||
inline std::string dump_types(lua_State* L) {
|
||||
std::string visual;
|
||||
std::size_t size = lua_gettop(L) + 1;
|
||||
for (std::size_t i = 1; i < size; ++i) {
|
||||
if (i != 1)
|
||||
visual += " | ";
|
||||
visual += type_name(L, stack::get<type>(L, i));
|
||||
}
|
||||
return visual;
|
||||
}
|
||||
|
||||
inline void print_stack (lua_State* L) {
|
||||
std::cout << dump_types(L) << std::endl;
|
||||
}
|
||||
|
||||
inline void print_section (const std::string& message, lua_State* L) {
|
||||
std::cout << "-- " << message << " -- [ " << dump_types(L) << " ]" << std::endl;
|
||||
}
|
||||
|
||||
} // debug
|
||||
} // sol
|
||||
|
||||
#endif // SOL_DEBUG_HPP
|
|
@ -24,10 +24,14 @@
|
|||
|
||||
#include "stack.hpp"
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace sol {
|
||||
namespace detail {
|
||||
template <typename T, typename Func, typename R>
|
||||
struct ref_call_t {};
|
||||
const auto ref_call = ref_call_t{};
|
||||
|
||||
template <typename T, typename Func, typename R, bool is_variable = std::is_member_object_pointer<Func>::value>
|
||||
struct functor {
|
||||
T* item;
|
||||
Func invocation;
|
||||
|
@ -43,7 +47,7 @@ struct functor {
|
|||
};
|
||||
|
||||
template <typename T, typename Func>
|
||||
struct functor<T, Func, void> {
|
||||
struct functor<T, Func, void, false> {
|
||||
T* item;
|
||||
Func invocation;
|
||||
|
||||
|
@ -56,6 +60,26 @@ struct functor<T, Func, void> {
|
|||
(member.*invocation)(std::forward<Args>(args)...);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename Func, typename R>
|
||||
struct functor<T, Func, R, true> {
|
||||
T* item;
|
||||
Func invocation;
|
||||
|
||||
template<typename... FxArgs>
|
||||
functor(FxArgs&&... fxargs): item(nullptr), invocation(std::forward<FxArgs>(fxargs)...) {}
|
||||
|
||||
template<typename Arg, typename... Args>
|
||||
void operator()(Arg&& arg, Args&&... args) {
|
||||
T& member = *item;
|
||||
(member.*invocation) = std::forward<Arg>(arg);
|
||||
}
|
||||
|
||||
R operator()() {
|
||||
T& member = *item;
|
||||
return (member.*invocation);
|
||||
}
|
||||
};
|
||||
} // detail
|
||||
|
||||
|
||||
|
@ -155,6 +179,17 @@ 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");
|
||||
|
@ -176,18 +211,19 @@ struct base_function {
|
|||
return base_gc(L, *pudata);
|
||||
}
|
||||
|
||||
template<std::size_t i>
|
||||
template<std::size_t I>
|
||||
struct userdata {
|
||||
static int call(lua_State* L) {
|
||||
// Zero-based template parameter, but upvalues start at 1
|
||||
return base_call(L, stack::get<upvalue_t>(L, i + 1));
|
||||
return base_call(L, stack::get<upvalue_t>(L, I + 1));
|
||||
}
|
||||
|
||||
static int ref_call(lua_State* L) {
|
||||
return ref_base_call(L, stack::get<upvalue_t>(L, I + 1));
|
||||
}
|
||||
};
|
||||
|
||||
template<std::size_t N>
|
||||
struct userdata_gc {
|
||||
static int gc(lua_State* L) {
|
||||
for (std::size_t i = 0; i < N; ++i) {
|
||||
for (std::size_t i = 0; i < I; ++i) {
|
||||
upvalue_t up = stack::get<upvalue_t>(L, i + 1);
|
||||
base_function* obj = static_cast<base_function*>(up.value);
|
||||
std::allocator<base_function> alloc{};
|
||||
|
@ -202,6 +238,10 @@ 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() {}
|
||||
};
|
||||
|
||||
|
@ -289,16 +329,17 @@ struct member_function : public base_function {
|
|||
};
|
||||
|
||||
template<typename Function, typename Tp>
|
||||
struct userdata_function : public base_function {
|
||||
struct userdata_function_core : public base_function {
|
||||
typedef typename std::remove_pointer<Tp>::type T;
|
||||
typedef typename std::remove_pointer<typename std::decay<Function>::type>::type function_type;
|
||||
typedef function_args_t<function_type> args_type;
|
||||
typedef function_return_t<function_type> return_type;
|
||||
typedef member_traits<Function> traits_type;
|
||||
typedef typename traits_type::args_type args_type;
|
||||
typedef typename traits_type::return_type return_type;
|
||||
|
||||
detail::functor<T, function_type, return_type> fx;
|
||||
|
||||
template<typename... FxArgs>
|
||||
userdata_function(FxArgs&&... fxargs): fx(std::forward<FxArgs>(fxargs)...) {}
|
||||
userdata_function_core(FxArgs&&... fxargs): fx(std::forward<FxArgs>(fxargs)...) {}
|
||||
|
||||
template<typename Return, typename Raw = Unqualified<Return>>
|
||||
typename std::enable_if<std::is_same<T, Raw>::value, void>::type push(lua_State* L, Return&& r) {
|
||||
|
@ -323,7 +364,8 @@ struct userdata_function : public base_function {
|
|||
|
||||
template<typename... Args>
|
||||
int operator()(types<void>, types<Args...> t, lua_State* L) {
|
||||
stack::get_call(L, 2, fx, t);
|
||||
static const std::size_t skew = static_cast<std::size_t>(std::is_member_object_pointer<function_type>::value);
|
||||
stack::get_call(L, 2 + skew, fx, t);
|
||||
std::ptrdiff_t nargs = sizeof...(Args);
|
||||
lua_pop(L, nargs);
|
||||
return 0;
|
||||
|
@ -342,12 +384,128 @@ struct userdata_function : public base_function {
|
|||
int operator()(types<>, types<Args...> t, lua_State* L) {
|
||||
return (*this)(types<void>(), t, L);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Function, typename Tp>
|
||||
struct userdata_function : public userdata_function_core<Function, Tp> {
|
||||
typedef userdata_function_core<Function, Tp> base_t;
|
||||
typedef typename std::remove_pointer<Tp>::type T;
|
||||
typedef member_traits<Function> traits_type;
|
||||
typedef typename traits_type::args_type args_type;
|
||||
typedef typename traits_type::return_type return_type;
|
||||
|
||||
template<typename... FxArgs>
|
||||
userdata_function(FxArgs&&... fxargs): base_t(std::forward<FxArgs>(fxargs)...) {}
|
||||
|
||||
template <typename Tx>
|
||||
int fx_call(lua_State* L) {
|
||||
this->fx.item = detail::get_ptr(stack::get<Tx>(L, 1));
|
||||
if (this->fx.item == nullptr)
|
||||
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);
|
||||
}
|
||||
|
||||
virtual int operator()(lua_State* L) override {
|
||||
fx.item = detail::get_ptr(stack::get<Tp>(L, 1));
|
||||
if (fx.item == nullptr)
|
||||
throw error("userdata for function call is null: are you using wrong call syntax? (use item:function(...) synax)");
|
||||
return (*this)(tuple_types<return_type>(), args_type(), L);
|
||||
return fx_call<T>(L);
|
||||
}
|
||||
|
||||
virtual int operator()(lua_State* L, detail::ref_call_t) override {
|
||||
return fx_call<T*>(L);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Function, typename Tp>
|
||||
struct userdata_variable_function : public userdata_function_core<Function, Tp> {
|
||||
typedef userdata_function_core<Function, Tp> base_t;
|
||||
typedef typename std::remove_pointer<Tp>::type T;
|
||||
typedef member_traits<Function> traits_type;
|
||||
typedef typename traits_type::args_type args_type;
|
||||
typedef typename traits_type::return_type return_type;
|
||||
|
||||
template<typename... FxArgs>
|
||||
userdata_variable_function(FxArgs&&... fxargs): base_t(std::forward<FxArgs>(fxargs)...) {}
|
||||
|
||||
template <typename Tx>
|
||||
int fx_call (lua_State* L) {
|
||||
type t = stack::get<type>(L, 1);
|
||||
switch(t) {
|
||||
case type::table:
|
||||
lua_getfield(L, 1, "sol.userdatavalue");
|
||||
this->fx.item = detail::get_ptr(stack::get<Tx>(L, -1));
|
||||
lua_pop(L, 1);
|
||||
break;
|
||||
default:
|
||||
this->fx.item = detail::get_ptr(stack::get<Tx>(L, 1));
|
||||
break;
|
||||
}
|
||||
if (this->fx.item == nullptr)
|
||||
throw error("userdata for member variable is null");
|
||||
int argcount = lua_gettop(L);
|
||||
switch (argcount) {
|
||||
case 2:
|
||||
return static_cast<base_t&>(*this)(tuple_types<return_type>(), types<>(), L);
|
||||
case 3:
|
||||
return static_cast<base_t&>(*this)(tuple_types<void>(), args_type(), L);
|
||||
default:
|
||||
throw error("cannot get/set userdata member variable with inappropriate number of arguments");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
virtual int operator()(lua_State* L) override {
|
||||
return fx_call<T>(L);
|
||||
}
|
||||
|
||||
virtual int operator()(lua_State* L, detail::ref_call_t) override {
|
||||
return fx_call<T*>(L);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Function, typename Tp>
|
||||
struct userdata_indexing_function : public userdata_function_core<Function, Tp> {
|
||||
typedef userdata_function_core<Function, Tp> base_t;
|
||||
typedef typename std::remove_pointer<Tp>::type T;
|
||||
typedef member_traits<Function> traits_type;
|
||||
typedef typename traits_type::args_type args_type;
|
||||
typedef typename traits_type::return_type return_type;
|
||||
|
||||
std::string name;
|
||||
std::unordered_map<std::string, std::pair<std::unique_ptr<base_function>, bool>> functions;
|
||||
|
||||
template<typename... FxArgs>
|
||||
userdata_indexing_function(std::string name, FxArgs&&... fxargs): base_t(std::forward<FxArgs>(fxargs)...), name(std::move(name)) {}
|
||||
|
||||
template <typename Tx>
|
||||
int fx_call (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_t>(L, function->second.first.get());
|
||||
stack::push(L, &base_function::userdata<0>::call, 1);
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
return (*function->second.first)(L);
|
||||
}
|
||||
}
|
||||
if (this->fx.invocation == nullptr) {
|
||||
std::string err = "invalid indexing \"";
|
||||
err += accessor;
|
||||
err += "\" on type: ";
|
||||
err += name;
|
||||
throw error(err);
|
||||
}
|
||||
this->fx.item = detail::get_ptr(stack::get<Tx>(L, 1));
|
||||
return static_cast<base_t&>(*this)(tuple_types<return_type>(), args_type(), L);
|
||||
}
|
||||
|
||||
virtual int operator()(lua_State* L) override {
|
||||
return fx_call<T>(L);
|
||||
}
|
||||
|
||||
virtual int operator()(lua_State* L, detail::ref_call_t) override {
|
||||
return fx_call<T*>(L);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -462,17 +462,6 @@ inline call_syntax get_call_syntax(lua_State* L, const std::string& meta) {
|
|||
return call_syntax::dot;
|
||||
}
|
||||
|
||||
inline std::string dump_types(lua_State* L) {
|
||||
std::string visual;
|
||||
std::size_t size = lua_gettop(L) + 1;
|
||||
for (std::size_t i = 1; i < size; ++i) {
|
||||
if (i != 1)
|
||||
visual += " | ";
|
||||
visual += type_name(L, stack::get<type>(L, i));
|
||||
}
|
||||
return visual;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
struct get_return {
|
||||
typedef decltype(get<T>(nullptr)) type;
|
||||
|
|
|
@ -79,33 +79,43 @@ public:
|
|||
switch(library) {
|
||||
case lib::base:
|
||||
luaL_requiref(L.get(), "base", luaopen_base, 1);
|
||||
lua_pop(L.get(), 1);
|
||||
break;
|
||||
case lib::package:
|
||||
luaL_requiref(L.get(), "package", luaopen_package, 1);
|
||||
lua_pop(L.get(), 1);
|
||||
break;
|
||||
case lib::coroutine:
|
||||
luaL_requiref(L.get(), "coroutine", luaopen_coroutine, 1);
|
||||
lua_pop(L.get(), 1);
|
||||
break;
|
||||
case lib::string:
|
||||
luaL_requiref(L.get(), "string", luaopen_string, 1);
|
||||
lua_pop(L.get(), 1);
|
||||
break;
|
||||
case lib::table:
|
||||
luaL_requiref(L.get(), "table", luaopen_table, 1);
|
||||
lua_pop(L.get(), 1);
|
||||
break;
|
||||
case lib::math:
|
||||
luaL_requiref(L.get(), "math", luaopen_math, 1);
|
||||
lua_pop(L.get(), 1);
|
||||
break;
|
||||
case lib::bit32:
|
||||
luaL_requiref(L.get(), "bit32", luaopen_bit32, 1);
|
||||
lua_pop(L.get(), 1);
|
||||
break;
|
||||
case lib::io:
|
||||
luaL_requiref(L.get(), "io", luaopen_io, 1);
|
||||
lua_pop(L.get(), 1);
|
||||
break;
|
||||
case lib::os:
|
||||
luaL_requiref(L.get(), "os", luaopen_os, 1);
|
||||
lua_pop(L.get(), 1);
|
||||
break;
|
||||
case lib::debug:
|
||||
luaL_requiref(L.get(), "debug", luaopen_debug, 1);
|
||||
lua_pop(L.get(), 1);
|
||||
break;
|
||||
case lib::count:
|
||||
break;
|
||||
|
|
|
@ -46,6 +46,14 @@ struct unwrap<std::reference_wrapper<T>> {
|
|||
typedef typename std::add_lvalue_reference<T>::type type;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct remove_member_pointer;
|
||||
|
||||
template <typename R, typename T>
|
||||
struct remove_member_pointer<R T::*> {
|
||||
typedef R type;
|
||||
};
|
||||
|
||||
template <typename T, template <typename...> class Templ>
|
||||
struct is_specialization_of : std::false_type { };
|
||||
template <typename... T, template <typename...> class Templ>
|
||||
|
@ -57,6 +65,9 @@ struct are_same : std::true_type { };
|
|||
template<class T, class U, class... Args>
|
||||
struct are_same<T, U, Args...> : std::integral_constant <bool, std::is_same<T, U>::value && are_same<T, Args...>::value> { };
|
||||
|
||||
template<typename T>
|
||||
using Type = typename T::type;
|
||||
|
||||
template<bool B>
|
||||
using Bool = std::integral_constant<bool, B>;
|
||||
|
||||
|
@ -66,6 +77,9 @@ using Not = Bool<!T::value>;
|
|||
template<typename Condition, typename Then, typename Else>
|
||||
using If = typename std::conditional<Condition::value, Then, Else>::type;
|
||||
|
||||
template<typename Condition, typename Then, typename Else>
|
||||
using TypeIf = typename std::conditional<Condition::value, Type<Then>, Type<Else>>::type;
|
||||
|
||||
template<typename... Args>
|
||||
struct And : Bool<true> {};
|
||||
|
||||
|
@ -206,6 +220,35 @@ struct function_traits<R(*)(Args...)> {
|
|||
using arg = typename std::tuple_element<i, arg_tuple_type>::type;
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
template <typename Signature, bool b = std::is_member_object_pointer<Signature>::value>
|
||||
struct member_traits : function_traits<Signature> {
|
||||
|
||||
};
|
||||
|
||||
template <typename Signature>
|
||||
struct member_traits<Signature, true> {
|
||||
typedef typename remove_member_pointer<Signature>::type Arg;
|
||||
typedef typename remove_member_pointer<Signature>::type R;
|
||||
typedef Signature signature_type;
|
||||
static const bool is_member_function = false;
|
||||
static const std::size_t arity = 1;
|
||||
typedef std::tuple<Arg> arg_tuple_type;
|
||||
typedef types<Arg> args_type;
|
||||
typedef R return_type;
|
||||
typedef R(function_type)(Arg);
|
||||
typedef R(*function_pointer_type)(Arg);
|
||||
typedef R(*free_function_pointer_type)(Arg);
|
||||
template<std::size_t i>
|
||||
using arg = typename std::tuple_element<i, arg_tuple_type>::type;
|
||||
};
|
||||
} // detail
|
||||
|
||||
template <typename Signature>
|
||||
struct member_traits : detail::member_traits<Signature> {
|
||||
|
||||
};
|
||||
|
||||
struct has_begin_end_impl {
|
||||
template<typename T, typename U = Unqualified<T>,
|
||||
typename B = decltype(std::declval<U&>().begin()),
|
||||
|
|
235
sol/userdata.hpp
235
sol/userdata.hpp
|
@ -41,18 +41,16 @@ inline std::unique_ptr<T> make_unique(Args&&... args) {
|
|||
template<typename T>
|
||||
class userdata {
|
||||
private:
|
||||
typedef std::unordered_map<std::string, std::pair<std::unique_ptr<base_function>, bool>> function_map_t;
|
||||
const static std::array<std::string, 2> metavariablenames;
|
||||
const static std::array<std::string, 19> metafunctionnames;
|
||||
std::string luaname;
|
||||
function_map_t indexmetafunctions, newindexmetafunctions;
|
||||
std::vector<std::string> functionnames;
|
||||
std::vector<std::unique_ptr<base_function>> funcs;
|
||||
std::vector<std::unique_ptr<base_function>> ptrfuncs;
|
||||
std::vector<std::unique_ptr<base_function>> metafuncs;
|
||||
std::vector<std::unique_ptr<base_function>> ptrmetafuncs;
|
||||
std::vector<luaL_Reg> functiontable;
|
||||
std::vector<luaL_Reg> ptrfunctiontable;
|
||||
std::vector<std::unique_ptr<base_function>> metafunctions;
|
||||
std::vector<luaL_Reg> metafunctiontable;
|
||||
std::vector<luaL_Reg> ptrmetafunctiontable;
|
||||
lua_CFunction cleanup;
|
||||
std::string luaname;
|
||||
|
||||
template<typename... TTypes>
|
||||
struct constructor {
|
||||
|
@ -84,14 +82,12 @@ private:
|
|||
T* obj = static_cast<T*>(udata);
|
||||
match_constructor(L, obj, syntax, argcount - static_cast<int>(syntax), typename identity<TTypes>::type()...);
|
||||
|
||||
|
||||
if (luaL_newmetatable(L, std::addressof(meta[0])) == 1) {
|
||||
lua_pop(L, 1);
|
||||
std::string err = "Unable to get userdata metatable for ";
|
||||
err += meta;
|
||||
throw error(err);
|
||||
}
|
||||
|
||||
lua_setmetatable(L, -2);
|
||||
|
||||
return 1;
|
||||
|
@ -108,59 +104,121 @@ private:
|
|||
}
|
||||
};
|
||||
|
||||
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_t>(L, c.release());
|
||||
else
|
||||
stack::push<upvalue_t>(L, c.get());
|
||||
++n;
|
||||
}
|
||||
return n;
|
||||
template<std::size_t N>
|
||||
void build_cleanup () {
|
||||
cleanup = &base_function::userdata<N>::gc;
|
||||
}
|
||||
|
||||
template <typename Meta, typename Funcs, typename FuncTable, typename MetaFuncs, typename MetaFuncTable>
|
||||
static void push_metatable(lua_State* L, Meta&& meta, Funcs&& funcs, FuncTable&& functable, MetaFuncs&& metafuncs, MetaFuncTable&& metafunctable) {
|
||||
luaL_newmetatable(L, std::addressof(meta[0]));
|
||||
if (functable.size() > 1) {
|
||||
// regular functions accessed through __index semantics
|
||||
int up = push_upvalues(L, funcs);
|
||||
luaL_setfuncs(L, functable.data(), up);
|
||||
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 = detail::make_unique<userdata_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::userdata<N>::call } );
|
||||
ptrmetafunctiontable.push_back( { name.c_str(), &base_function::userdata<N>::ref_call } );
|
||||
++extracount;
|
||||
}
|
||||
auto& idx = *index;
|
||||
for (auto&& namedfunc : indexmetafunctions ) {
|
||||
idx.emplace(std::move(namedfunc.first), std::move(namedfunc.second));
|
||||
}
|
||||
}
|
||||
if (metafunctable.size() > 1) {
|
||||
// meta functions
|
||||
int up = push_upvalues(L, metafuncs);
|
||||
luaL_setfuncs(L, metafunctable.data(), up);
|
||||
if (!newindexmetafunctions.empty()) {
|
||||
if (newindex == nullptr) {
|
||||
auto idxptr = detail::make_unique<userdata_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::userdata<N + 1>::call } );
|
||||
ptrmetafunctiontable.push_back( { name.c_str(), &base_function::userdata<N + 1>::ref_call } );
|
||||
}
|
||||
else {
|
||||
metafunctiontable.push_back( { name.c_str(), &base_function::userdata<N>::call } );
|
||||
ptrmetafunctiontable.push_back( { name.c_str(), &base_function::userdata<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:
|
||||
build_cleanup<N + 2>();
|
||||
break;
|
||||
case 1:
|
||||
build_cleanup<N + 1>();
|
||||
break;
|
||||
case 0:
|
||||
default:
|
||||
build_cleanup<N + 0>();
|
||||
break;
|
||||
}
|
||||
lua_pushvalue(L, -1);
|
||||
lua_setfield(L, -1, "__index");
|
||||
}
|
||||
|
||||
template<std::size_t N, std::size_t M>
|
||||
void build_function_tables() {}
|
||||
|
||||
template<std::size_t N, std::size_t M, typename... Args, typename TBase, typename Ret>
|
||||
void build_function_tables(std::string funcname, Ret TBase::* func, Args&&... args) {
|
||||
template<std::size_t N, typename TBase, typename Ret>
|
||||
bool build_function(std::true_type, function_map_t*&, function_map_t*&, std::string funcname, Ret TBase::* func) {
|
||||
static_assert(std::is_base_of<TBase, T>::value, "Any registered function must be part of the class");
|
||||
typedef typename std::decay<decltype(func)>::type function_type;
|
||||
functionnames.push_back(std::move(funcname));
|
||||
std::string& name = functionnames.back();
|
||||
auto metamethod = std::find(metafunctionnames.begin(), metafunctionnames.end(), name);
|
||||
indexmetafunctions.emplace(funcname, std::make_pair(detail::make_unique<userdata_variable_function<function_type, T>>(func), false));
|
||||
newindexmetafunctions.emplace(funcname, std::make_pair(detail::make_unique<userdata_variable_function<function_type, T>>(func), false));
|
||||
return false;
|
||||
}
|
||||
|
||||
template<std::size_t N, typename TBase, typename Ret>
|
||||
bool build_function(std::false_type, function_map_t*& index, function_map_t*& newindex, std::string funcname, Ret TBase::* func) {
|
||||
static_assert(std::is_base_of<TBase, T>::value, "Any registered function must be part of the class");
|
||||
typedef typename std::decay<decltype(func)>::type function_type;
|
||||
auto metamethod = std::find(metafunctionnames.begin(), metafunctionnames.end(), funcname);
|
||||
if (metamethod != metafunctionnames.end()) {
|
||||
metafuncs.emplace_back(detail::make_unique<userdata_function<function_type, T>>(std::move(func)));
|
||||
ptrmetafuncs.emplace_back(detail::make_unique<userdata_function<function_type, typename std::add_pointer<T>::type>>(std::move(func)));
|
||||
metafunctiontable.push_back({ name.c_str(), &base_function::userdata<N>::call });
|
||||
ptrmetafunctiontable.push_back({ name.c_str(), &base_function::userdata<N>::call });
|
||||
build_function_tables<N + 1, M>(std::forward<Args>(args)...);
|
||||
functionnames.push_back(std::move(funcname));
|
||||
std::string& name = functionnames.back();
|
||||
auto indexmetamethod = std::find(metavariablenames.begin(), metavariablenames.end(), name);
|
||||
std::unique_ptr<base_function> ptr(nullptr);
|
||||
if (indexmetamethod != metavariablenames.end()) {
|
||||
auto idxptr = detail::make_unique<userdata_indexing_function<function_type, T>>(name, func);
|
||||
switch( std::distance(indexmetamethod, metavariablenames.end()) ) {
|
||||
case 0:
|
||||
index = &(idxptr->functions);
|
||||
break;
|
||||
case 1:
|
||||
newindex = &(idxptr->functions);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
ptr = std::move(idxptr);
|
||||
}
|
||||
else {
|
||||
ptr = detail::make_unique<userdata_function<function_type, T>>(func);
|
||||
}
|
||||
metafunctions.emplace_back(std::move(ptr));
|
||||
metafunctiontable.push_back( { name.c_str(), &base_function::userdata<N>::call } );
|
||||
ptrmetafunctiontable.push_back( { name.c_str(), &base_function::userdata<N>::ref_call } );
|
||||
return true;
|
||||
}
|
||||
indexmetafunctions.emplace(funcname, std::make_pair(detail::make_unique<userdata_function<function_type, T>>(func), true ));
|
||||
newindexmetafunctions.emplace(funcname, std::make_pair(detail::make_unique<userdata_function<function_type, T>>(func), true));
|
||||
return false;
|
||||
}
|
||||
|
||||
template<std::size_t N, typename TBase, typename Ret, typename... Args>
|
||||
void build_function_tables(function_map_t*& index, function_map_t*& newindex, std::string funcname, Ret TBase::* func, Args&&... args) {
|
||||
typedef typename std::is_member_object_pointer<decltype(func)>::type 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), func)) {
|
||||
build_function_tables<N + V>(index, newindex, std::forward<Args>(args)...);
|
||||
}
|
||||
else {
|
||||
funcs.emplace_back(detail::make_unique<userdata_function<function_type, T>>(std::move(func)));
|
||||
ptrfuncs.emplace_back(detail::make_unique<userdata_function<function_type, typename std::add_pointer<T>::type>>(std::move(func)));
|
||||
functiontable.push_back({ name.c_str(), &base_function::userdata<M>::call });
|
||||
ptrfunctiontable.push_back({ name.c_str(), &base_function::userdata<M>::call });
|
||||
build_function_tables<N, M + 1>(std::forward<Args>(args)...);
|
||||
build_function_tables<N>(index, newindex, std::forward<Args>(args)...);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -174,22 +232,16 @@ public:
|
|||
template<typename... Args, typename... CArgs>
|
||||
userdata(std::string name, constructors<CArgs...>, Args&&... args): luaname(std::move(name)) {
|
||||
functionnames.reserve(sizeof...(args) + 2);
|
||||
|
||||
functiontable.reserve(sizeof...(args));
|
||||
ptrfunctiontable.reserve(sizeof...(args));
|
||||
metafunctiontable.reserve(sizeof...(args));
|
||||
ptrmetafunctiontable.reserve(sizeof...(args));
|
||||
|
||||
funcs.reserve(sizeof...(args));
|
||||
ptrfuncs.reserve(sizeof...(args));
|
||||
metafuncs.reserve(sizeof...(args));
|
||||
ptrmetafuncs.reserve(sizeof...(args));
|
||||
|
||||
cleanup = &base_function::userdata_gc<sizeof...(Args)>::gc;
|
||||
|
||||
build_function_tables<0, 0>(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");
|
||||
functiontable.push_back({ functionnames.back().c_str(), &constructor<CArgs...>::construct });
|
||||
metafunctiontable.push_back({ functionnames.back().c_str(), &constructor<CArgs...>::construct });
|
||||
functionnames.push_back("__gc");
|
||||
metafunctiontable.push_back({ functionnames.back().c_str(), &destructor::destruct });
|
||||
// ptr_functions does not participate in garbage collection/new,
|
||||
|
@ -197,10 +249,8 @@ public:
|
|||
// 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 });
|
||||
ptrfunctiontable.push_back({ nullptr, nullptr });
|
||||
ptrmetafunctiontable.push_back({ nullptr, nullptr });
|
||||
metafunctiontable.push_back( { nullptr, nullptr } );
|
||||
ptrmetafunctiontable.push_back( { nullptr, nullptr } );
|
||||
}
|
||||
|
||||
template<typename... Args, typename... CArgs>
|
||||
|
@ -213,27 +263,60 @@ public:
|
|||
|
||||
void 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(...)`
|
||||
// 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, userdata_traits<T*>::metatable,
|
||||
ptrfuncs, ptrfunctiontable,
|
||||
ptrmetafuncs, ptrmetafunctiontable);
|
||||
metafunctions, ptrmetafunctiontable);
|
||||
lua_pop(L, 1);
|
||||
|
||||
push_metatable(L, userdata_traits<T>::metatable,
|
||||
funcs, functiontable,
|
||||
metafuncs, metafunctiontable);
|
||||
metafunctions, metafunctiontable);
|
||||
set_global_deleter(L);
|
||||
}
|
||||
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()
|
||||
lua_createtable(L, 0, 0);
|
||||
lua_createtable(L, 0, 1);
|
||||
int up = push_upvalues<true>(L, funcs);
|
||||
up += push_upvalues<true>(L, ptrfuncs);
|
||||
up += push_upvalues<true>(L, metafuncs);
|
||||
up += push_upvalues<true>(L, ptrmetafuncs);
|
||||
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(userdata_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_t>(L, c.release());
|
||||
else
|
||||
stack::push<upvalue_t>(L, c.get());
|
||||
++n;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
const std::array<std::string, 2> userdata<T>::metavariablenames = {
|
||||
"__index",
|
||||
"__newindex"
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
|
|
65
tests.cpp
65
tests.cpp
|
@ -148,6 +148,18 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
struct Vec {
|
||||
float x, y, z;
|
||||
Vec(float x, float y, float z) : x{x}, y{y}, z{z} {}
|
||||
float length() {
|
||||
return sqrtf(x*x + y*y + z*z);
|
||||
}
|
||||
Vec normalized() {
|
||||
float invS = 1 / length();
|
||||
return {x * invS, y * invS, z * invS};
|
||||
}
|
||||
};
|
||||
|
||||
TEST_CASE("simple/set_global", "Check if the set_global works properly.") {
|
||||
sol::state lua;
|
||||
|
||||
|
@ -713,19 +725,6 @@ TEST_CASE("tables/issue-number-twenty-five", "Using pointers and references from
|
|||
}
|
||||
|
||||
TEST_CASE("userdata/issue-number-thirty-five", "using value types created from lua-called C++ code, fixing user-defined types with constructors") {
|
||||
struct Vec {
|
||||
float x, y, z;
|
||||
Vec(float x, float y, float z) : x{x}, y{y}, z{z} {}
|
||||
float length() {
|
||||
return sqrtf(x*x + y*y + z*z);
|
||||
}
|
||||
|
||||
Vec normalized() {
|
||||
float invS = 1 / length();
|
||||
return {x * invS, y * invS, z * invS};
|
||||
}
|
||||
};
|
||||
|
||||
struct Line {
|
||||
Vec p1, p2;
|
||||
Line() : p1{0, 0, 0}, p2{0, 0, 0} {}
|
||||
|
@ -753,18 +752,6 @@ TEST_CASE("userdata/issue-number-thirty-five", "using value types created from l
|
|||
}
|
||||
|
||||
TEST_CASE("userdata/lua-stored-userdata", "ensure userdata values can be stored without keeping userdata object alive") {
|
||||
struct Vec {
|
||||
float x, y, z;
|
||||
Vec(float x, float y, float z) : x{x}, y{y}, z{z} {}
|
||||
float length() {
|
||||
return sqrtf(x*x + y*y + z*z);
|
||||
}
|
||||
Vec normalized() {
|
||||
float invS = 1 / length();
|
||||
return {x * invS, y * invS, z * invS};
|
||||
}
|
||||
};
|
||||
|
||||
sol::state lua;
|
||||
lua.open_libraries(sol::lib::base);
|
||||
|
||||
|
@ -785,3 +772,31 @@ TEST_CASE("userdata/lua-stored-userdata", "ensure userdata values can be stored
|
|||
REQUIRE_NOTHROW(lua.script("v = Vec.new(1, 2, 3)\n"
|
||||
"print(v:normalized():length())" ));
|
||||
}
|
||||
|
||||
TEST_CASE("userdata/member-variables", "allow table-like accessors to behave as member variables for userdata") {
|
||||
sol::state lua;
|
||||
lua.open_libraries(sol::lib::base);
|
||||
sol::constructors<sol::types<float, float, float>> ctor;
|
||||
sol::userdata<Vec> udata("Vec", ctor,
|
||||
"x", &Vec::x,
|
||||
"y", &Vec::y,
|
||||
"z", &Vec::z,
|
||||
"normalized", &Vec::normalized,
|
||||
"length", &Vec::length);
|
||||
lua.set_userdata(udata);
|
||||
|
||||
REQUIRE_NOTHROW(lua.script("v = Vec.new(1, 2, 3)\n"
|
||||
"v2 = Vec.new(0, 1, 0)\n"
|
||||
"print(v:length())\n"
|
||||
"v.x = 2\n"
|
||||
"v2.y = 2\n"
|
||||
"print(v.x, v.y, v.z)\n"
|
||||
"print(v2.x, v2.y, v2.z)\n"
|
||||
"assert(v.x == 2)\n"
|
||||
"assert(v2.x == 0)\n"
|
||||
"assert(v2.y == 2)\n"
|
||||
"v.x = 3\n"
|
||||
"local x = v.x\n"
|
||||
"assert(x == 3)\n"
|
||||
));
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user