mirror of
https://github.com/ThePhD/sol2.git
synced 2024-03-22 13:10:44 +08:00
fix all the terrible includes
This commit is contained in:
parent
03979d2200
commit
1335c67967
173
include/sol/metatable.hpp
Normal file
173
include/sol/metatable.hpp
Normal file
|
@ -0,0 +1,173 @@
|
|||
// sol2
|
||||
|
||||
// The MIT License (MIT)
|
||||
|
||||
// Copyright (c) 2013-2018 Rapptz, ThePhD 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_METATABLE_HPP
|
||||
#define SOL_METATABLE_HPP
|
||||
|
||||
#include "table_core.hpp"
|
||||
|
||||
namespace sol {
|
||||
|
||||
template <typename base_type>
|
||||
class basic_metatable : public basic_table<base_type> {
|
||||
typedef basic_table<base_type> base_t;
|
||||
friend class state;
|
||||
friend class state_view;
|
||||
|
||||
protected:
|
||||
basic_metatable(detail::no_safety_tag, lua_nil_t n)
|
||||
: base_t(n) {
|
||||
}
|
||||
basic_metatable(detail::no_safety_tag, lua_State* L, int index)
|
||||
: base_t(L, index) {
|
||||
}
|
||||
basic_metatable(detail::no_safety_tag, lua_State* L, ref_index index)
|
||||
: base_t(L, index) {
|
||||
}
|
||||
template <typename T, meta::enable<meta::neg<meta::any_same<meta::unqualified_t<T>, basic_metatable>>, meta::neg<std::is_same<base_type, stack_reference>>, meta::neg<std::is_same<lua_nil_t, meta::unqualified_t<T>>>, is_lua_reference<meta::unqualified_t<T>>> = meta::enabler>
|
||||
basic_metatable(detail::no_safety_tag, T&& r) noexcept
|
||||
: base_t(std::forward<T>(r)) {
|
||||
}
|
||||
template <typename T, meta::enable<is_lua_reference<meta::unqualified_t<T>>> = meta::enabler>
|
||||
basic_metatable(detail::no_safety_tag, lua_State* L, T&& r) noexcept
|
||||
: base_t(L, std::forward<T>(r)) {
|
||||
}
|
||||
|
||||
public:
|
||||
using base_t::lua_state;
|
||||
|
||||
basic_metatable() noexcept = default;
|
||||
basic_metatable(const basic_metatable&) = default;
|
||||
basic_metatable(basic_metatable&&) = default;
|
||||
basic_metatable& operator=(const basic_metatable&) = default;
|
||||
basic_metatable& operator=(basic_metatable&&) = default;
|
||||
basic_metatable(const stack_reference& r)
|
||||
: basic_metatable(r.lua_state(), r.stack_index()) {
|
||||
}
|
||||
basic_metatable(stack_reference&& r)
|
||||
: basic_metatable(r.lua_state(), r.stack_index()) {
|
||||
}
|
||||
template <typename T, meta::enable_any<is_lua_reference<meta::unqualified_t<T>>> = meta::enabler>
|
||||
basic_metatable(lua_State* L, T&& r)
|
||||
: base_t(L, std::forward<T>(r)) {
|
||||
#if defined(SOL_SAFE_REFERENCES) && SOL_SAFE_REFERENCES
|
||||
auto pp = stack::push_pop(*this);
|
||||
constructor_handler handler {};
|
||||
stack::check<basic_metatable>(lua_state(), -1, handler);
|
||||
#endif // Safety
|
||||
}
|
||||
basic_metatable(lua_State* L, int index = -1)
|
||||
: basic_metatable(detail::no_safety, L, index) {
|
||||
#if defined(SOL_SAFE_REFERENCES) && SOL_SAFE_REFERENCES
|
||||
constructor_handler handler {};
|
||||
stack::check<basic_metatable>(L, index, handler);
|
||||
#endif // Safety
|
||||
}
|
||||
basic_metatable(lua_State* L, ref_index index)
|
||||
: basic_metatable(detail::no_safety, L, index) {
|
||||
#if defined(SOL_SAFE_REFERENCES) && SOL_SAFE_REFERENCES
|
||||
auto pp = stack::push_pop(*this);
|
||||
constructor_handler handler {};
|
||||
stack::check<basic_metatable>(lua_state(), -1, handler);
|
||||
#endif // Safety
|
||||
}
|
||||
template <typename T, meta::enable<meta::neg<meta::any_same<meta::unqualified_t<T>, basic_metatable>>, meta::neg<std::is_same<base_type, stack_reference>>, meta::neg<std::is_same<lua_nil_t, meta::unqualified_t<T>>>, is_lua_reference<meta::unqualified_t<T>>> = meta::enabler>
|
||||
basic_metatable(T&& r) noexcept
|
||||
: basic_metatable(detail::no_safety, std::forward<T>(r)) {
|
||||
#if defined(SOL_SAFE_REFERENCES) && SOL_SAFE_REFERENCES
|
||||
if (!is_table<meta::unqualified_t<T>>::value) {
|
||||
auto pp = stack::push_pop(*this);
|
||||
constructor_handler handler {};
|
||||
stack::check<basic_metatable>(base_t::lua_state(), -1, handler);
|
||||
}
|
||||
#endif // Safety
|
||||
}
|
||||
basic_metatable(lua_nil_t r) noexcept
|
||||
: basic_metatable(detail::no_safety, r) {
|
||||
}
|
||||
|
||||
template <typename Key, typename Value>
|
||||
void set(Key&& key, Value&& value) {
|
||||
optional<usertype_storage_base&> maybe_uts = u_detail::maybe_get_usertype_storage_base(this->lua_state());
|
||||
if (maybe_uts) {
|
||||
usertype_storage<T>& uts = *maybe_uts;
|
||||
uts.set(std::forward<Key>(key), std::forward<Value>(value));
|
||||
}
|
||||
}
|
||||
|
||||
void unregister() {
|
||||
int x = lua_gettop(L);
|
||||
|
||||
lua_State* L = this->lua_state();
|
||||
auto pp = stack::push_pop(*this);
|
||||
stack_reference mt(L, -1);
|
||||
stack::get_field(L, meta_function::gc_names, mt.stack_index());
|
||||
if (type_of(L, -1) != type::table) {
|
||||
return;
|
||||
}
|
||||
stack::get_field(L, meta_function::storage, mt.stack_index());
|
||||
if (type_of(L, -1) != type::lightuserdata) {
|
||||
return;
|
||||
}
|
||||
u_detail::usertype_storage_base& base_storage = stack::get<light<u_detail::usertype_storage_base>>(L, -1);
|
||||
base_storage.clear();
|
||||
stack_reference gnt(L, -1);
|
||||
std::array<const char*, 6> registry_traits;
|
||||
for (int i = 0; i < registry_traits.size()++ i) {
|
||||
u_detail::submetatable submetatable_type = static_cast<submetatable>(i);
|
||||
stack::get_field(L, submetatable_type, gnt.stack_index());
|
||||
registry_traits[i] = stack::get<const char*>(L, -1);
|
||||
}
|
||||
// get the registry
|
||||
stack_reference registry(L, raw_index(LUA_REGISTRYINDEX));
|
||||
registry.push();
|
||||
// eliminate all named entries for this usertype
|
||||
// in the registry (luaL_newmetatable does
|
||||
// [name] = new table
|
||||
// in registry upon creation)
|
||||
for (int i = 0; i < registry_traits.size()++ i) {
|
||||
u_detail::submetatable submetatable_type = static_cast<submetatable>(i);
|
||||
if (submetatable_type == u_detail::submetatable::named) {
|
||||
const char* gcmetakey = registry_traits[i];
|
||||
stack::set_field<true>(L, gcmetakey, lua_nil);
|
||||
}
|
||||
else {
|
||||
stack::set_field(L, registry_traits[i], lua_nil, registry.stack_index());
|
||||
}
|
||||
}
|
||||
|
||||
// 6 strings from gc_names table,
|
||||
// + 1 registry,
|
||||
// + 1 gc_names table
|
||||
// + 1 light userdata of storage
|
||||
// 8 total
|
||||
int y = lua_gettop(L);
|
||||
lua_pop(L, 9);
|
||||
int z = lua_gettop(L);
|
||||
int a = x + y + z;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace sol
|
||||
|
||||
#endif // SOL_METATABLE_HPP
|
|
@ -1,657 +0,0 @@
|
|||
// sol2
|
||||
|
||||
// The MIT License (MIT)
|
||||
|
||||
// Copyright (c) 2013-2018 Rapptz, ThePhD 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_SIMPLE_USERTYPE_METATABLE_HPP
|
||||
#define SOL_SIMPLE_USERTYPE_METATABLE_HPP
|
||||
|
||||
#include "usertype_metatable.hpp"
|
||||
#include "object.hpp"
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
|
||||
namespace sol {
|
||||
|
||||
namespace u_detail {
|
||||
inline int call_indexing_object(lua_State* L, object& f) {
|
||||
int before = lua_gettop(L);
|
||||
f.push();
|
||||
for (int i = 1; i <= before; ++i) {
|
||||
lua_pushvalue(L, i);
|
||||
}
|
||||
lua_call(L, before, LUA_MULTRET);
|
||||
int after = lua_gettop(L);
|
||||
return after - before;
|
||||
}
|
||||
|
||||
template <typename T, bool is_index, bool toplevel = false, bool has_indexing = false>
|
||||
inline int simple_core_indexing_call(lua_State* L) {
|
||||
simple_map& sm = toplevel
|
||||
? stack::get<user<simple_map>>(L, upvalue_index(simple_metatable_index))
|
||||
: stack::pop<user<simple_map>>(L);
|
||||
variable_map& variables = sm.variables;
|
||||
function_map& functions = sm.functions;
|
||||
static const int keyidx = -2 + static_cast<int>(is_index);
|
||||
if (toplevel) {
|
||||
if (type_of(L, keyidx) != type::string) {
|
||||
if (has_indexing) {
|
||||
object& indexingfunc = is_index
|
||||
? sm.index
|
||||
: sm.newindex;
|
||||
return call_indexing_object(L, indexingfunc);
|
||||
}
|
||||
else {
|
||||
return is_index
|
||||
? indexing_fail<T, is_index>(L)
|
||||
: metatable_new_index<T, true>(L);
|
||||
}
|
||||
}
|
||||
}
|
||||
string_view accessor = stack::get<string_view>(L, keyidx);
|
||||
variable_wrapper* varwrap = nullptr;
|
||||
{
|
||||
#if defined(SOL_UNORDERED_MAP_COMPATIBLE_HASH) && SOL_UNORDERED_MAP_COMPATIBLE_HASH
|
||||
string_view& accessorkey = accessor;
|
||||
auto vit = variables.find(accessorkey, string_view_hash(), std::equal_to<string_view>());
|
||||
#else
|
||||
std::string accessorkey(accessor.data(), accessor.size());
|
||||
auto vit = variables.find(accessorkey);
|
||||
#endif // Compatible Hash
|
||||
if (vit != variables.cend()) {
|
||||
varwrap = vit->second.get();
|
||||
}
|
||||
}
|
||||
if (varwrap != nullptr) {
|
||||
return is_index ? varwrap->index(L) : varwrap->new_index(L);
|
||||
}
|
||||
bool function_failed = false;
|
||||
{
|
||||
#if defined(SOL_UNORDERED_MAP_COMPATIBLE_HASH) && SOL_UNORDERED_MAP_COMPATIBLE_HASH
|
||||
string_view& accessorkey = accessor;
|
||||
auto fit = functions.find(accessorkey, string_view_hash(), std::equal_to<string_view>());
|
||||
#else
|
||||
std::string accessorkey(accessor.data(), accessor.size());
|
||||
auto fit = functions.find(accessorkey);
|
||||
#endif // Compatible Hash
|
||||
if (fit != functions.cend()) {
|
||||
object& func = fit->second;
|
||||
if (is_index) {
|
||||
return stack::push(L, func);
|
||||
}
|
||||
else {
|
||||
function_failed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (function_failed) {
|
||||
if (has_indexing && !is_toplevel(L)) {
|
||||
object& indexingfunc = is_index
|
||||
? sm.index
|
||||
: sm.newindex;
|
||||
return call_indexing_object(L, indexingfunc);
|
||||
}
|
||||
else {
|
||||
return is_index
|
||||
? indexing_fail<T, is_index>(L)
|
||||
: metatable_new_index<T, true>(L);
|
||||
}
|
||||
}
|
||||
/* Check table storage first for a method that works
|
||||
luaL_getmetatable(L, sm.metakey);
|
||||
if (type_of(L, -1) != type::lua_nil) {
|
||||
stack::get_field<false, true>(L, accessor.c_str(), lua_gettop(L));
|
||||
if (type_of(L, -1) != type::lua_nil) {
|
||||
// Woo, we found it?
|
||||
lua_remove(L, -2);
|
||||
return 1;
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
*/
|
||||
|
||||
int ret = 0;
|
||||
bool found = false;
|
||||
// Otherwise, we need to do propagating calls through the bases
|
||||
if (is_index) {
|
||||
sm.indexbaseclasspropogation(L, found, ret, accessor);
|
||||
}
|
||||
else {
|
||||
sm.newindexbaseclasspropogation(L, found, ret, accessor);
|
||||
}
|
||||
if (found) {
|
||||
return ret;
|
||||
}
|
||||
if (toplevel) {
|
||||
if (has_indexing && !is_toplevel(L)) {
|
||||
object& indexingfunc = is_index
|
||||
? sm.index
|
||||
: sm.newindex;
|
||||
return call_indexing_object(L, indexingfunc);
|
||||
}
|
||||
else {
|
||||
return is_index
|
||||
? indexing_fail<T, is_index>(L)
|
||||
: metatable_new_index<T, true>(L);
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
template <typename T, bool has_indexing = false>
|
||||
inline int simple_real_index_call(lua_State* L) {
|
||||
return simple_core_indexing_call<T, true, true, has_indexing>(L);
|
||||
}
|
||||
|
||||
template <typename T, bool has_indexing = false>
|
||||
inline int simple_real_new_index_call(lua_State* L) {
|
||||
return simple_core_indexing_call<T, false, true, has_indexing>(L);
|
||||
}
|
||||
|
||||
template <typename T, bool has_indexing = false>
|
||||
inline int simple_index_call(lua_State* L) {
|
||||
#if defined(__clang__)
|
||||
return detail::trampoline(L, &simple_real_index_call<T, has_indexing>);
|
||||
#else
|
||||
return detail::typed_static_trampoline<decltype(&simple_real_index_call<T, has_indexing>), (&simple_real_index_call<T, has_indexing>)>(L);
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename T, bool has_indexing = false>
|
||||
inline int simple_new_index_call(lua_State* L) {
|
||||
#if defined(__clang__)
|
||||
return detail::trampoline(L, &simple_real_new_index_call<T, has_indexing>);
|
||||
#else
|
||||
return detail::typed_static_trampoline<decltype(&simple_real_new_index_call<T, has_indexing>), (&simple_real_new_index_call<T, has_indexing>)>(L);
|
||||
#endif
|
||||
}
|
||||
} // namespace u_detail
|
||||
|
||||
struct simple_tag {
|
||||
} const simple {};
|
||||
|
||||
template <typename T>
|
||||
struct simple_usertype_metatable : u_detail::registrar {
|
||||
public:
|
||||
u_detail::function_map registrations;
|
||||
u_detail::variable_map varmap;
|
||||
object callconstructfunc;
|
||||
object indexfunc;
|
||||
object newindexfunc;
|
||||
lua_CFunction indexbase;
|
||||
lua_CFunction newindexbase;
|
||||
u_detail::base_walk indexbaseclasspropogation;
|
||||
u_detail::base_walk newindexbaseclasspropogation;
|
||||
void* baseclasscheck;
|
||||
void* baseclasscast;
|
||||
bool mustindex;
|
||||
bool secondarymeta;
|
||||
std::array<bool, 32> properties;
|
||||
|
||||
template <typename N>
|
||||
void insert(N&& n, object&& o) {
|
||||
std::string key = u_detail::make_string(std::forward<N>(n));
|
||||
int is_indexer = static_cast<int>(u_detail::is_indexer(n));
|
||||
if (is_indexer == 1) {
|
||||
indexfunc = o;
|
||||
mustindex = true;
|
||||
}
|
||||
else if (is_indexer == 2) {
|
||||
newindexfunc = o;
|
||||
mustindex = true;
|
||||
}
|
||||
auto hint = registrations.find(key);
|
||||
if (hint == registrations.cend()) {
|
||||
registrations.emplace_hint(hint, std::move(key), std::move(o));
|
||||
return;
|
||||
}
|
||||
hint->second = std::move(o);
|
||||
}
|
||||
|
||||
template <typename N, typename F, typename... Args>
|
||||
void insert_prepare(std::true_type, lua_State* L, N&&, F&& f, Args&&... args) {
|
||||
object o = make_object<F>(L, std::forward<F>(f), function_detail::call_indicator(), std::forward<Args>(args)...);
|
||||
callconstructfunc = std::move(o);
|
||||
}
|
||||
|
||||
template <typename N, typename F, typename... Args>
|
||||
void insert_prepare(std::false_type, lua_State* L, N&& n, F&& f, Args&&... args) {
|
||||
object o = make_object<F>(L, std::forward<F>(f), std::forward<Args>(args)...);
|
||||
insert(std::forward<N>(n), std::move(o));
|
||||
}
|
||||
|
||||
template <typename N, typename F>
|
||||
void add_member_function(std::true_type, lua_State* L, N&& n, F&& f) {
|
||||
insert_prepare(std::is_same<meta::unqualified_t<N>, call_construction>(), L, std::forward<N>(n), std::forward<F>(f), function_detail::class_indicator<T>());
|
||||
}
|
||||
|
||||
template <typename N, typename F>
|
||||
void add_member_function(std::false_type, lua_State* L, N&& n, F&& f) {
|
||||
insert_prepare(std::is_same<meta::unqualified_t<N>, call_construction>(), L, std::forward<N>(n), std::forward<F>(f));
|
||||
}
|
||||
|
||||
template <typename N, typename F, meta::enable<meta::is_callable<meta::unwrap_unqualified_t<F>>> = meta::enabler>
|
||||
void add_function(lua_State* L, N&& n, F&& f) {
|
||||
object o = make_object(L, as_function_reference(std::forward<F>(f)));
|
||||
if (std::is_same<meta::unqualified_t<N>, call_construction>::value) {
|
||||
callconstructfunc = std::move(o);
|
||||
return;
|
||||
}
|
||||
insert(std::forward<N>(n), std::move(o));
|
||||
}
|
||||
|
||||
template <typename N, typename F, meta::disable<meta::is_callable<meta::unwrap_unqualified_t<F>>> = meta::enabler>
|
||||
void add_function(lua_State* L, N&& n, F&& f) {
|
||||
add_member_function(std::is_member_pointer<meta::unwrap_unqualified_t<F>>(), L, std::forward<N>(n), std::forward<F>(f));
|
||||
}
|
||||
|
||||
template <typename N, typename F, meta::disable<is_variable_binding<meta::unqualified_t<F>>> = meta::enabler>
|
||||
void add(lua_State* L, N&& n, F&& f) {
|
||||
add_function(L, std::forward<N>(n), std::forward<F>(f));
|
||||
}
|
||||
|
||||
template <typename N, typename F, meta::enable<is_variable_binding<meta::unqualified_t<F>>> = meta::enabler>
|
||||
void add(lua_State*, N&& n, F&& f) {
|
||||
mustindex = true;
|
||||
secondarymeta = true;
|
||||
std::string key = u_detail::make_string(std::forward<N>(n));
|
||||
auto o = std::make_unique<u_detail::callable_binding<T, std::decay_t<F>>>(std::forward<F>(f));
|
||||
auto hint = varmap.find(key);
|
||||
if (hint == varmap.cend()) {
|
||||
varmap.emplace_hint(hint, std::move(key), std::move(o));
|
||||
return;
|
||||
}
|
||||
hint->second = std::move(o);
|
||||
}
|
||||
|
||||
template <typename N, typename... Fxs>
|
||||
void add(lua_State* L, N&& n, constructor_wrapper<Fxs...> c) {
|
||||
object o(L, in_place_type<detail::tagged<T, constructor_wrapper<Fxs...>>>, std::move(c));
|
||||
if (std::is_same<meta::unqualified_t<N>, call_construction>::value) {
|
||||
callconstructfunc = std::move(o);
|
||||
return;
|
||||
}
|
||||
insert(std::forward<N>(n), std::move(o));
|
||||
}
|
||||
|
||||
template <typename N, typename... Lists>
|
||||
void add(lua_State* L, N&& n, constructor_list<Lists...> c) {
|
||||
object o(L, in_place_type<detail::tagged<T, constructor_list<Lists...>>>, std::move(c));
|
||||
if (std::is_same<meta::unqualified_t<N>, call_construction>::value) {
|
||||
callconstructfunc = std::move(o);
|
||||
return;
|
||||
}
|
||||
insert(std::forward<N>(n), std::move(o));
|
||||
}
|
||||
|
||||
template <typename N>
|
||||
void add(lua_State* L, N&& n, destructor_wrapper<void> c) {
|
||||
object o(L, in_place_type<detail::tagged<T, destructor_wrapper<void>>>, std::move(c));
|
||||
if (std::is_same<meta::unqualified_t<N>, call_construction>::value) {
|
||||
callconstructfunc = std::move(o);
|
||||
return;
|
||||
}
|
||||
insert(std::forward<N>(n), std::move(o));
|
||||
}
|
||||
|
||||
template <typename N, typename Fx>
|
||||
void add(lua_State* L, N&& n, destructor_wrapper<Fx> c) {
|
||||
object o(L, in_place_type<detail::tagged<T, destructor_wrapper<Fx>>>, std::move(c));
|
||||
if (std::is_same<meta::unqualified_t<N>, call_construction>::value) {
|
||||
callconstructfunc = std::move(o);
|
||||
return;
|
||||
}
|
||||
insert(std::forward<N>(n), std::move(o));
|
||||
}
|
||||
|
||||
template <typename... Bases>
|
||||
void add(lua_State*, base_classes_tag, bases<Bases...>) {
|
||||
static_assert(sizeof(u_detail::base_walk) <= sizeof(void*), "size of function pointer is greater than sizeof(void*); cannot work on this platform. Please file a bug report.");
|
||||
static_assert(!meta::any_same<T, Bases...>::value, "base classes cannot list the original class as part of the bases");
|
||||
if (sizeof...(Bases) < 1) {
|
||||
return;
|
||||
}
|
||||
mustindex = true;
|
||||
//(void)detail::swallow{0, ((detail::has_derived<Bases>::value = true), 0)...};
|
||||
|
||||
static_assert(sizeof(void*) <= sizeof(detail::inheritance_check_function), "The size of this data pointer is too small to fit the inheritance checking function: Please file a bug report.");
|
||||
static_assert(sizeof(void*) <= sizeof(detail::inheritance_cast_function), "The size of this data pointer is too small to fit the inheritance checking function: Please file a bug report.");
|
||||
baseclasscheck = reinterpret_cast<void*>(&detail::inheritance<T, Bases...>::type_check);
|
||||
baseclasscast = reinterpret_cast<void*>(&detail::inheritance<T, Bases...>::type_cast);
|
||||
indexbaseclasspropogation = u_detail::walk_all_bases<true, Bases...>;
|
||||
newindexbaseclasspropogation = u_detail::walk_all_bases<false, Bases...>;
|
||||
}
|
||||
|
||||
private:
|
||||
template <std::size_t... I, typename Tuple>
|
||||
simple_usertype_metatable(detail::verified_tag, std::index_sequence<I...>, lua_State* L, Tuple&& args)
|
||||
: callconstructfunc(lua_nil), indexfunc(lua_nil), newindexfunc(lua_nil), indexbase(&u_detail::simple_core_indexing_call<T, true>), newindexbase(&u_detail::simple_core_indexing_call<T, false>), indexbaseclasspropogation(u_detail::walk_all_bases<true>), newindexbaseclasspropogation(&u_detail::walk_all_bases<false>), baseclasscheck(nullptr), baseclasscast(nullptr), mustindex(false), secondarymeta(false), properties() {
|
||||
properties.fill(false);
|
||||
|
||||
(void)detail::swallow { 0,
|
||||
(add(L, detail::forward_get<I * 2>(args), detail::forward_get<I * 2 + 1>(args)), 0)... };
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
simple_usertype_metatable(lua_State* L, detail::verified_tag v, Args&&... args)
|
||||
: simple_usertype_metatable(v, std::make_index_sequence<sizeof...(Args) / 2>(), L, std::forward_as_tuple(std::forward<Args>(args)...)) {
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
simple_usertype_metatable(lua_State* L, detail::add_destructor_tag, Args&&... args)
|
||||
: simple_usertype_metatable(L, detail::verified, std::forward<Args>(args)..., "__gc", default_destructor) {
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
simple_usertype_metatable(lua_State* L, detail::check_destructor_tag, Args&&... args)
|
||||
: simple_usertype_metatable(L, meta::condition<meta::all<std::is_destructible<T>, meta::neg<detail::has_destructor<Args...>>>, detail::add_destructor_tag, detail::verified_tag>(), std::forward<Args>(args)...) {
|
||||
}
|
||||
|
||||
public:
|
||||
simple_usertype_metatable(lua_State* L)
|
||||
: simple_usertype_metatable(L, meta::condition<meta::all<std::is_default_constructible<T>>, decltype(default_constructor), detail::check_destructor_tag>()) {
|
||||
}
|
||||
|
||||
template <typename Arg, typename... Args, meta::disable_any<meta::any_same<meta::unqualified_t<Arg>, detail::verified_tag, detail::add_destructor_tag, detail::check_destructor_tag>, meta::is_specialization_of<meta::unqualified_t<Arg>, constructors>, meta::is_specialization_of<meta::unqualified_t<Arg>, constructor_wrapper>> = meta::enabler>
|
||||
simple_usertype_metatable(lua_State* L, Arg&& arg, Args&&... args)
|
||||
: simple_usertype_metatable(L, meta::condition<meta::all<std::is_default_constructible<T>, meta::neg<detail::has_constructor<Args...>>>, decltype(default_constructor), detail::check_destructor_tag>(), std::forward<Arg>(arg), std::forward<Args>(args)...) {
|
||||
}
|
||||
|
||||
template <typename... Args, typename... CArgs>
|
||||
simple_usertype_metatable(lua_State* L, constructors<CArgs...> constructorlist, Args&&... args)
|
||||
: simple_usertype_metatable(L, detail::check_destructor_tag(), std::forward<Args>(args)..., "new", constructorlist) {
|
||||
}
|
||||
|
||||
template <typename... Args, typename... Fxs>
|
||||
simple_usertype_metatable(lua_State* L, constructor_wrapper<Fxs...> constructorlist, Args&&... args)
|
||||
: simple_usertype_metatable(L, detail::check_destructor_tag(), std::forward<Args>(args)..., "new", constructorlist) {
|
||||
}
|
||||
|
||||
simple_usertype_metatable(const simple_usertype_metatable&) = default;
|
||||
simple_usertype_metatable(simple_usertype_metatable&&) = default;
|
||||
simple_usertype_metatable& operator=(const simple_usertype_metatable&) = default;
|
||||
simple_usertype_metatable& operator=(simple_usertype_metatable&&) = default;
|
||||
|
||||
virtual int push_um(lua_State* L) override {
|
||||
return stack::push(L, std::move(*this));
|
||||
}
|
||||
};
|
||||
|
||||
namespace stack {
|
||||
template <typename T>
|
||||
struct pusher<simple_usertype_metatable<T>> {
|
||||
typedef simple_usertype_metatable<T> umt_t;
|
||||
|
||||
static u_detail::simple_map& make_cleanup(lua_State* L, umt_t& umx) {
|
||||
static int uniqueness = 0;
|
||||
std::string uniquegcmetakey = usertype_traits<T>::user_gc_metatable();
|
||||
// std::to_string doesn't exist in android still, with NDK, so this bullshit
|
||||
// is necessary
|
||||
// thanks, Android :v
|
||||
int appended = snprintf(nullptr, 0, "%d", uniqueness);
|
||||
std::size_t insertionpoint = uniquegcmetakey.length() - 1;
|
||||
uniquegcmetakey.append(appended, '\0');
|
||||
char* uniquetarget = &uniquegcmetakey[insertionpoint];
|
||||
snprintf(uniquetarget, uniquegcmetakey.length(), "%d", uniqueness);
|
||||
++uniqueness;
|
||||
|
||||
const char* gcmetakey = &usertype_traits<T>::gc_table()[0];
|
||||
stack::push<user<u_detail::simple_map>>(L, metatable_key, uniquegcmetakey, &usertype_traits<T>::metatable()[0],
|
||||
umx.indexbaseclasspropogation, umx.newindexbaseclasspropogation,
|
||||
std::move(umx.indexfunc), std::move(umx.newindexfunc),
|
||||
std::move(umx.varmap), std::move(umx.registrations));
|
||||
stack_reference stackvarmap(L, -1);
|
||||
stack::set_field<true>(L, gcmetakey, stackvarmap);
|
||||
stackvarmap.pop();
|
||||
|
||||
stack::get_field<true>(L, gcmetakey);
|
||||
u_detail::simple_map& varmap = stack::pop<user<u_detail::simple_map>>(L);
|
||||
return varmap;
|
||||
}
|
||||
|
||||
static int push(lua_State* L, umt_t&& umx) {
|
||||
bool hasindex = umx.indexfunc.valid();
|
||||
bool hasnewindex = umx.newindexfunc.valid();
|
||||
auto& varmap = make_cleanup(L, umx);
|
||||
auto& properties = umx.properties;
|
||||
auto sic = hasindex ? &u_detail::simple_index_call<T, true> : &u_detail::simple_index_call<T, false>;
|
||||
auto snic = hasnewindex ? &u_detail::simple_new_index_call<T, true> : &u_detail::simple_new_index_call<T, false>;
|
||||
|
||||
lua_createtable(L, 0, 2);
|
||||
stack_reference type_table(L, -1);
|
||||
|
||||
stack::set_field(L, "name", detail::demangle<T>(), type_table.stack_index());
|
||||
stack::set_field(L, "is", &u_detail::is_check<T>, type_table.stack_index());
|
||||
|
||||
auto safety_check = [&](const std::string& first) {
|
||||
for (std::size_t j = 0; j < properties.size(); ++j) {
|
||||
meta_function mf = static_cast<meta_function>(j);
|
||||
const std::string& mfname = to_string(mf);
|
||||
bool& prop = properties[j];
|
||||
if (mfname != first)
|
||||
continue;
|
||||
switch (mf) {
|
||||
case meta_function::construct:
|
||||
if (prop) {
|
||||
#if defined(SOL_NO_EXCEPTIONS) && SOL_NO_EXCEPTIONS
|
||||
assert(false && "sol: 2 separate constructor (new) functions were set on this type. Please specify only 1 sol::meta_function::construct/'new' type AND wrap the function in a sol::factories/initializers call, as shown by the documentation and examples, otherwise you may create problems");
|
||||
#else
|
||||
throw error("sol: 2 separate constructor (new) functions were set on this type. Please specify only 1 sol::meta_function::construct/'new' type AND wrap the function in a sol::factories/initializers call, as shown by the documentation and examples, otherwise you may create problems");
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
case meta_function::garbage_collect:
|
||||
if (prop) {
|
||||
#if defined(SOL_NO_EXCEPTIONS) && SOL_NO_EXCEPTIONS
|
||||
assert(false && "sol: 2 separate constructor (new) functions were set on this type. Please specify only 1 sol::meta_function::construct/'new' type AND wrap the function in a sol::factories/initializers call, as shown by the documentation and examples, otherwise you may create problems");
|
||||
#else
|
||||
throw error("sol: 2 separate constructor (new) functions were set on this type. Please specify only 1 sol::meta_function::construct/'new' type AND wrap the function in a sol::factories/initializers call, as shown by the documentation and examples, otherwise you may create problems");
|
||||
#endif
|
||||
}
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
prop = true;
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
for (auto& kvp : varmap.functions) {
|
||||
auto& first = std::get<0>(kvp);
|
||||
safety_check(first);
|
||||
}
|
||||
|
||||
auto register_kvp = [&](std::size_t meta_index, stack_reference& t, const std::string& first, object& second) {
|
||||
meta_function mf = meta_function::construct;
|
||||
for (std::size_t j = 0; j < properties.size(); ++j) {
|
||||
mf = static_cast<meta_function>(j);
|
||||
const std::string& mfname = to_string(mf);
|
||||
bool& prop = properties[j];
|
||||
if (mfname != first)
|
||||
continue;
|
||||
switch (mf) {
|
||||
case meta_function::index:
|
||||
umx.indexfunc = second;
|
||||
break;
|
||||
case meta_function::new_index:
|
||||
umx.newindexfunc = second;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
prop = true;
|
||||
break;
|
||||
}
|
||||
switch (meta_index) {
|
||||
case 0:
|
||||
if (mf == meta_function::garbage_collect) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
if (mf == meta_function::garbage_collect) {
|
||||
stack::set_field(L, first, detail::unique_destruct<T>, t.stack_index());
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
stack::set_field(L, first, second, t.stack_index());
|
||||
};
|
||||
for (std::size_t i = 0; i < 3; ++i) {
|
||||
const char* metakey = nullptr;
|
||||
switch (i) {
|
||||
case 0:
|
||||
metakey = &usertype_traits<T*>::metatable()[0];
|
||||
break;
|
||||
case 1:
|
||||
metakey = &usertype_traits<detail::unique_usertype<T>>::metatable()[0];
|
||||
break;
|
||||
case 2:
|
||||
default:
|
||||
metakey = &usertype_traits<T>::metatable()[0];
|
||||
break;
|
||||
}
|
||||
luaL_newmetatable(L, metakey);
|
||||
stack_reference t(L, -1);
|
||||
stack::set_field(L, meta_function::type, type_table, t.stack_index());
|
||||
|
||||
for (auto& kvp : varmap.functions) {
|
||||
auto& first = std::get<0>(kvp);
|
||||
auto& second = std::get<1>(kvp);
|
||||
register_kvp(i, t, first, second);
|
||||
}
|
||||
luaL_Reg opregs[34] {};
|
||||
int opregsindex = 0;
|
||||
auto prop_fx = [&](meta_function mf) { return !properties[static_cast<int>(mf)]; };
|
||||
u_detail::insert_default_registrations<T>(opregs, opregsindex, prop_fx);
|
||||
t.push();
|
||||
luaL_setfuncs(L, opregs, 0);
|
||||
t.pop();
|
||||
|
||||
if (umx.baseclasscheck != nullptr) {
|
||||
stack::set_field(L, detail::base_class_check_key(), umx.baseclasscheck, t.stack_index());
|
||||
}
|
||||
if (umx.baseclasscast != nullptr) {
|
||||
stack::set_field(L, detail::base_class_cast_key(), umx.baseclasscast, t.stack_index());
|
||||
}
|
||||
|
||||
// Base class propagation features
|
||||
stack::set_field(L, detail::base_class_index_propogation_key(), umx.indexbase, t.stack_index());
|
||||
stack::set_field(L, detail::base_class_new_index_propogation_key(), umx.newindexbase, t.stack_index());
|
||||
|
||||
if (umx.mustindex) {
|
||||
// use indexing function
|
||||
stack::set_field(L, meta_function::index,
|
||||
make_closure(sic,
|
||||
nullptr,
|
||||
make_light(varmap)),
|
||||
t.stack_index());
|
||||
stack::set_field(L, meta_function::new_index,
|
||||
make_closure(snic,
|
||||
nullptr,
|
||||
make_light(varmap)),
|
||||
t.stack_index());
|
||||
}
|
||||
else {
|
||||
// Metatable indexes itself
|
||||
stack::set_field(L, meta_function::index, t, t.stack_index());
|
||||
}
|
||||
// metatable on the metatable
|
||||
// for call constructor purposes and such
|
||||
lua_createtable(L, 0, 2 * static_cast<int>(umx.secondarymeta) + static_cast<int>(umx.callconstructfunc.valid()));
|
||||
stack_reference metabehind(L, -1);
|
||||
stack::set_field(L, meta_function::type, type_table, metabehind.stack_index());
|
||||
if (umx.callconstructfunc.valid()) {
|
||||
stack::set_field(L, meta_function::call_function, umx.callconstructfunc, metabehind.stack_index());
|
||||
}
|
||||
if (umx.secondarymeta) {
|
||||
stack::set_field(L, meta_function::index,
|
||||
make_closure(sic,
|
||||
nullptr,
|
||||
make_light(varmap)),
|
||||
metabehind.stack_index());
|
||||
stack::set_field(L, meta_function::new_index,
|
||||
make_closure(snic,
|
||||
nullptr,
|
||||
make_light(varmap)),
|
||||
metabehind.stack_index());
|
||||
}
|
||||
stack::set_field(L, metatable_key, metabehind, t.stack_index());
|
||||
metabehind.pop();
|
||||
|
||||
t.pop();
|
||||
}
|
||||
|
||||
// Now for the shim-table that actually gets pushed
|
||||
luaL_newmetatable(L, &usertype_traits<T>::user_metatable()[0]);
|
||||
stack_reference t(L, -1);
|
||||
stack::set_field(L, meta_function::type, type_table, t.stack_index());
|
||||
|
||||
for (auto& kvp : varmap.functions) {
|
||||
auto& first = std::get<0>(kvp);
|
||||
auto& second = std::get<1>(kvp);
|
||||
register_kvp(2, t, first, second);
|
||||
}
|
||||
{
|
||||
lua_createtable(L, 0, 2 + static_cast<int>(umx.callconstructfunc.valid()));
|
||||
stack_reference metabehind(L, -1);
|
||||
stack::set_field(L, meta_function::type, type_table, metabehind.stack_index());
|
||||
if (umx.callconstructfunc.valid()) {
|
||||
stack::set_field(L, meta_function::call_function, umx.callconstructfunc, metabehind.stack_index());
|
||||
}
|
||||
// use indexing function
|
||||
stack::set_field(L, meta_function::index,
|
||||
make_closure(sic,
|
||||
nullptr,
|
||||
make_light(varmap),
|
||||
nullptr,
|
||||
nullptr,
|
||||
u_detail::toplevel_magic),
|
||||
metabehind.stack_index());
|
||||
stack::set_field(L, meta_function::new_index,
|
||||
make_closure(snic,
|
||||
nullptr,
|
||||
make_light(varmap),
|
||||
nullptr,
|
||||
nullptr,
|
||||
u_detail::toplevel_magic),
|
||||
metabehind.stack_index());
|
||||
stack::set_field(L, metatable_key, metabehind, t.stack_index());
|
||||
metabehind.pop();
|
||||
}
|
||||
|
||||
lua_remove(L, type_table.stack_index());
|
||||
|
||||
// Don't pop the table when we're done;
|
||||
// return it
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
} // namespace stack
|
||||
} // namespace sol
|
||||
|
||||
#endif // SOL_SIMPLE_USERTYPE_METATABLE_HPP
|
444
include/sol/usertype_container.hpp
Normal file
444
include/sol/usertype_container.hpp
Normal file
|
@ -0,0 +1,444 @@
|
|||
// sol2
|
||||
|
||||
// The MIT License (MIT)
|
||||
|
||||
// Copyright (c) 2013-2018 Rapptz, ThePhD 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_USERTYPE_CONTAINER_HPP
|
||||
#define SOL_USERTYPE_CONTAINER_HPP
|
||||
|
||||
#include "stack.hpp"
|
||||
#include "container_traits.hpp"
|
||||
#include <unordered_map>
|
||||
|
||||
namespace sol {
|
||||
|
||||
template <typename X>
|
||||
struct usertype_container {
|
||||
typedef std::remove_pointer_t<meta::unqualified_t<X>> T;
|
||||
typedef container_traits<T> traits;
|
||||
typedef container_detail::container_traits_default<T> default_traits;
|
||||
|
||||
static int real_index_get_traits(std::true_type, lua_State* L) {
|
||||
return traits::index_get(L);
|
||||
}
|
||||
|
||||
static int real_index_get_traits(std::false_type, lua_State* L) {
|
||||
return default_traits::index_get(L);
|
||||
}
|
||||
|
||||
static int real_index_call(lua_State* L) {
|
||||
typedef u_detail::map_t<std::string, lua_CFunction> call_map;
|
||||
static const call_map calls {
|
||||
{ "at", &at_call },
|
||||
{ "get", &real_get_call },
|
||||
{ "set", &real_set_call },
|
||||
{ "size", &real_length_call },
|
||||
{ "add", &real_add_call },
|
||||
{ "empty", &real_empty_call },
|
||||
{ "insert", &real_insert_call },
|
||||
{ "clear", &real_clear_call },
|
||||
{ "find", &real_find_call },
|
||||
{ "erase", &real_erase_call },
|
||||
{ "pairs", &pairs_call },
|
||||
{ "next", &next_call },
|
||||
};
|
||||
auto maybenameview = stack::unqualified_check_get<string_view>(L, 2);
|
||||
if (maybenameview) {
|
||||
const string_view& nameview = *maybenameview;
|
||||
#if defined(SOL_UNORDERED_MAP_COMPATIBLE_HASH) && SOL_UNORDERED_MAP_COMPATIBLE_HASH
|
||||
auto it = calls.find(nameview, string_view_hash(), std::equal_to<string_view>());
|
||||
#else
|
||||
std::string name(nameview.data(), nameview.size());
|
||||
auto it = calls.find(name);
|
||||
#endif
|
||||
if (it != calls.cend()) {
|
||||
return stack::push(L, it->second);
|
||||
}
|
||||
}
|
||||
return real_index_get_traits(container_detail::has_traits_index_get<traits>(), L);
|
||||
}
|
||||
|
||||
static int real_at_traits(std::true_type, lua_State* L) {
|
||||
return traits::at(L);
|
||||
}
|
||||
|
||||
static int real_at_traits(std::false_type, lua_State* L) {
|
||||
return default_traits::at(L);
|
||||
}
|
||||
|
||||
static int real_at_call(lua_State* L) {
|
||||
return real_at_traits(container_detail::has_traits_at<traits>(), L);
|
||||
}
|
||||
|
||||
static int real_get_traits(std::true_type, lua_State* L) {
|
||||
return traits::get(L);
|
||||
}
|
||||
|
||||
static int real_get_traits(std::false_type, lua_State* L) {
|
||||
return default_traits::get(L);
|
||||
}
|
||||
|
||||
static int real_get_call(lua_State* L) {
|
||||
return real_get_traits(container_detail::has_traits_get<traits>(), L);
|
||||
}
|
||||
|
||||
static int real_set_traits(std::true_type, lua_State* L) {
|
||||
return traits::set(L);
|
||||
}
|
||||
|
||||
static int real_set_traits(std::false_type, lua_State* L) {
|
||||
return default_traits::set(L);
|
||||
}
|
||||
|
||||
static int real_set_call(lua_State* L) {
|
||||
return real_set_traits(container_detail::has_traits_set<traits>(), L);
|
||||
}
|
||||
|
||||
static int real_index_set_traits(std::true_type, lua_State* L) {
|
||||
return traits::index_set(L);
|
||||
}
|
||||
|
||||
static int real_index_set_traits(std::false_type, lua_State* L) {
|
||||
return default_traits::index_set(L);
|
||||
}
|
||||
|
||||
static int real_new_index_call(lua_State* L) {
|
||||
return real_index_set_traits(container_detail::has_traits_index_set<traits>(), L);
|
||||
}
|
||||
|
||||
static int real_pairs_traits(std::true_type, lua_State* L) {
|
||||
return traits::pairs(L);
|
||||
}
|
||||
|
||||
static int real_pairs_traits(std::false_type, lua_State* L) {
|
||||
return default_traits::pairs(L);
|
||||
}
|
||||
|
||||
static int real_pairs_call(lua_State* L) {
|
||||
return real_pairs_traits(container_detail::has_traits_pairs<traits>(), L);
|
||||
}
|
||||
|
||||
static int real_ipairs_traits(std::true_type, lua_State* L) {
|
||||
return traits::ipairs(L);
|
||||
}
|
||||
|
||||
static int real_ipairs_traits(std::false_type, lua_State* L) {
|
||||
return default_traits::ipairs(L);
|
||||
}
|
||||
|
||||
static int real_ipairs_call(lua_State* L) {
|
||||
return real_ipairs_traits(container_detail::has_traits_ipairs<traits>(), L);
|
||||
}
|
||||
|
||||
static int real_next_traits(std::true_type, lua_State* L) {
|
||||
return traits::next(L);
|
||||
}
|
||||
|
||||
static int real_next_traits(std::false_type, lua_State* L) {
|
||||
return default_traits::next(L);
|
||||
}
|
||||
|
||||
static int real_next_call(lua_State* L) {
|
||||
return real_next_traits(container_detail::has_traits_next<traits>(), L);
|
||||
}
|
||||
|
||||
static int real_size_traits(std::true_type, lua_State* L) {
|
||||
return traits::size(L);
|
||||
}
|
||||
|
||||
static int real_size_traits(std::false_type, lua_State* L) {
|
||||
return default_traits::size(L);
|
||||
}
|
||||
|
||||
static int real_length_call(lua_State* L) {
|
||||
return real_size_traits(container_detail::has_traits_size<traits>(), L);
|
||||
}
|
||||
|
||||
static int real_add_traits(std::true_type, lua_State* L) {
|
||||
return traits::add(L);
|
||||
}
|
||||
|
||||
static int real_add_traits(std::false_type, lua_State* L) {
|
||||
return default_traits::add(L);
|
||||
}
|
||||
|
||||
static int real_add_call(lua_State* L) {
|
||||
return real_add_traits(container_detail::has_traits_add<traits>(), L);
|
||||
}
|
||||
|
||||
static int real_insert_traits(std::true_type, lua_State* L) {
|
||||
return traits::insert(L);
|
||||
}
|
||||
|
||||
static int real_insert_traits(std::false_type, lua_State* L) {
|
||||
return default_traits::insert(L);
|
||||
}
|
||||
|
||||
static int real_insert_call(lua_State* L) {
|
||||
return real_insert_traits(container_detail::has_traits_insert<traits>(), L);
|
||||
}
|
||||
|
||||
static int real_clear_traits(std::true_type, lua_State* L) {
|
||||
return traits::clear(L);
|
||||
}
|
||||
|
||||
static int real_clear_traits(std::false_type, lua_State* L) {
|
||||
return default_traits::clear(L);
|
||||
}
|
||||
|
||||
static int real_clear_call(lua_State* L) {
|
||||
return real_clear_traits(container_detail::has_traits_clear<traits>(), L);
|
||||
}
|
||||
|
||||
static int real_empty_traits(std::true_type, lua_State* L) {
|
||||
return traits::empty(L);
|
||||
}
|
||||
|
||||
static int real_empty_traits(std::false_type, lua_State* L) {
|
||||
return default_traits::empty(L);
|
||||
}
|
||||
|
||||
static int real_empty_call(lua_State* L) {
|
||||
return real_empty_traits(container_detail::has_traits_empty<traits>(), L);
|
||||
}
|
||||
|
||||
static int real_erase_traits(std::true_type, lua_State* L) {
|
||||
return traits::erase(L);
|
||||
}
|
||||
|
||||
static int real_erase_traits(std::false_type, lua_State* L) {
|
||||
return default_traits::erase(L);
|
||||
}
|
||||
|
||||
static int real_erase_call(lua_State* L) {
|
||||
return real_erase_traits(container_detail::has_traits_erase<traits>(), L);
|
||||
}
|
||||
|
||||
static int real_find_traits(std::true_type, lua_State* L) {
|
||||
return traits::find(L);
|
||||
}
|
||||
|
||||
static int real_find_traits(std::false_type, lua_State* L) {
|
||||
return default_traits::find(L);
|
||||
}
|
||||
|
||||
static int real_find_call(lua_State* L) {
|
||||
return real_find_traits(container_detail::has_traits_find<traits>(), L);
|
||||
}
|
||||
|
||||
static int add_call(lua_State* L) {
|
||||
return detail::typed_static_trampoline<decltype(&real_add_call), (&real_add_call)>(L);
|
||||
}
|
||||
|
||||
static int erase_call(lua_State* L) {
|
||||
return detail::typed_static_trampoline<decltype(&real_erase_call), (&real_erase_call)>(L);
|
||||
}
|
||||
|
||||
static int insert_call(lua_State* L) {
|
||||
return detail::typed_static_trampoline<decltype(&real_insert_call), (&real_insert_call)>(L);
|
||||
}
|
||||
|
||||
static int clear_call(lua_State* L) {
|
||||
return detail::typed_static_trampoline<decltype(&real_clear_call), (&real_clear_call)>(L);
|
||||
}
|
||||
|
||||
static int empty_call(lua_State* L) {
|
||||
return detail::typed_static_trampoline<decltype(&real_empty_call), (&real_empty_call)>(L);
|
||||
}
|
||||
|
||||
static int find_call(lua_State* L) {
|
||||
return detail::typed_static_trampoline<decltype(&real_find_call), (&real_find_call)>(L);
|
||||
}
|
||||
|
||||
static int length_call(lua_State* L) {
|
||||
return detail::typed_static_trampoline<decltype(&real_length_call), (&real_length_call)>(L);
|
||||
}
|
||||
|
||||
static int pairs_call(lua_State* L) {
|
||||
return detail::typed_static_trampoline<decltype(&real_pairs_call), (&real_pairs_call)>(L);
|
||||
}
|
||||
|
||||
static int ipairs_call(lua_State* L) {
|
||||
return detail::typed_static_trampoline<decltype(&real_ipairs_call), (&real_ipairs_call)>(L);
|
||||
}
|
||||
|
||||
static int next_call(lua_State* L) {
|
||||
return detail::typed_static_trampoline<decltype(&real_next_call), (&real_next_call)>(L);
|
||||
}
|
||||
|
||||
static int at_call(lua_State* L) {
|
||||
return detail::typed_static_trampoline<decltype(&real_at_call), (&real_at_call)>(L);
|
||||
}
|
||||
|
||||
static int get_call(lua_State* L) {
|
||||
return detail::typed_static_trampoline<decltype(&real_get_call), (&real_get_call)>(L);
|
||||
}
|
||||
|
||||
static int set_call(lua_State* L) {
|
||||
return detail::typed_static_trampoline<decltype(&real_set_call), (&real_set_call)>(L);
|
||||
}
|
||||
|
||||
static int index_call(lua_State* L) {
|
||||
return detail::typed_static_trampoline<decltype(&real_index_call), (&real_index_call)>(L);
|
||||
}
|
||||
|
||||
static int new_index_call(lua_State* L) {
|
||||
return detail::typed_static_trampoline<decltype(&real_new_index_call), (&real_new_index_call)>(L);
|
||||
}
|
||||
};
|
||||
|
||||
namespace stack {
|
||||
namespace stack_detail {
|
||||
template <typename T, bool is_shim = false>
|
||||
struct metatable_setup {
|
||||
lua_State* L;
|
||||
|
||||
metatable_setup(lua_State* L)
|
||||
: L(L) {
|
||||
}
|
||||
|
||||
void operator()() {
|
||||
typedef usertype_container<std::conditional_t<is_shim,
|
||||
as_container_t<std::remove_pointer_t<T>>,
|
||||
std::remove_pointer_t<T>>>
|
||||
meta_cumt;
|
||||
static const char* metakey = is_shim ? &usertype_traits<as_container_t<std::remove_pointer_t<T>>>::metatable()[0] : &usertype_traits<T>::metatable()[0];
|
||||
static const std::array<luaL_Reg, 19> reg = { { { "__pairs", &meta_cumt::pairs_call },
|
||||
{ "__ipairs", &meta_cumt::ipairs_call },
|
||||
{ "__len", &meta_cumt::length_call },
|
||||
{ "__index", &meta_cumt::index_call },
|
||||
{ "__newindex", &meta_cumt::new_index_call },
|
||||
{ "pairs", &meta_cumt::pairs_call },
|
||||
{ "next", &meta_cumt::next_call },
|
||||
{ "at", &meta_cumt::at_call },
|
||||
{ "get", &meta_cumt::get_call },
|
||||
{ "set", &meta_cumt::set_call },
|
||||
{ "size", &meta_cumt::length_call },
|
||||
{ "empty", &meta_cumt::empty_call },
|
||||
{ "clear", &meta_cumt::clear_call },
|
||||
{ "insert", &meta_cumt::insert_call },
|
||||
{ "add", &meta_cumt::add_call },
|
||||
{ "find", &meta_cumt::find_call },
|
||||
{ "erase", &meta_cumt::erase_call },
|
||||
std::is_pointer<T>::value ? luaL_Reg { nullptr, nullptr } : luaL_Reg { "__gc", &detail::usertype_alloc_destruct<T> },
|
||||
{ nullptr, nullptr } } };
|
||||
|
||||
if (luaL_newmetatable(L, metakey) == 1) {
|
||||
luaL_setfuncs(L, reg.data(), 0);
|
||||
}
|
||||
lua_setmetatable(L, -2);
|
||||
}
|
||||
};
|
||||
} // namespace stack_detail
|
||||
|
||||
template <typename T>
|
||||
struct pusher<as_container_t<T>> {
|
||||
typedef meta::unqualified_t<T> C;
|
||||
|
||||
static int push_lvalue(std::true_type, lua_State* L, const C& cont) {
|
||||
stack_detail::metatable_setup<C*, true> fx(L);
|
||||
return pusher<detail::as_pointer_tag<const C>> {}.push_fx(L, fx, detail::ptr(cont));
|
||||
}
|
||||
|
||||
static int push_lvalue(std::false_type, lua_State* L, const C& cont) {
|
||||
stack_detail::metatable_setup<C, true> fx(L);
|
||||
return pusher<detail::as_value_tag<C>> {}.push_fx(L, fx, cont);
|
||||
}
|
||||
|
||||
static int push_rvalue(std::true_type, lua_State* L, C&& cont) {
|
||||
stack_detail::metatable_setup<C, true> fx(L);
|
||||
return pusher<detail::as_value_tag<C>> {}.push_fx(L, fx, std::move(cont));
|
||||
}
|
||||
|
||||
static int push_rvalue(std::false_type, lua_State* L, const C& cont) {
|
||||
return push_lvalue(std::is_lvalue_reference<T>(), L, cont);
|
||||
}
|
||||
|
||||
static int push(lua_State* L, const as_container_t<T>& as_cont) {
|
||||
return push_lvalue(std::is_lvalue_reference<T>(), L, as_cont.source);
|
||||
}
|
||||
|
||||
static int push(lua_State* L, as_container_t<T>&& as_cont) {
|
||||
return push_rvalue(meta::all<std::is_rvalue_reference<T>, meta::neg<std::is_lvalue_reference<T>>>(), L, std::forward<T>(as_cont.source));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct pusher<as_container_t<T*>> {
|
||||
typedef std::add_pointer_t<meta::unqualified_t<std::remove_pointer_t<T>>> C;
|
||||
|
||||
static int push(lua_State* L, T* cont) {
|
||||
stack_detail::metatable_setup<C> fx(L);
|
||||
return pusher<detail::as_pointer_tag<T>> {}.push_fx(L, fx, cont);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct pusher<T, std::enable_if_t<meta::all<is_container<meta::unqualified_t<T>>, meta::neg<is_lua_reference<meta::unqualified_t<T>>>>::value>> {
|
||||
typedef meta::unqualified_t<T> C;
|
||||
|
||||
static int push(lua_State* L, const T& cont) {
|
||||
stack_detail::metatable_setup<C> fx(L);
|
||||
return pusher<detail::as_value_tag<T>> {}.push_fx(L, fx, cont);
|
||||
}
|
||||
|
||||
static int push(lua_State* L, T&& cont) {
|
||||
stack_detail::metatable_setup<C> fx(L);
|
||||
return pusher<detail::as_value_tag<T>> {}.push_fx(L, fx, std::move(cont));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct pusher<T*, std::enable_if_t<meta::all<is_container<meta::unqualified_t<T>>, meta::neg<is_lua_reference<meta::unqualified_t<T>>>>::value>> {
|
||||
typedef std::add_pointer_t<meta::unqualified_t<std::remove_pointer_t<T>>> C;
|
||||
|
||||
static int push(lua_State* L, T* cont) {
|
||||
stack_detail::metatable_setup<C> fx(L);
|
||||
return pusher<detail::as_pointer_tag<T>> {}.push_fx(L, fx, cont);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename C>
|
||||
struct checker<as_container_t<T>, type::userdata, C> {
|
||||
template <typename Handler>
|
||||
static bool check(lua_State* L, int index, Handler&& handler, record& tracking) {
|
||||
return stack::check<T>(L, index, std::forward<Handler>(handler), tracking);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct getter<as_container_t<T>> {
|
||||
static decltype(auto) get(lua_State* L, int index, record& tracking) {
|
||||
return stack::unqualified_get<T>(L, index, tracking);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct getter<as_container_t<T>*> {
|
||||
static decltype(auto) get(lua_State* L, int index, record& tracking) {
|
||||
return stack::unqualified_get<T*>(L, index, tracking);
|
||||
}
|
||||
};
|
||||
} // namespace stack
|
||||
|
||||
} // namespace sol
|
||||
|
||||
#endif // SOL_USERTYPE_CONTAINER_HPP
|
|
@ -1,841 +0,0 @@
|
|||
// sol2
|
||||
|
||||
// The MIT License (MIT)
|
||||
|
||||
// Copyright (c) 2013-2018 Rapptz, ThePhD 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_USERTYPE_METATABLE_HPP
|
||||
#define SOL_USERTYPE_METATABLE_HPP
|
||||
|
||||
#include "wrapper.hpp"
|
||||
#include "call.hpp"
|
||||
#include "stack.hpp"
|
||||
#include "types.hpp"
|
||||
#include "stack_reference.hpp"
|
||||
#include "usertype_traits.hpp"
|
||||
#include "inheritance.hpp"
|
||||
#include "raii.hpp"
|
||||
#include "deprecate.hpp"
|
||||
#include "object.hpp"
|
||||
#include "container_usertype_metatable.hpp"
|
||||
#include "usertype_core.hpp"
|
||||
#include "map.hpp"
|
||||
#include <unordered_map>
|
||||
#include <cstdio>
|
||||
#include <sstream>
|
||||
#include <cassert>
|
||||
#include <bitset>
|
||||
|
||||
namespace sol {
|
||||
namespace u_detail {
|
||||
const int metatable_index = 2;
|
||||
const int metatable_core_index = 3;
|
||||
const int filler_index = 4;
|
||||
const int magic_index = 5;
|
||||
|
||||
const int simple_metatable_index = 2;
|
||||
const int index_function_index = 3;
|
||||
const int newindex_function_index = 4;
|
||||
|
||||
typedef void (*base_walk)(lua_State*, bool&, int&, string_view&);
|
||||
typedef int (*member_search)(lua_State*, void*, int);
|
||||
|
||||
struct call_information {
|
||||
member_search index;
|
||||
member_search new_index;
|
||||
int runtime_target;
|
||||
|
||||
call_information(member_search index, member_search newindex)
|
||||
: call_information(index, newindex, -1) {
|
||||
}
|
||||
call_information(member_search index, member_search newindex, int runtimetarget)
|
||||
: index(index), new_index(newindex), runtime_target(runtimetarget) {
|
||||
}
|
||||
};
|
||||
|
||||
typedef detail::unordered_map<std::string, call_information> mapping_t;
|
||||
|
||||
struct variable_wrapper {
|
||||
virtual int index(lua_State* L) = 0;
|
||||
virtual int new_index(lua_State* L) = 0;
|
||||
virtual ~variable_wrapper() {};
|
||||
};
|
||||
|
||||
template <typename T, typename F>
|
||||
struct callable_binding : variable_wrapper {
|
||||
F fx_;
|
||||
|
||||
template <typename Arg>
|
||||
callable_binding(Arg&& arg)
|
||||
: fx_(std::forward<Arg>(arg)) {
|
||||
}
|
||||
|
||||
virtual int index(lua_State* L) override {
|
||||
return call_detail::call_wrapped<T, true, true>(L, fx_);
|
||||
}
|
||||
|
||||
virtual int new_index(lua_State* L) override {
|
||||
return call_detail::call_wrapped<T, false, true>(L, fx_);
|
||||
}
|
||||
};
|
||||
|
||||
typedef detail::unordered_map<std::string, std::unique_ptr<variable_wrapper>> variable_map;
|
||||
typedef detail::unordered_map<std::string, object> function_map;
|
||||
|
||||
struct simple_map {
|
||||
const char* metakey;
|
||||
variable_map variables;
|
||||
function_map functions;
|
||||
object index;
|
||||
object newindex;
|
||||
base_walk indexbaseclasspropogation;
|
||||
base_walk newindexbaseclasspropogation;
|
||||
|
||||
simple_map(const char* mkey, base_walk index, base_walk newindex, object i, object ni, variable_map&& vars, function_map&& funcs)
|
||||
: metakey(mkey), variables(std::move(vars)), functions(std::move(funcs)), index(std::move(i)), newindex(std::move(ni)), indexbaseclasspropogation(index), newindexbaseclasspropogation(newindex) {
|
||||
}
|
||||
};
|
||||
} // namespace u_detail
|
||||
|
||||
struct usertype_metatable_core {
|
||||
u_detail::mapping_t mapping;
|
||||
lua_CFunction indexfunc;
|
||||
lua_CFunction newindexfunc;
|
||||
std::vector<object> runtime;
|
||||
bool mustindex;
|
||||
|
||||
usertype_metatable_core(lua_CFunction ifx, lua_CFunction nifx)
|
||||
: mapping(), indexfunc(ifx), newindexfunc(nifx), runtime(), mustindex(false) {
|
||||
}
|
||||
|
||||
usertype_metatable_core(const usertype_metatable_core&) = default;
|
||||
usertype_metatable_core(usertype_metatable_core&&) = default;
|
||||
usertype_metatable_core& operator=(const usertype_metatable_core&) = default;
|
||||
usertype_metatable_core& operator=(usertype_metatable_core&&) = default;
|
||||
};
|
||||
|
||||
namespace u_detail {
|
||||
constexpr const lua_Integer toplevel_magic = static_cast<lua_Integer>(0xCCC2CCC1);
|
||||
|
||||
inline int is_indexer(string_view s) {
|
||||
if (s == to_string(meta_function::index)) {
|
||||
return 1;
|
||||
}
|
||||
else if (s == to_string(meta_function::new_index)) {
|
||||
return 2;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
inline int is_indexer(meta_function mf) {
|
||||
if (mf == meta_function::index) {
|
||||
return 1;
|
||||
}
|
||||
else if (mf == meta_function::new_index) {
|
||||
return 2;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
inline int is_indexer(call_construction) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
inline int is_indexer(base_classes_tag) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
inline auto make_string_view(string_view s) {
|
||||
return s;
|
||||
}
|
||||
|
||||
inline auto make_string_view(call_construction) {
|
||||
return string_view(to_string(meta_function::call_function));
|
||||
}
|
||||
|
||||
inline auto make_string_view(meta_function mf) {
|
||||
return string_view(to_string(mf));
|
||||
}
|
||||
|
||||
inline auto make_string_view(base_classes_tag) {
|
||||
return string_view(detail::base_class_cast_key());
|
||||
}
|
||||
|
||||
template <typename Arg>
|
||||
inline std::string make_string(Arg&& arg) {
|
||||
string_view s = make_string_view(arg);
|
||||
return std::string(s.data(), s.size());
|
||||
}
|
||||
|
||||
template <typename N>
|
||||
inline luaL_Reg make_reg(N&& n, lua_CFunction f) {
|
||||
luaL_Reg l { make_string_view(std::forward<N>(n)).data(), f };
|
||||
return l;
|
||||
}
|
||||
|
||||
struct registrar {
|
||||
registrar() = default;
|
||||
registrar(const registrar&) = default;
|
||||
registrar(registrar&&) = default;
|
||||
registrar& operator=(const registrar&) = default;
|
||||
registrar& operator=(registrar&&) = default;
|
||||
virtual int push_um(lua_State* L) = 0;
|
||||
virtual ~registrar() {
|
||||
}
|
||||
};
|
||||
|
||||
inline bool is_toplevel(lua_State* L, int index = magic_index) {
|
||||
int isnum = 0;
|
||||
lua_Integer magic = lua_tointegerx(L, upvalue_index(index), &isnum);
|
||||
return isnum != 0 && magic == toplevel_magic;
|
||||
}
|
||||
|
||||
inline int runtime_object_call(lua_State* L, void*, int runtimetarget) {
|
||||
usertype_metatable_core& umc = stack::get<light<usertype_metatable_core>>(L, upvalue_index(metatable_core_index));
|
||||
std::vector<object>& runtime = umc.runtime;
|
||||
object& runtimeobj = runtime[runtimetarget];
|
||||
return stack::push(L, runtimeobj);
|
||||
}
|
||||
|
||||
template <typename T, bool is_index>
|
||||
inline int indexing_fail(lua_State* L) {
|
||||
if (is_index) {
|
||||
#if 0 //defined(SOL_SAFE_USERTYPE) && SOL_SAFE_USERTYPE
|
||||
auto maybeaccessor = stack::get<optional<string_view>>(L, is_index ? -1 : -2);
|
||||
string_view accessor = maybeaccessor.value_or(string_detail::string_shim("(unknown)"));
|
||||
return luaL_error(L, "sol: attempt to index (get) nil value \"%s\" on userdata (bad (misspelled?) key name or does not exist)", accessor.data());
|
||||
#else
|
||||
if (is_toplevel(L)) {
|
||||
if (lua_getmetatable(L, 1) == 1) {
|
||||
int metatarget = lua_gettop(L);
|
||||
stack::get_field(L, stack_reference(L, raw_index(2)), metatarget);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
// With runtime extensibility, we can't hard-error things. They have to return nil, like regular table types, unfortunately...
|
||||
return stack::push(L, lua_nil);
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
auto maybeaccessor = stack::get<optional<string_view>>(L, is_index ? -1 : -2);
|
||||
string_view accessor = maybeaccessor.value_or(string_view("(unknown)"));
|
||||
return luaL_error(L, "sol: attempt to index (set) nil value \"%s\" on userdata (bad (misspelled?) key name or does not exist)", accessor.data());
|
||||
}
|
||||
}
|
||||
|
||||
int runtime_new_index(lua_State* L, void*, int runtimetarget);
|
||||
|
||||
template <typename T, bool is_simple>
|
||||
inline int metatable_new_index(lua_State* L) {
|
||||
if (is_toplevel(L)) {
|
||||
auto non_indexable = [&L]() {
|
||||
if (is_simple) {
|
||||
simple_map& sm = stack::get<user<simple_map>>(L, upvalue_index(simple_metatable_index));
|
||||
function_map& functions = sm.functions;
|
||||
optional<string_view> maybeaccessor = stack::get<optional<string_view>>(L, 2);
|
||||
if (!maybeaccessor) {
|
||||
return;
|
||||
}
|
||||
string_view& accessor_view = maybeaccessor.value();
|
||||
#if defined(SOL_UNORDERED_MAP_COMPATIBLE_HASH) && SOL_UNORDERED_MAP_COMPATIBLE_HASH
|
||||
auto preexistingit = functions.find(accessor_view, string_view_hash(), std::equal_to<string_view>());
|
||||
#else
|
||||
std::string accessor(accessor_view.data(), accessor_view.size());
|
||||
auto preexistingit = functions.find(accessor);
|
||||
#endif
|
||||
if (preexistingit == functions.cend()) {
|
||||
#if defined(SOL_UNORDERED_MAP_COMPATIBLE_HASH) && SOL_UNORDERED_MAP_COMPATIBLE_HASH
|
||||
std::string accessor(accessor_view.data(), accessor_view.size());
|
||||
#endif
|
||||
functions.emplace_hint(preexistingit, std::move(accessor), object(L, 3));
|
||||
}
|
||||
else {
|
||||
preexistingit->second = object(L, 3);
|
||||
}
|
||||
return;
|
||||
}
|
||||
usertype_metatable_core& umc = stack::get<light<usertype_metatable_core>>(L, upvalue_index(metatable_core_index));
|
||||
bool mustindex = umc.mustindex;
|
||||
if (!mustindex)
|
||||
return;
|
||||
optional<string_view> maybeaccessor = stack::get<optional<string_view>>(L, 2);
|
||||
if (!maybeaccessor) {
|
||||
return;
|
||||
}
|
||||
string_view& accessor_view = maybeaccessor.value();
|
||||
mapping_t& mapping = umc.mapping;
|
||||
std::vector<object>& runtime = umc.runtime;
|
||||
int target = static_cast<int>(runtime.size());
|
||||
#if defined(SOL_UNORDERED_MAP_COMPATIBLE_HASH) && SOL_UNORDERED_MAP_COMPATIBLE_HASH
|
||||
auto preexistingit = mapping.find(accessor_view, string_view_hash(), std::equal_to<string_view>());
|
||||
#else
|
||||
std::string accessor(accessor_view.data(), accessor_view.size());
|
||||
auto preexistingit = mapping.find(accessor);
|
||||
#endif
|
||||
if (preexistingit == mapping.cend()) {
|
||||
#if defined(SOL_UNORDERED_MAP_COMPATIBLE_HASH) && SOL_UNORDERED_MAP_COMPATIBLE_HASH
|
||||
std::string accessor(accessor_view.data(), accessor_view.size());
|
||||
#endif
|
||||
runtime.emplace_back(L, 3);
|
||||
mapping.emplace_hint(mapping.cend(), std::move(accessor), call_information(&runtime_object_call, &runtime_new_index, target));
|
||||
}
|
||||
else {
|
||||
target = preexistingit->second.runtime_target;
|
||||
runtime[target] = object(L, 3);
|
||||
preexistingit->second = call_information(&runtime_object_call, &runtime_new_index, target);
|
||||
}
|
||||
};
|
||||
non_indexable();
|
||||
for (std::size_t i = 0; i < 4; lua_settop(L, 3), ++i) {
|
||||
const char* metakey = nullptr;
|
||||
switch (i) {
|
||||
case 0:
|
||||
metakey = &usertype_traits<T*>::metatable()[0];
|
||||
luaL_getmetatable(L, metakey);
|
||||
break;
|
||||
case 1:
|
||||
metakey = &usertype_traits<detail::unique_usertype<T>>::metatable()[0];
|
||||
luaL_getmetatable(L, metakey);
|
||||
break;
|
||||
case 2:
|
||||
metakey = &usertype_traits<T>::metatable()[0];
|
||||
luaL_getmetatable(L, metakey);
|
||||
break;
|
||||
case 3:
|
||||
default:
|
||||
metakey = &usertype_traits<T>::user_metatable()[0];
|
||||
{
|
||||
luaL_getmetatable(L, metakey);
|
||||
lua_getmetatable(L, -1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
int tableindex = lua_gettop(L);
|
||||
if (type_of(L, tableindex) == type::lua_nil) {
|
||||
continue;
|
||||
}
|
||||
stack::set_field<false, true>(L, stack_reference(L, raw_index(2)), stack_reference(L, raw_index(3)), tableindex);
|
||||
}
|
||||
lua_settop(L, 0);
|
||||
return 0;
|
||||
}
|
||||
return indexing_fail<T, false>(L);
|
||||
}
|
||||
|
||||
inline int runtime_new_index(lua_State* L, void*, int runtimetarget) {
|
||||
usertype_metatable_core& umc = stack::get<light<usertype_metatable_core>>(L, upvalue_index(metatable_core_index));
|
||||
std::vector<object>& runtime = umc.runtime;
|
||||
object& runtimeobj = runtime[runtimetarget];
|
||||
runtimeobj = object(L, 3);
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <bool is_index, typename Base>
|
||||
static void walk_single_base(lua_State* L, bool& found, int& ret, string_view&) {
|
||||
if (found)
|
||||
return;
|
||||
const char* metakey = &usertype_traits<Base>::metatable()[0];
|
||||
const char* gcmetakey = &usertype_traits<Base>::gc_table()[0];
|
||||
const char* basewalkkey = is_index ? detail::base_class_index_propogation_key() : detail::base_class_new_index_propogation_key();
|
||||
|
||||
luaL_getmetatable(L, metakey);
|
||||
if (type_of(L, -1) == type::lua_nil) {
|
||||
lua_pop(L, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
stack::get_field(L, basewalkkey);
|
||||
if (type_of(L, -1) == type::lua_nil) {
|
||||
lua_pop(L, 2);
|
||||
return;
|
||||
}
|
||||
lua_CFunction basewalkfunc = stack::pop<lua_CFunction>(L);
|
||||
lua_pop(L, 1);
|
||||
|
||||
stack::get_field<true>(L, gcmetakey);
|
||||
int value = basewalkfunc(L);
|
||||
if (value > -1) {
|
||||
found = true;
|
||||
ret = value;
|
||||
}
|
||||
}
|
||||
|
||||
template <bool is_index, typename... Bases>
|
||||
static void walk_all_bases(lua_State* L, bool& found, int& ret, string_view& accessor) {
|
||||
(void)L;
|
||||
(void)found;
|
||||
(void)ret;
|
||||
(void)accessor;
|
||||
(void)detail::swallow { 0, (walk_single_base<is_index, Bases>(L, found, ret, accessor), 0)... };
|
||||
}
|
||||
} // namespace u_detail
|
||||
|
||||
template <typename T>
|
||||
struct clean_type {
|
||||
typedef std::conditional_t<std::is_array<meta::unqualified_t<T>>::value, T&, std::decay_t<T>> type;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
using clean_type_t = typename clean_type<T>::type;
|
||||
|
||||
template <typename T, typename IndexSequence, typename... Tn>
|
||||
struct usertype_metatable : u_detail::registrar {};
|
||||
|
||||
template <typename T, std::size_t... I, typename... Tn>
|
||||
struct usertype_metatable<T, std::index_sequence<I...>, Tn...> : usertype_metatable_core, u_detail::registrar {
|
||||
typedef std::make_index_sequence<sizeof...(I) * 2> indices;
|
||||
typedef std::index_sequence<I...> half_indices;
|
||||
typedef std::array<luaL_Reg, sizeof...(Tn) / 2 + 1 + 31> regs_t;
|
||||
typedef std::tuple<Tn...> RawTuple;
|
||||
typedef std::tuple<clean_type_t<Tn>...> Tuple;
|
||||
template <std::size_t Idx>
|
||||
struct check_binding : is_variable_binding<meta::unqualified_tuple_element_t<Idx, Tuple>> {};
|
||||
Tuple functions;
|
||||
lua_CFunction destructfunc;
|
||||
lua_CFunction callconstructfunc;
|
||||
lua_CFunction indexbase;
|
||||
lua_CFunction newindexbase;
|
||||
u_detail::base_walk indexbaseclasspropogation;
|
||||
u_detail::base_walk newindexbaseclasspropogation;
|
||||
void* baseclasscheck;
|
||||
void* baseclasscast;
|
||||
bool secondarymeta;
|
||||
std::bitset<32> properties;
|
||||
|
||||
template <std::size_t Idx, meta::enable<std::is_same<lua_CFunction, meta::unqualified_tuple_element<Idx + 1, RawTuple>>> = meta::enabler>
|
||||
lua_CFunction make_func() const {
|
||||
return std::get<Idx + 1>(functions);
|
||||
}
|
||||
|
||||
template <std::size_t Idx, meta::disable<std::is_same<lua_CFunction, meta::unqualified_tuple_element<Idx + 1, RawTuple>>> = meta::enabler>
|
||||
lua_CFunction make_func() const {
|
||||
const auto& name = std::get<Idx>(functions);
|
||||
return (u_detail::make_string_view(name) == "__newindex") ? &call<Idx + 1, false> : &call<Idx + 1, true>;
|
||||
}
|
||||
|
||||
static bool contains_variable() {
|
||||
typedef meta::any<check_binding<(I * 2 + 1)>...> has_variables;
|
||||
return has_variables::value;
|
||||
}
|
||||
|
||||
bool contains_index() const {
|
||||
bool idx = false;
|
||||
(void)detail::swallow { 0, ((idx |= (u_detail::is_indexer(std::get<I * 2>(functions)) != 0)), 0)... };
|
||||
return idx;
|
||||
}
|
||||
|
||||
int finish_regs(regs_t& l, int& index) {
|
||||
auto prop_fx = [&](meta_function mf) { return !properties[static_cast<int>(mf)]; };
|
||||
u_detail::insert_default_registrations<T>(l, index, prop_fx);
|
||||
if (destructfunc != nullptr) {
|
||||
l[index] = luaL_Reg { to_string(meta_function::garbage_collect).c_str(), destructfunc };
|
||||
++index;
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
template <std::size_t Idx, typename F>
|
||||
void make_regs(regs_t&, int&, call_construction, F&&) {
|
||||
callconstructfunc = call<Idx + 1>;
|
||||
secondarymeta = true;
|
||||
}
|
||||
|
||||
template <std::size_t, typename... Bases>
|
||||
void make_regs(regs_t&, int&, base_classes_tag, bases<Bases...>) {
|
||||
static_assert(!meta::any_same<T, Bases...>::value, "base classes cannot list the original class as part of the bases");
|
||||
if (sizeof...(Bases) < 1) {
|
||||
return;
|
||||
}
|
||||
mustindex = true;
|
||||
|
||||
static_assert(sizeof(void*) <= sizeof(detail::inheritance_check_function), "The size of this data pointer is too small to fit the inheritance checking function: file a bug report.");
|
||||
static_assert(sizeof(void*) <= sizeof(detail::inheritance_cast_function), "The size of this data pointer is too small to fit the inheritance checking function: file a bug report.");
|
||||
baseclasscheck = (void*)&detail::inheritance<T, Bases...>::type_check;
|
||||
baseclasscast = (void*)&detail::inheritance<T, Bases...>::type_cast;
|
||||
indexbaseclasspropogation = u_detail::walk_all_bases<true, Bases...>;
|
||||
newindexbaseclasspropogation = u_detail::walk_all_bases<false, Bases...>;
|
||||
}
|
||||
|
||||
template <std::size_t Idx, typename N, typename F, typename = std::enable_if_t<!meta::any_same<meta::unqualified_t<N>, base_classes_tag, call_construction>::value>>
|
||||
void make_regs(regs_t& l, int& index, N&& n, F&&) {
|
||||
if (is_variable_binding<meta::unqualified_t<F>>::value) {
|
||||
return;
|
||||
}
|
||||
luaL_Reg reg = u_detail::make_reg(std::forward<N>(n), make_func<Idx>());
|
||||
for (std::size_t i = 0; i < properties.size(); ++i) {
|
||||
meta_function mf = static_cast<meta_function>(i);
|
||||
const std::string& mfname = to_string(mf);
|
||||
if (mfname == reg.name) {
|
||||
switch (mf) {
|
||||
case meta_function::construct:
|
||||
if (properties[i]) {
|
||||
#if !(defined(SOL_NO_EXCEPTIONS) && SOL_NO_EXCEPTIONS)
|
||||
throw error("sol: 2 separate constructor (new) functions were set on this type. Please specify only 1 sol::meta_function::construct/'new' type AND wrap the function in a sol::factories/initializers call, as shown by the documentation and examples, otherwise you may create problems");
|
||||
#else
|
||||
assert(false && "sol: 2 separate constructor (new) functions were set on this type. Please specify only 1 sol::meta_function::construct/'new' type AND wrap the function in a sol::factories/initializers call, as shown by the documentation and examples, otherwise you may create problems");
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
case meta_function::garbage_collect:
|
||||
if (destructfunc != nullptr) {
|
||||
#if !(defined(SOL_NO_EXCEPTIONS) && SOL_NO_EXCEPTIONS)
|
||||
throw error("sol: 2 separate constructor (new) functions were set on this type. Please specify only 1 sol::meta_function::construct/'new' type AND wrap the function in a sol::factories/initializers call, as shown by the documentation and examples, otherwise you may create problems");
|
||||
#else
|
||||
assert(false && "sol: 2 separate constructor (new) functions were set on this type. Please specify only 1 sol::meta_function::construct/'new' type AND wrap the function in a sol::factories/initializers call, as shown by the documentation and examples, otherwise you may create problems");
|
||||
#endif
|
||||
}
|
||||
destructfunc = reg.func;
|
||||
return;
|
||||
case meta_function::index:
|
||||
indexfunc = reg.func;
|
||||
mustindex = true;
|
||||
properties.set(i);
|
||||
return;
|
||||
case meta_function::new_index:
|
||||
newindexfunc = reg.func;
|
||||
mustindex = true;
|
||||
properties.set(i);
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
properties.set(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
l[index] = reg;
|
||||
++index;
|
||||
}
|
||||
|
||||
template <typename... Args, typename = std::enable_if_t<sizeof...(Args) == sizeof...(Tn)>>
|
||||
usertype_metatable(Args&&... args)
|
||||
: usertype_metatable_core(&u_detail::indexing_fail<T, true>, &u_detail::metatable_new_index<T, false>), u_detail::registrar(), functions(std::forward<Args>(args)...), destructfunc(nullptr), callconstructfunc(nullptr), indexbase(&core_indexing_call<true>), newindexbase(&core_indexing_call<false>), indexbaseclasspropogation(u_detail::walk_all_bases<true>), newindexbaseclasspropogation(u_detail::walk_all_bases<false>), baseclasscheck(nullptr), baseclasscast(nullptr), secondarymeta(contains_variable()), properties() {
|
||||
properties.reset();
|
||||
std::initializer_list<typename u_detail::mapping_t::value_type> ilist { { std::pair<std::string, u_detail::call_information>(u_detail::make_string(std::get<I * 2>(functions)),
|
||||
u_detail::call_information(&usertype_metatable::real_find_call<I * 2, I * 2 + 1, true>,
|
||||
&usertype_metatable::real_find_call<I * 2, I * 2 + 1, false>)) }... };
|
||||
this->mapping.insert(ilist);
|
||||
for (const auto& n : meta_function_names()) {
|
||||
this->mapping.erase(n);
|
||||
}
|
||||
this->mustindex = contains_variable() || contains_index();
|
||||
}
|
||||
|
||||
usertype_metatable(const usertype_metatable&) = default;
|
||||
usertype_metatable(usertype_metatable&&) = default;
|
||||
usertype_metatable& operator=(const usertype_metatable&) = default;
|
||||
usertype_metatable& operator=(usertype_metatable&&) = default;
|
||||
|
||||
template <std::size_t I0, std::size_t I1, bool is_index>
|
||||
static int real_find_call(lua_State* L, void* um, int) {
|
||||
auto& f = *static_cast<usertype_metatable*>(um);
|
||||
if (is_variable_binding<decltype(std::get<I1>(f.functions))>::value) {
|
||||
return real_call_with<I1, is_index, true>(L, f);
|
||||
}
|
||||
// set up upvalues
|
||||
// for a chained call
|
||||
int upvalues = 0;
|
||||
upvalues += stack::push(L, nullptr);
|
||||
upvalues += stack::push(L, light<usertype_metatable>(f));
|
||||
auto cfunc = &call<I1, is_index>;
|
||||
return stack::push(L, c_closure(cfunc, upvalues));
|
||||
}
|
||||
|
||||
template <bool is_index>
|
||||
static int real_meta_call(lua_State* L, void* um, int) {
|
||||
auto& f = *static_cast<usertype_metatable*>(um);
|
||||
return is_index ? f.indexfunc(L) : f.newindexfunc(L);
|
||||
}
|
||||
|
||||
template <bool is_index, bool toplevel = false, bool is_meta_bound = false>
|
||||
static int core_indexing_call(lua_State* L) {
|
||||
usertype_metatable& f = toplevel
|
||||
? static_cast<usertype_metatable&>(stack::get<light<usertype_metatable>>(L, upvalue_index(u_detail::metatable_index)))
|
||||
: static_cast<usertype_metatable&>(stack::pop<user<usertype_metatable>>(L));
|
||||
static const int keyidx = -2 + static_cast<int>(is_index);
|
||||
if (toplevel && stack::get<type>(L, keyidx) != type::string) {
|
||||
return is_index ? f.indexfunc(L) : f.newindexfunc(L);
|
||||
}
|
||||
int runtime_target = 0;
|
||||
u_detail::member_search member = nullptr;
|
||||
{
|
||||
#if defined(SOL_UNORDERED_MAP_COMPATIBLE_HASH) && SOL_UNORDERED_MAP_COMPATIBLE_HASH
|
||||
string_view name = stack::get<string_view>(L, keyidx);
|
||||
auto memberit = f.mapping.find(name, string_view_hash(), std::equal_to<string_view>());
|
||||
#else
|
||||
std::string name = stack::get<std::string>(L, keyidx);
|
||||
auto memberit = f.mapping.find(name);
|
||||
#endif
|
||||
if (memberit != f.mapping.cend()) {
|
||||
const u_detail::call_information& ci = memberit->second;
|
||||
member = is_index ? ci.index : ci.new_index;
|
||||
runtime_target = ci.runtime_target;
|
||||
}
|
||||
}
|
||||
if (member != nullptr) {
|
||||
return (member)(L, static_cast<void*>(&f), runtime_target);
|
||||
}
|
||||
string_view accessor = stack::get<string_view>(L, keyidx);
|
||||
int ret = 0;
|
||||
bool found = false;
|
||||
// Otherwise, we need to do propagating calls through the bases
|
||||
if (is_index)
|
||||
f.indexbaseclasspropogation(L, found, ret, accessor);
|
||||
else
|
||||
f.newindexbaseclasspropogation(L, found, ret, accessor);
|
||||
if (found) {
|
||||
return ret;
|
||||
}
|
||||
if (is_meta_bound) {
|
||||
return is_index ? u_detail::indexing_fail<T, is_index>(L) : u_detail::metatable_new_index<T, false>(L);
|
||||
}
|
||||
return toplevel ? (is_index ? f.indexfunc(L) : f.newindexfunc(L)) : -1;
|
||||
}
|
||||
|
||||
static int real_index_call(lua_State* L) {
|
||||
return core_indexing_call<true, true>(L);
|
||||
}
|
||||
|
||||
static int real_new_index_call(lua_State* L) {
|
||||
return core_indexing_call<false, true>(L);
|
||||
}
|
||||
|
||||
static int real_meta_index_call(lua_State* L) {
|
||||
return core_indexing_call<true, true, true>(L);
|
||||
}
|
||||
|
||||
static int real_meta_new_index_call(lua_State* L) {
|
||||
return core_indexing_call<false, true, true>(L);
|
||||
}
|
||||
|
||||
template <std::size_t Idx, bool is_index = true, bool is_variable = false>
|
||||
static int real_call(lua_State* L) {
|
||||
usertype_metatable& f = stack::get<light<usertype_metatable>>(L, upvalue_index(u_detail::metatable_index));
|
||||
return real_call_with<Idx, is_index, is_variable>(L, f);
|
||||
}
|
||||
|
||||
template <std::size_t Idx, bool is_index = true, bool is_variable = false>
|
||||
static int real_call_with(lua_State* L, usertype_metatable& um) {
|
||||
typedef meta::unqualified_tuple_element_t<Idx - 1, Tuple> K;
|
||||
typedef meta::unqualified_tuple_element_t<Idx, Tuple> F;
|
||||
static const int boost = !detail::is_non_factory_constructor<F>::value
|
||||
&& std::is_same<K, call_construction>::value
|
||||
? 1
|
||||
: 0;
|
||||
auto& f = std::get<Idx>(um.functions);
|
||||
return call_detail::call_wrapped<T, is_index, is_variable, boost>(L, f);
|
||||
}
|
||||
|
||||
template <std::size_t Idx, bool is_index = true, bool is_variable = false>
|
||||
static int call(lua_State* L) {
|
||||
return detail::typed_static_trampoline<decltype(&real_call<Idx, is_index, is_variable>), (&real_call<Idx, is_index, is_variable>)>(L);
|
||||
}
|
||||
|
||||
template <std::size_t Idx, bool is_index = true, bool is_variable = false>
|
||||
static int call_with(lua_State* L) {
|
||||
return detail::typed_static_trampoline<decltype(&real_call_with<Idx, is_index, is_variable>), (&real_call_with<Idx, is_index, is_variable>)>(L);
|
||||
}
|
||||
|
||||
static int index_call(lua_State* L) {
|
||||
return detail::typed_static_trampoline<decltype(&real_index_call), (&real_index_call)>(L);
|
||||
}
|
||||
|
||||
static int new_index_call(lua_State* L) {
|
||||
return detail::typed_static_trampoline<decltype(&real_new_index_call), (&real_new_index_call)>(L);
|
||||
}
|
||||
|
||||
static int meta_index_call(lua_State* L) {
|
||||
return detail::typed_static_trampoline<decltype(&real_meta_index_call), (&real_meta_index_call)>(L);
|
||||
}
|
||||
|
||||
static int meta_new_index_call(lua_State* L) {
|
||||
return detail::typed_static_trampoline<decltype(&real_meta_new_index_call), (&real_meta_new_index_call)>(L);
|
||||
}
|
||||
|
||||
virtual int push_um(lua_State* L) override {
|
||||
return stack::push(L, std::move(*this));
|
||||
}
|
||||
|
||||
~usertype_metatable() override {
|
||||
}
|
||||
};
|
||||
|
||||
namespace stack {
|
||||
|
||||
template <typename T, std::size_t... I, typename... Args>
|
||||
struct pusher<usertype_metatable<T, std::index_sequence<I...>, Args...>> {
|
||||
typedef usertype_metatable<T, std::index_sequence<I...>, Args...> umt_t;
|
||||
typedef typename umt_t::regs_t regs_t;
|
||||
|
||||
static umt_t& make_cleanup(lua_State* L, umt_t&& umx) {
|
||||
// ensure some sort of uniqueness
|
||||
static int uniqueness = 0;
|
||||
std::string uniquegcmetakey = usertype_traits<T>::user_gc_metatable();
|
||||
// std::to_string doesn't exist in android still, with NDK, so this bullshit
|
||||
// is necessary
|
||||
// thanks, Android :v
|
||||
int appended = snprintf(nullptr, 0, "%d", uniqueness);
|
||||
std::size_t insertionpoint = uniquegcmetakey.length() - 1;
|
||||
uniquegcmetakey.append(appended, '\0');
|
||||
char* uniquetarget = &uniquegcmetakey[insertionpoint];
|
||||
snprintf(uniquetarget, uniquegcmetakey.length(), "%d", uniqueness);
|
||||
++uniqueness;
|
||||
|
||||
const char* gcmetakey = &usertype_traits<T>::gc_table()[0];
|
||||
// Make sure userdata's memory is properly in lua first,
|
||||
// otherwise all the light userdata we make later will become invalid
|
||||
stack::push<user<umt_t>>(L, metatable_key, uniquegcmetakey, std::move(umx));
|
||||
// Create the top level thing that will act as our deleter later on
|
||||
stack_reference umt(L, -1);
|
||||
stack::set_field<true>(L, gcmetakey, umt);
|
||||
umt.pop();
|
||||
|
||||
stack::get_field<true>(L, gcmetakey);
|
||||
umt_t& target_umt = stack::pop<user<umt_t>>(L);
|
||||
return target_umt;
|
||||
}
|
||||
|
||||
static int push(lua_State* L, umt_t&& umx) {
|
||||
|
||||
umt_t& um = make_cleanup(L, std::move(umx));
|
||||
usertype_metatable_core& umc = um;
|
||||
regs_t value_table { {} };
|
||||
int lastreg = 0;
|
||||
(void)detail::swallow { 0, (um.template make_regs<(I * 2)>(value_table, lastreg, std::get<(I * 2)>(um.functions), std::get<(I * 2 + 1)>(um.functions)), 0)... };
|
||||
um.finish_regs(value_table, lastreg);
|
||||
value_table[lastreg] = { nullptr, nullptr };
|
||||
regs_t ref_table = value_table;
|
||||
regs_t unique_table = value_table;
|
||||
bool hasdestructor = !value_table.empty() && to_string(meta_function::garbage_collect) == value_table[lastreg - 1].name;
|
||||
if (hasdestructor) {
|
||||
ref_table[lastreg - 1] = { nullptr, nullptr };
|
||||
}
|
||||
unique_table[lastreg - 1] = { value_table[lastreg - 1].name, detail::unique_destruct<T> };
|
||||
|
||||
lua_createtable(L, 0, 2);
|
||||
stack_reference type_table(L, -1);
|
||||
|
||||
stack::set_field(L, "name", detail::demangle<T>(), type_table.stack_index());
|
||||
stack::set_field(L, "is", &u_detail::is_check<T>, type_table.stack_index());
|
||||
|
||||
// Now use um
|
||||
const bool& mustindex = umc.mustindex;
|
||||
for (std::size_t i = 0; i < 3; ++i) {
|
||||
// Pointer types, AKA "references" from C++
|
||||
const char* metakey = nullptr;
|
||||
luaL_Reg* metaregs = nullptr;
|
||||
switch (i) {
|
||||
case 0:
|
||||
metakey = &usertype_traits<T*>::metatable()[0];
|
||||
metaregs = ref_table.data();
|
||||
break;
|
||||
case 1:
|
||||
metakey = &usertype_traits<detail::unique_usertype<T>>::metatable()[0];
|
||||
metaregs = unique_table.data();
|
||||
break;
|
||||
case 2:
|
||||
default:
|
||||
metakey = &usertype_traits<T>::metatable()[0];
|
||||
metaregs = value_table.data();
|
||||
break;
|
||||
}
|
||||
luaL_newmetatable(L, metakey);
|
||||
stack_reference t(L, -1);
|
||||
stack::set_field(L, meta_function::type, type_table, t.stack_index());
|
||||
int upvalues = 0;
|
||||
upvalues += stack::push(L, nullptr);
|
||||
upvalues += stack::push(L, make_light(um));
|
||||
luaL_setfuncs(L, metaregs, upvalues);
|
||||
|
||||
if (um.baseclasscheck != nullptr) {
|
||||
stack::set_field(L, detail::base_class_check_key(), um.baseclasscheck, t.stack_index());
|
||||
}
|
||||
if (um.baseclasscast != nullptr) {
|
||||
stack::set_field(L, detail::base_class_cast_key(), um.baseclasscast, t.stack_index());
|
||||
}
|
||||
|
||||
stack::set_field(L, detail::base_class_index_propogation_key(), make_closure(um.indexbase, nullptr, make_light(um), make_light(umc)), t.stack_index());
|
||||
stack::set_field(L, detail::base_class_new_index_propogation_key(), make_closure(um.newindexbase, nullptr, make_light(um), make_light(umc)), t.stack_index());
|
||||
|
||||
if (mustindex) {
|
||||
// Basic index pushing: specialize
|
||||
// index and newindex to give variables and stuff
|
||||
stack::set_field(L, meta_function::index, make_closure(umt_t::index_call, nullptr, make_light(um), make_light(umc)), t.stack_index());
|
||||
stack::set_field(L, meta_function::new_index, make_closure(umt_t::new_index_call, nullptr, make_light(um), make_light(umc)), t.stack_index());
|
||||
}
|
||||
else {
|
||||
// If there's only functions, we can use the fast index version
|
||||
stack::set_field(L, meta_function::index, t, t.stack_index());
|
||||
}
|
||||
// metatable on the metatable
|
||||
// for call constructor purposes and such
|
||||
lua_createtable(L, 0, 3);
|
||||
stack_reference metabehind(L, -1);
|
||||
stack::set_field(L, meta_function::type, type_table, metabehind.stack_index());
|
||||
if (um.callconstructfunc != nullptr) {
|
||||
stack::set_field(L, meta_function::call_function, make_closure(um.callconstructfunc, nullptr, make_light(um), make_light(umc)), metabehind.stack_index());
|
||||
}
|
||||
if (um.secondarymeta) {
|
||||
stack::set_field(L, meta_function::index, make_closure(umt_t::index_call, nullptr, make_light(um), make_light(umc)), metabehind.stack_index());
|
||||
stack::set_field(L, meta_function::new_index, make_closure(umt_t::new_index_call, nullptr, make_light(um), make_light(umc)), metabehind.stack_index());
|
||||
}
|
||||
// type information needs to be present on the behind-tables too
|
||||
|
||||
stack::set_field(L, metatable_key, metabehind, t.stack_index());
|
||||
metabehind.pop();
|
||||
// We want to just leave the table
|
||||
// in the registry only, otherwise we return it
|
||||
t.pop();
|
||||
}
|
||||
|
||||
// Now for the shim-table that actually gets assigned to the name
|
||||
luaL_newmetatable(L, &usertype_traits<T>::user_metatable()[0]);
|
||||
stack_reference t(L, -1);
|
||||
stack::set_field(L, meta_function::type, type_table, t.stack_index());
|
||||
int upvalues = 0;
|
||||
upvalues += stack::push(L, nullptr);
|
||||
upvalues += stack::push(L, make_light(um));
|
||||
luaL_setfuncs(L, value_table.data(), upvalues);
|
||||
{
|
||||
lua_createtable(L, 0, 3);
|
||||
stack_reference metabehind(L, -1);
|
||||
// type information needs to be present on the behind-tables too
|
||||
stack::set_field(L, meta_function::type, type_table, metabehind.stack_index());
|
||||
if (um.callconstructfunc != nullptr) {
|
||||
stack::set_field(L, meta_function::call_function, make_closure(um.callconstructfunc, nullptr, make_light(um), make_light(umc)), metabehind.stack_index());
|
||||
}
|
||||
|
||||
stack::set_field(L, meta_function::index, make_closure(umt_t::meta_index_call, nullptr, make_light(um), make_light(umc), nullptr, u_detail::toplevel_magic), metabehind.stack_index());
|
||||
stack::set_field(L, meta_function::new_index, make_closure(umt_t::meta_new_index_call, nullptr, make_light(um), make_light(umc), nullptr, u_detail::toplevel_magic), metabehind.stack_index());
|
||||
stack::set_field(L, metatable_key, metabehind, t.stack_index());
|
||||
metabehind.pop();
|
||||
}
|
||||
|
||||
lua_remove(L, type_table.stack_index());
|
||||
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace stack
|
||||
|
||||
} // namespace sol
|
||||
|
||||
#endif // SOL_USERTYPE_METATABLE_HPP
|
66
include/sol/usertype_proxy.hpp
Normal file
66
include/sol/usertype_proxy.hpp
Normal file
|
@ -0,0 +1,66 @@
|
|||
// sol2
|
||||
|
||||
// The MIT License (MIT)
|
||||
|
||||
// Copyright (c) 2013-2018 Rapptz, ThePhD 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_USERTYPE_PROXY_HPP
|
||||
#define SOL_USERTYPE_PROXY_HPP
|
||||
|
||||
#include "traits.hpp"
|
||||
#include "function.hpp"
|
||||
#include "protected_function.hpp"
|
||||
#include "proxy_base.hpp"
|
||||
|
||||
namespace sol {
|
||||
template <typename Table, typename Key>
|
||||
struct usertype_proxy : public proxy_base<usertype_proxy<Table, Key>> {
|
||||
public:
|
||||
Table tbl;
|
||||
Key key;
|
||||
|
||||
template <typename T>
|
||||
usertype_proxy(Table table, T&& k)
|
||||
: tbl(table), key(std::forward<T>(k)) {
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
usertype_proxy& set(T&& value) {
|
||||
tbl.set(key, std::forward<T>(value));
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
usertype_proxy& operator=(U&& other) {
|
||||
return set(std::forward<U>(other));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
usertype_proxy& operator=(std::initializer_list<T> other) {
|
||||
return set(std::move(other));
|
||||
}
|
||||
|
||||
lua_State* lua_state() const {
|
||||
return tbl.lua_state();
|
||||
}
|
||||
};
|
||||
} // namespace sol
|
||||
|
||||
#endif // SOL_USERTYPE_PROXY_HPP
|
792
include/sol/usertype_storage.hpp
Normal file
792
include/sol/usertype_storage.hpp
Normal file
|
@ -0,0 +1,792 @@
|
|||
// sol2
|
||||
|
||||
// The MIT License (MIT)
|
||||
|
||||
// Copyright (c) 2013-2018 Rapptz, ThePhD 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_EXPERIMENTAL_USERTYPE_HPP
|
||||
#define SOL_EXPERIMENTAL_USERTYPE_HPP
|
||||
|
||||
#include "usertype_core.hpp"
|
||||
#include "make_reference.hpp"
|
||||
#include "map.hpp"
|
||||
|
||||
#include <bitset>
|
||||
|
||||
namespace sol {
|
||||
|
||||
namespace u_detail {
|
||||
|
||||
using index_call_function = int(lua_State*, void*);
|
||||
|
||||
struct index_call_storage {
|
||||
index_call_function* index;
|
||||
index_call_function* new_index;
|
||||
void* binding_data;
|
||||
};
|
||||
|
||||
struct binding_base {
|
||||
virtual ~binding_base() {
|
||||
}
|
||||
};
|
||||
|
||||
template <typename K, typename F, typename T = void>
|
||||
struct binding : binding_base {
|
||||
F data_;
|
||||
|
||||
template <typename... Args>
|
||||
binding(Args&&... args)
|
||||
: data_(std::forward<Args>(args)...) {
|
||||
}
|
||||
|
||||
void* data() {
|
||||
return static_cast<void*>(std::addressof(data_));
|
||||
}
|
||||
|
||||
template <bool is_index = true, bool is_variable = false>
|
||||
static int call_with_(lua_State* L, void* target) {
|
||||
constexpr int boost = !detail::is_non_factory_constructor<F>::value
|
||||
&& std::is_same<K, call_construction>::value
|
||||
? 1
|
||||
: 0;
|
||||
auto& f = *static_cast<F*>(target);
|
||||
return call_detail::call_wrapped<T, is_index, is_variable, boost>(L, f);
|
||||
}
|
||||
|
||||
template <bool is_index = true, bool is_variable = false>
|
||||
static int call_(lua_State* L) {
|
||||
void* f = stack::get<void*>(L, upvalue_index(usertype_storage_index));
|
||||
return call_with_<is_index, is_variable>(L, f);
|
||||
}
|
||||
|
||||
template <bool is_index = true, bool is_variable = false>
|
||||
static int call(lua_State* L) {
|
||||
return detail::typed_static_trampoline<decltype(&call_<is_index, is_variable>), (&call_<is_index, is_variable>)>(L);
|
||||
}
|
||||
|
||||
template <bool is_index = true, bool is_variable = false>
|
||||
static int index_call_with_(lua_State* L, void* target) {
|
||||
if constexpr (!is_variable) {
|
||||
// set up upvalues
|
||||
// for a chained call
|
||||
int upvalues = 0;
|
||||
upvalues += stack::push(L, nullptr);
|
||||
upvalues += stack::push(L, target);
|
||||
auto cfunc = &call<is_index, is_variable>;
|
||||
return stack::push(L, c_closure(cfunc, upvalues));
|
||||
}
|
||||
else {
|
||||
constexpr int boost = !detail::is_non_factory_constructor<F>::value
|
||||
&& std::is_same<K, call_construction>::value
|
||||
? 1
|
||||
: 0;
|
||||
auto& f = *static_cast<F*>(target);
|
||||
return call_detail::call_wrapped<T, is_index, is_variable, boost>(L, f);
|
||||
}
|
||||
}
|
||||
|
||||
template <bool is_index = true, bool is_variable = false>
|
||||
static int index_call_(lua_State* L) {
|
||||
void* f = stack::get<void*>(L, upvalue_index(usertype_storage_index));
|
||||
return index_call_with_<is_index, is_variable>(L, f);
|
||||
}
|
||||
|
||||
template <bool is_index = true, bool is_variable = false>
|
||||
static int index_call(lua_State* L) {
|
||||
return detail::typed_static_trampoline<decltype(&index_call_<is_index, is_variable>), (&index_call_<is_index, is_variable>)>(L);
|
||||
}
|
||||
};
|
||||
|
||||
inline int index_fail(lua_State* L) {
|
||||
if (lua_getmetatable(L, 1) == 1) {
|
||||
int metatarget = lua_gettop(L);
|
||||
stack::get_field(L, stack_reference(L, raw_index(2)), metatarget);
|
||||
return 1;
|
||||
}
|
||||
// With runtime extensibility, we can't hard-error things. They have to return nil, like regular table types
|
||||
return stack::push(L, lua_nil);
|
||||
}
|
||||
|
||||
inline int new_index_fail(lua_State* L) {
|
||||
return luaL_error(L, "sol: cannot set (new_index) into this object: no defined new_index operation on usertype");
|
||||
}
|
||||
|
||||
struct usertype_storage_base {
|
||||
public:
|
||||
std::vector<std::unique_ptr<binding_base>> storage;
|
||||
detail::unordered_map<std::string, index_call_storage> string_keys;
|
||||
detail::unordered_map<reference, reference, reference_hash, reference_equals> auxiliary_keys;
|
||||
reference value_index_table;
|
||||
reference reference_index_table;
|
||||
reference unique_index_table;
|
||||
reference const_reference_index_table;
|
||||
reference const_value_index_table;
|
||||
reference named_index_table;
|
||||
reference type_table;
|
||||
reference gc_names_table;
|
||||
reference metametatable;
|
||||
std::bitset<64> properties;
|
||||
lua_CFunction base_index;
|
||||
lua_CFunction base_new_index;
|
||||
bool is_using_index;
|
||||
bool is_using_new_index;
|
||||
|
||||
usertype_storage_base(lua_State* L)
|
||||
: storage(), string_keys(), auxiliary_keys(), value_index_table(), reference_index_table(), unique_index_table(), const_reference_index_table(), type_table(make_reference(L, create)), gc_names_table(make_reference(L, create)), metametatable(make_reference(L, create)), properties(), base_index(index_fail), base_new_index(new_index_fail), is_using_index(false), is_using_new_index(false) {
|
||||
}
|
||||
|
||||
void clear() {
|
||||
storage.clear();
|
||||
string_keys.clear();
|
||||
auxiliary_keys.clear();
|
||||
// TODO: also nuke individual lua tables,
|
||||
// one by one,
|
||||
// then replace getter/setter
|
||||
}
|
||||
|
||||
template <typename Base>
|
||||
static void base_walk_index(lua_State* L, usertype_storage_base& self, bool& keep_going, int& base_result) {
|
||||
using bases = typename base<Base>::type;
|
||||
if (!keep_going) {
|
||||
return;
|
||||
}
|
||||
(void)L;
|
||||
(void)self;
|
||||
// TODO: get base table, dump it out
|
||||
usertype_storage_base& base_storage = get_usertype_storage<Base>(L);
|
||||
base_result = self_index_call<true>(bases(), L, base_storage);
|
||||
keep_going = base_result != base_walking_failed_index;
|
||||
}
|
||||
|
||||
template <typename Base>
|
||||
static void base_walk_new_index(lua_State* L, usertype_storage_base& self, bool& keep_going, int& base_result) {
|
||||
using bases = typename base<Base>::type;
|
||||
if (!keep_going) {
|
||||
return;
|
||||
}
|
||||
(void)L;
|
||||
(void)self;
|
||||
// TODO: get base table, dump it out
|
||||
usertype_storage_base& base_storage = get_usertype_storage<Base>(L);
|
||||
base_result = self_index_call<true>(bases(), L, base_storage);
|
||||
keep_going = base_result != base_walking_failed_index;
|
||||
}
|
||||
|
||||
template <bool base_walking = false, bool from_named_metatable = false, typename... Bases>
|
||||
static int self_index_call(types<Bases...>, lua_State* L, usertype_storage_base& self) {
|
||||
type k_type = stack::get<type>(L, 2);
|
||||
if (k_type == type::string) {
|
||||
index_call_storage* target = nullptr;
|
||||
{
|
||||
std::string k = stack::get<std::string>(L, 2);
|
||||
auto it = self.string_keys.find(k);
|
||||
if (it != self.string_keys.cend()) {
|
||||
target = &it->second;
|
||||
}
|
||||
}
|
||||
if (target != nullptr) {
|
||||
// let the target decide what to do
|
||||
return (target->index)(L, target->binding_data);
|
||||
}
|
||||
}
|
||||
else if (k_type != type::nil && k_type != type::none) {
|
||||
reference* target = nullptr;
|
||||
{
|
||||
stack_reference k = stack::get<stack_reference>(L, 2);
|
||||
auto it = self.auxiliary_keys.find(k);
|
||||
if (it != self.auxiliary_keys.cend()) {
|
||||
target = &it->second;
|
||||
}
|
||||
}
|
||||
if (target != nullptr) {
|
||||
// push target to return
|
||||
// what we found
|
||||
return stack::push(L, *target);
|
||||
}
|
||||
}
|
||||
|
||||
// retrieve bases and walk through them.
|
||||
bool keep_going = true;
|
||||
int base_result;
|
||||
detail::swallow { 1, (1, base_walk_index<Bases>(L, self, keep_going, base_result))... };
|
||||
if (sizeof...(Bases) > 0 && !keep_going) {
|
||||
return base_result;
|
||||
}
|
||||
if (base_walking) {
|
||||
// if we're JUST base-walking then don't index-fail, just
|
||||
// return the false bits
|
||||
return base_walking_failed_index;
|
||||
}
|
||||
if constexpr (from_named_metatable) {
|
||||
return index_fail(L);
|
||||
}
|
||||
else {
|
||||
return self.base_index(L);
|
||||
}
|
||||
}
|
||||
|
||||
template <bool base_walking = false, bool from_named_metatable = false, typename... Bases>
|
||||
static int self_new_index_call(types<Bases...>, lua_State* L, usertype_storage_base& self) {
|
||||
type k_type = stack::get<type>(L, 2);
|
||||
if (k_type == type::string) {
|
||||
index_call_storage* target = nullptr;
|
||||
{
|
||||
std::string k = stack::get<std::string>(L, 2);
|
||||
auto it = self.string_keys.find(k);
|
||||
if (it != self.string_keys.cend()) {
|
||||
target = &it->second;
|
||||
}
|
||||
}
|
||||
if (target != nullptr) {
|
||||
// set value through
|
||||
// new_index call, whatever that entails,
|
||||
// and return
|
||||
return (target->new_index)(L, target->binding_data);
|
||||
}
|
||||
}
|
||||
else if (k_type != type::nil) {
|
||||
reference* target = nullptr;
|
||||
{
|
||||
stack_reference k = stack::get<stack_reference>(L, 2);
|
||||
auto it = self.auxiliary_keys.find(k);
|
||||
if (it != self.auxiliary_keys.cend()) {
|
||||
target = &it->second;
|
||||
}
|
||||
}
|
||||
if (target != nullptr) {
|
||||
// set value and return
|
||||
*target = reference(L, 3);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// retrieve bases and walk through them.
|
||||
bool keep_going = true;
|
||||
int base_result;
|
||||
detail::swallow { 1, (1, base_walk_new_index<Bases>(L, self, keep_going, base_result))... };
|
||||
if (sizeof...(Bases) > 0 && !keep_going) {
|
||||
return base_result;
|
||||
}
|
||||
if (base_walking) {
|
||||
// if we're JUST base-walking then don't index-fail, just
|
||||
// return the false bits
|
||||
return base_walking_failed_index;
|
||||
}
|
||||
if constexpr (from_named_metatable) {
|
||||
self.set(L, reference(L, raw_index(2)), reference(L, raw_index(3)));
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
return self.base_new_index(L);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void change_indexing(lua_State* L, submetatable submetatable_type, stack_reference& t) {
|
||||
using uts = usertype_storage<T>;
|
||||
usertype_storage_base& this_base = *this;
|
||||
this->is_using_index |= true;
|
||||
this->is_using_new_index |= true;
|
||||
if constexpr (std::is_void_v<T>) {
|
||||
static_assert(!std::is_void_v<T>, "You cannot add variables or index methods without going through the strongly-typed usertype<T> metatable!");
|
||||
}
|
||||
else {
|
||||
if (submetatable_type == submetatable::named) {
|
||||
stack::set_field(L, metatable_key, metametatable, t.stack_index());
|
||||
stack_reference stack_metametatable(L, -metametatable.push());
|
||||
stack::set_field<false, true>(L, meta_function::index, make_closure(uts::meta_index_call, nullptr, make_light(*this), make_light(this_base), nullptr, toplevel_magic), stack_metametatable.stack_index());
|
||||
stack::set_field<false, true>(L, meta_function::new_index, make_closure(uts::meta_new_index_call, nullptr, make_light(*this), make_light(this_base), nullptr, toplevel_magic), stack_metametatable.stack_index());
|
||||
stack_metametatable.pop();
|
||||
}
|
||||
else {
|
||||
stack::set_field<false, true>(L, meta_function::index, make_closure(uts::index_call, nullptr, make_light(*this), make_light(this_base), nullptr, toplevel_magic), t.stack_index());
|
||||
stack::set_field<false, true>(L, meta_function::new_index, make_closure(uts::new_index_call, nullptr, make_light(*this), make_light(this_base), nullptr, toplevel_magic), t.stack_index());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Fx>
|
||||
void for_each_table(lua_State* L, Fx&& fx) {
|
||||
for (int i = 0; i < 6; ++i) {
|
||||
submetatable submetatable_type = static_cast<submetatable>(i);
|
||||
reference* p_fast_index_table = nullptr;
|
||||
switch (submetatable_type) {
|
||||
case submetatable::const_value:
|
||||
p_fast_index_table = &this->const_value_index_table;
|
||||
break;
|
||||
case submetatable::reference:
|
||||
p_fast_index_table = &this->reference_index_table;
|
||||
break;
|
||||
case submetatable::unique:
|
||||
p_fast_index_table = &this->unique_index_table;
|
||||
break;
|
||||
case submetatable::const_reference:
|
||||
p_fast_index_table = &this->const_reference_index_table;
|
||||
break;
|
||||
case submetatable::named:
|
||||
p_fast_index_table = &this->named_index_table;
|
||||
break;
|
||||
case submetatable::value:
|
||||
default:
|
||||
p_fast_index_table = &this->value_index_table;
|
||||
break;
|
||||
}
|
||||
fx(L, submetatable_type, *p_fast_index_table);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T = void, typename Key, typename Value>
|
||||
void set(lua_State* L, Key&& key, Value&& value);
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct usertype_storage : usertype_storage_base {
|
||||
|
||||
using usertype_storage_base::usertype_storage_base;
|
||||
|
||||
template <bool from_named_metatable>
|
||||
static int index_call_(lua_State* L) {
|
||||
using bases = typename base<T>::type;
|
||||
usertype_storage_base& self = stack::get<light<usertype_storage_base>>(L, upvalue_index(usertype_storage_index));
|
||||
return self_index_call<false, from_named_metatable>(bases(), L, self);
|
||||
}
|
||||
|
||||
template <bool from_named_metatable>
|
||||
static int new_index_call_(lua_State* L) {
|
||||
using bases = typename base<T>::type;
|
||||
usertype_storage_base& self = stack::get<light<usertype_storage_base>>(L, upvalue_index(usertype_storage_index));
|
||||
return self_new_index_call<false, from_named_metatable>(bases(), L, self);
|
||||
}
|
||||
|
||||
static int index_call(lua_State* L) {
|
||||
return detail::static_trampoline<&index_call_<false>>(L);
|
||||
}
|
||||
|
||||
static int new_index_call(lua_State* L) {
|
||||
return detail::static_trampoline<&new_index_call_<false>>(L);
|
||||
}
|
||||
|
||||
static int meta_index_call(lua_State* L) {
|
||||
return detail::static_trampoline<&index_call_<true>>(L);
|
||||
}
|
||||
|
||||
static int meta_new_index_call(lua_State* L) {
|
||||
return detail::static_trampoline<&new_index_call_<true>>(L);
|
||||
}
|
||||
|
||||
template <typename Key, typename Value>
|
||||
void set(lua_State* L, Key&& key, Value&& value) {
|
||||
static_cast<usertype_storage_base&>(*this).set<T>(L, std::forward<Key>(key), std::forward<Value>(value));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename Key, typename Value>
|
||||
void usertype_storage_base::set(lua_State* L, Key&& key, Value&& value) {
|
||||
using ValueU = meta::unwrap_unqualified_t<Value>;
|
||||
using KeyU = meta::unwrap_unqualified_t<Key>;
|
||||
using Binding = binding<KeyU, ValueU, T>;
|
||||
using is_var_bind = is_variable_binding<ValueU>;
|
||||
if constexpr (std::is_same_v<Key, call_construction>) {
|
||||
std::unique_ptr<Binding> p_binding = std::make_unique<Binding>(std::forward<Value>(value));
|
||||
Binding& b = *p_binding;
|
||||
this->storage.push_back(std::move(p_binding));
|
||||
|
||||
this->named_index_table.push();
|
||||
absolute_index metametatable_index(L, -1);
|
||||
stack::push(L, nullptr);
|
||||
stack::push(L, b.data());
|
||||
lua_CFunction target_func = &b.call<false, false>;
|
||||
lua_pushcclosure(L, target_func, 2);
|
||||
lua_setfield(L, metametatable_index, to_string(meta_function::call).c_str());
|
||||
this->named_index_table.pop();
|
||||
}
|
||||
else if constexpr ((meta::is_string_constructible<KeyU>::value || std::is_same_v<KeyU, meta_function>)&&(!is_lua_reference_or_proxy<ValueU>::value && !is_lua_reference_or_proxy<KeyU>::value)) {
|
||||
std::unique_ptr<Binding> p_binding = std::make_unique<Binding>(std::forward<Value>(value));
|
||||
Binding& b = *p_binding;
|
||||
this->storage.push_back(std::move(p_binding));
|
||||
|
||||
std::string s = u_detail::make_string(std::forward<Key>(key));
|
||||
bool is_index = is_var_bind::value || (s == to_string(meta_function::index));
|
||||
bool is_new_index = is_var_bind::value || (s == to_string(meta_function::new_index));
|
||||
bool no_use_named = s == to_string(meta_function::call);
|
||||
index_call_storage ics;
|
||||
ics.binding_data = b.data();
|
||||
ics.index = &b.index_call_with_<true, is_var_bind::value>;
|
||||
ics.new_index = &b.index_call_with_<false, is_var_bind::value>;
|
||||
// need to swap everything to use fast indexing here
|
||||
auto fet = [&](lua_State* L, submetatable submetatable_type, reference& fast_index_table) {
|
||||
if (submetatable_type == submetatable::named && no_use_named) {
|
||||
// do not override __call or
|
||||
// other specific meta functions on named metatable:
|
||||
// we need that for call construction
|
||||
// and other amenities
|
||||
return;
|
||||
}
|
||||
int fast_index_table_push = fast_index_table.push();
|
||||
stack_reference t(L, -fast_index_table_push);
|
||||
stack::set_field<false, true>(L, s, make_closure(&b.call<false, is_var_bind::value>, nullptr, ics.binding_data), t.stack_index());
|
||||
if (is_index && is_new_index) {
|
||||
change_indexing<T>(L, submetatable_type, t);
|
||||
}
|
||||
t.pop();
|
||||
};
|
||||
this->for_each_table(L, fet);
|
||||
this->string_keys.insert_or_assign(std::move(s), std::move(ics));
|
||||
}
|
||||
else {
|
||||
// the reference-based implementation might compare poorly and hash
|
||||
// poorly in some cases...
|
||||
if constexpr (is_lua_reference<KeyU>::value && is_lua_reference<ValueU>::value) {
|
||||
auto ref_additions_fx = [&](lua_State* L, submetatable, reference& fast_index_table) {
|
||||
int fast_index_table_push = fast_index_table.push();
|
||||
stack_reference t(L, -fast_index_table_push);
|
||||
stack::set_field<false, true>(L, key, value, t.stack_index());
|
||||
t.pop();
|
||||
};
|
||||
this->for_each_table(L, ref_additions_fx);
|
||||
key.push();
|
||||
optional<std::string> maybe_string_key = stack::get<optional<std::string>>(L, -1);
|
||||
if (maybe_string_key) {
|
||||
std::string s = *maybe_string_key;
|
||||
std::unique_ptr<Binding> p_binding = std::make_unique<Binding>(std::forward<Value>(value));
|
||||
Binding& b = *p_binding;
|
||||
this->storage.push_back(std::move(p_binding));
|
||||
|
||||
index_call_storage ics;
|
||||
ics.binding_data = b.data();
|
||||
ics.index = &b.index_call_with_<true, true>;
|
||||
ics.new_index = &b.index_call_with_<false, true>;
|
||||
|
||||
this->string_keys.insert_or_assign(std::move(s), ics);
|
||||
}
|
||||
else {
|
||||
// its auxiliary and must be
|
||||
// indexed with weirdness
|
||||
this->auxiliary_keys.insert_or_assign(std::forward<Key>(key), std::forward<Value>(value));
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
else {
|
||||
reference ref_key = make_reference(L, std::forward<Key>(key));
|
||||
reference ref_value = make_reference(L, std::forward<Value>(value));
|
||||
auto ref_additions_fx = [&](lua_State* L, submetatable, reference& fast_index_table) {
|
||||
int fast_index_table_push = fast_index_table.push();
|
||||
stack_reference t(L, -fast_index_table_push);
|
||||
stack::set_field<false, true>(L, ref_key, ref_value, t.stack_index());
|
||||
t.pop();
|
||||
};
|
||||
this->for_each_table(L, ref_additions_fx);
|
||||
this->auxiliary_keys.insert_or_assign(std::move(ref_key), std::move(ref_value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline usertype_storage<T>& create_usertype_storage(lua_State* L) {
|
||||
const char* gcmetakey = &usertype_traits<T>::gc_table()[0];
|
||||
|
||||
// Make sure userdata's memory is properly in lua first,
|
||||
// otherwise all the light userdata we make later will become invalid
|
||||
int usertype_storage_push_count = stack::push<user<usertype_storage<T>>>(L, no_metatable, L);
|
||||
stack_reference usertype_storage_ref(L, -usertype_storage_push_count);
|
||||
|
||||
// create and push onto the stack a table to use as metatable for this GC
|
||||
// we create a metatable to attach to the regular gc_table
|
||||
// so that the destructor is called for the usertype storage
|
||||
int usertype_storage_metatabe_count = stack::push(L, new_table(0, 1));
|
||||
stack_table usertype_storage_metatable(L, -usertype_storage_metatabe_count);
|
||||
// set the destruction routine on the metatable
|
||||
stack::set_field(L, meta_function::garbage_collect, detail::user_alloc_destruct<T>, usertype_storage_metatable.stack_index());
|
||||
// set the metatable on the usertype storage userdata
|
||||
stack::set_field(L, metatable_key, usertype_storage_metatable, usertype_storage_ref.stack_index());
|
||||
usertype_storage_metatable.pop();
|
||||
|
||||
// set the usertype storage and its metatable
|
||||
// into the global table...
|
||||
stack::set_field<true>(L, gcmetakey, usertype_storage_ref);
|
||||
usertype_storage_ref.pop();
|
||||
|
||||
// then retrieve the lua-stored version so we have a well-pinned
|
||||
// reference that does not die
|
||||
stack::get_field<true>(L, gcmetakey);
|
||||
usertype_storage<T>& target_umt = stack::pop<user<usertype_storage<T>>>(L);
|
||||
return target_umt;
|
||||
}
|
||||
|
||||
inline optional<usertype_storage_base&> maybe_get_usertype_storage_base(lua_State* L, const char* gcmetakey) {
|
||||
stack::get_field<true>(L, gcmetakey);
|
||||
stack::record tracking;
|
||||
usertype_storage_base& target_umt = stack::stack_detail::unchecked_unqualified_get<user<usertype_storage_base>>(L, -1, tracking);
|
||||
return target_umt;
|
||||
}
|
||||
|
||||
inline usertype_storage_base& get_usertype_storage_base(lua_State* L, const char* gcmetakey) {
|
||||
stack::get_field<true>(L, gcmetakey);
|
||||
stack::record tracking;
|
||||
usertype_storage_base& target_umt = stack::stack_detail::unchecked_unqualified_get<user<usertype_storage_base>>(L, -1, tracking);
|
||||
return target_umt;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline optional<usertype_storage<T>&> maybe_get_usertype_storage(lua_State* L) {
|
||||
const char* gcmetakey = &usertype_traits<T>::gc_table()[0];
|
||||
stack::get_field<true>(L, gcmetakey);
|
||||
if (!stack::check<user<usertype_storage<T>>>(L)) {
|
||||
return nullopt;
|
||||
}
|
||||
usertype_storage<T>& target_umt = stack::pop<user<usertype_storage<T>>>(L);
|
||||
return target_umt;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline usertype_storage<T>& get_usertype_storage(lua_State* L) {
|
||||
const char* gcmetakey = &usertype_traits<T>::gc_table()[0];
|
||||
stack::get_field<true>(L, gcmetakey);
|
||||
usertype_storage<T>& target_umt = stack::pop<user<usertype_storage<T>>>(L);
|
||||
return target_umt;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void delete_usertype_storage(lua_State* L) {
|
||||
using u_traits = usertype_traits<T>;
|
||||
using u_const_traits = usertype_traits<const T>;
|
||||
using u_unique_traits = usertype_traits<detail::unique_usertype<T>>;
|
||||
using u_ref_traits = usertype_traits<T*>;
|
||||
using u_const_ref_traits = usertype_traits<T const*>;
|
||||
using uts = usertype_storage<T>;
|
||||
|
||||
const char* gcmetakey = &u_traits::gc_table()[0];
|
||||
stack::get_field<true>(L, gcmetakey);
|
||||
if (!stack::check<user<uts>>(L)) {
|
||||
lua_pop(L, 1);
|
||||
return;
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
|
||||
// get the registry
|
||||
stack_reference registry(L, raw_index(LUA_REGISTRYINDEX));
|
||||
registry.push();
|
||||
// eliminate all named entries for this usertype
|
||||
// in the registry (luaL_newmetatable does
|
||||
// [name] = new table
|
||||
// in registry upon creation
|
||||
stack::set_field(L, &u_traits::metatable()[0], lua_nil, registry.stack_index());
|
||||
stack::set_field(L, &u_const_traits::metatable()[0], lua_nil, registry.stack_index());
|
||||
stack::set_field(L, &u_const_ref_traits::metatable()[0], lua_nil, registry.stack_index());
|
||||
stack::set_field(L, &u_ref_traits::metatable()[0], lua_nil, registry.stack_index());
|
||||
stack::set_field(L, &u_unique_traits::metatable()[0], lua_nil, registry.stack_index());
|
||||
registry.pop();
|
||||
|
||||
stack::set_field<true>(L, gcmetakey, lua_nil);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline int register_usertype(lua_State* L, optional<no_construction> no_default_constructor = nullopt) {
|
||||
using u_traits = usertype_traits<T>;
|
||||
using u_const_traits = usertype_traits<const T>;
|
||||
using u_unique_traits = usertype_traits<detail::unique_usertype<T>>;
|
||||
using u_ref_traits = usertype_traits<T*>;
|
||||
using u_const_ref_traits = usertype_traits<T const*>;
|
||||
using uts = usertype_storage<T>;
|
||||
|
||||
// always have __new_index point to usertype_storage method
|
||||
// have __index always point to regular fast-lookup
|
||||
// meta_method table
|
||||
// if __new_index is invoked, runtime-swap
|
||||
// to slow __index if necessary
|
||||
// (no speed penalty because function calls
|
||||
// are all read-only -- only depend on __index
|
||||
// to retrieve function and then call happens VIA Lua)
|
||||
|
||||
// __type entry:
|
||||
// table contains key -> value lookup,
|
||||
// where key is entry in metatable
|
||||
// and value is type information as a string as
|
||||
// best as we can give it
|
||||
|
||||
// name entry:
|
||||
// string that contains raw class name,
|
||||
// as defined from C++
|
||||
|
||||
// is entry:
|
||||
// checks if argument supplied is of type T
|
||||
|
||||
// __storage entry:
|
||||
// a light userdata pointing to the storage
|
||||
// mostly to enable this new abstraction
|
||||
// to not require the type name `T`
|
||||
// to get at the C++ usertype storage within
|
||||
|
||||
|
||||
// we then let typical definitions potentially override these intrinsics
|
||||
// it's the user's fault if they override things or screw them up:
|
||||
// these names have been reserved and documented since sol2
|
||||
|
||||
// STEP 0: tell the old usertype (if it exists)
|
||||
// to fuck off
|
||||
delete_usertype_storage<T>(L);
|
||||
|
||||
// STEP 1: Create backing store for usertype storage
|
||||
// Pretty much the most important step.
|
||||
// STEP 2: Create Lua tables used for fast method indexing.
|
||||
// This is done inside of the storage table's constructor
|
||||
usertype_storage<T>& storage = create_usertype_storage<T>(L);
|
||||
usertype_storage_base& base_storage = storage;
|
||||
|
||||
// STEP 3: set up GC escape hatch table entirely
|
||||
storage.gc_names_table.push();
|
||||
stack_reference gnt(L, -1);
|
||||
stack::set_field(L, submetatable::named, &u_traits::gc_table()[0], gnt.stack_index());
|
||||
stack::set_field(L, submetatable::const_value, &u_const_traits::metatable()[0], gnt.stack_index());
|
||||
stack::set_field(L, submetatable::const_reference, &u_const_ref_traits::metatable()[0], gnt.stack_index());
|
||||
stack::set_field(L, submetatable::reference, &u_ref_traits::metatable()[0], gnt.stack_index());
|
||||
stack::set_field(L, submetatable::unique, &u_unique_traits::metatable()[0], gnt.stack_index());
|
||||
stack::set_field(L, submetatable::value, &u_traits::metatable()[0], gnt.stack_index());
|
||||
gnt.pop();
|
||||
|
||||
// STEP 4: add some useful information to the type table
|
||||
stack_reference stacked_type_table(L, -storage.type_table.push());
|
||||
stack::set_field(L, "name", detail::demangle<T>(), stacked_type_table.stack_index());
|
||||
stack::set_field(L, "is", &u_detail::is_check<T>, stacked_type_table.stack_index());
|
||||
stacked_type_table.pop();
|
||||
|
||||
// STEP 5: create and hook up metatable,
|
||||
// add intrinsics
|
||||
// this one is the actual meta-handling table,
|
||||
// the next one will be the one for
|
||||
for (std::size_t subusertype_storage_index = 0; subusertype_storage_index < 6; ++subusertype_storage_index) {
|
||||
submetatable submetatable_type = static_cast<submetatable>(subusertype_storage_index);
|
||||
// Pointer types, AKA "references" from C++
|
||||
const char* metakey = nullptr;
|
||||
reference* p_fast_index_table = nullptr;
|
||||
switch (submetatable_type) {
|
||||
case submetatable::const_value:
|
||||
metakey = &u_const_traits::metatable()[0];
|
||||
p_fast_index_table = &storage.const_value_index_table;
|
||||
break;
|
||||
case submetatable::reference:
|
||||
metakey = &u_ref_traits::metatable()[0];
|
||||
p_fast_index_table = &storage.reference_index_table;
|
||||
break;
|
||||
case submetatable::unique:
|
||||
metakey = &u_unique_traits::metatable()[0];
|
||||
p_fast_index_table = &storage.unique_index_table;
|
||||
break;
|
||||
case submetatable::const_reference:
|
||||
metakey = &u_const_ref_traits::metatable()[0];
|
||||
p_fast_index_table = &storage.const_reference_index_table;
|
||||
break;
|
||||
case submetatable::named:
|
||||
metakey = &u_traits::user_metatable()[0];
|
||||
p_fast_index_table = &storage.named_index_table;
|
||||
break;
|
||||
case submetatable::value:
|
||||
default:
|
||||
metakey = &u_traits::metatable()[0];
|
||||
p_fast_index_table = &storage.value_index_table;
|
||||
break;
|
||||
}
|
||||
reference& fast_index_table = *p_fast_index_table;
|
||||
|
||||
luaL_newmetatable(L, metakey);
|
||||
stack_reference t(L, -1);
|
||||
stack::set_field(L, meta_function::type, storage.type_table, t.stack_index());
|
||||
if constexpr (std::is_destructible_v<T>) {
|
||||
// destructible: serialize default
|
||||
// destructor here
|
||||
switch (submetatable_type) {
|
||||
case submetatable::const_reference:
|
||||
case submetatable::reference:
|
||||
break;
|
||||
case submetatable::unique:
|
||||
stack::set_field(L, meta_function::garbage_collect, &detail::unique_destruct<T>, t.stack_index());
|
||||
break;
|
||||
case submetatable::value:
|
||||
case submetatable::const_value:
|
||||
default:
|
||||
stack::set_field(L, meta_function::garbage_collect, &detail::usertype_alloc_destruct<T>, t.stack_index());
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// not destructible: serialize a
|
||||
// "hey you messed up"
|
||||
// destructor
|
||||
stack::set_field(L, meta_function::garbage_collect, &detail::cannot_destruct<T>, t.stack_index());
|
||||
}
|
||||
|
||||
if (base<T>::value) {
|
||||
stack::set_field(L, detail::base_class_check_key(), &detail::inheritance<T>::type_check, t.stack_index());
|
||||
}
|
||||
if (base<T>::value) {
|
||||
stack::set_field(L, detail::base_class_cast_key(), &detail::inheritance<T>::type_cast, t.stack_index());
|
||||
}
|
||||
|
||||
auto prop_fx = [&](meta_function mf) {
|
||||
return !storage.properties[static_cast<int>(mf)];
|
||||
};
|
||||
auto insert_fx = [&](meta_function mf, lua_CFunction reg) {
|
||||
stack::set_field(L, mf, reg, t.stack_index());
|
||||
storage.properties[static_cast<int>(mf)] = true;
|
||||
};
|
||||
u_detail::insert_default_registrations<T>(insert_fx, prop_fx);
|
||||
|
||||
// There are no variables, so serialize the fast function stuff
|
||||
// be sure to reset the index stuff to the non-fast version
|
||||
// if the user ever adds something later!
|
||||
stack::set_field(L, meta_function::index, t, t.stack_index());
|
||||
if (submetatable_type == submetatable::named) {
|
||||
// add escape hatch storage pointer and gc names
|
||||
stack::set_field(L, meta_function::storage, make_light(base_storage), t.stack_index());
|
||||
stack::set_field(L, meta_function::gc_names, storage.gc_names_table, t.stack_index());
|
||||
|
||||
// fancy new_indexing when using the named table
|
||||
stack::set_field<false, true>(L, metatable_key, storage.metametatable, t.stack_index());
|
||||
stack_reference stack_metametatable(L, -storage.metametatable.push());
|
||||
stack::set_field<false, true>(L, meta_function::index, make_closure(uts::meta_index_call, nullptr, make_light(storage), make_light(base_storage), nullptr, toplevel_magic), stack_metametatable.stack_index());
|
||||
stack::set_field<false, true>(L, meta_function::new_index, make_closure(uts::meta_new_index_call, nullptr, make_light(storage), make_light(base_storage), nullptr, toplevel_magic), stack_metametatable.stack_index());
|
||||
stack_metametatable.pop();
|
||||
}
|
||||
else {
|
||||
// otherwise just plain
|
||||
stack::set_field(L, meta_function::new_index, t, t.stack_index());
|
||||
}
|
||||
|
||||
fast_index_table = reference(L, t);
|
||||
if (submetatable_type != submetatable::named) {
|
||||
t.pop();
|
||||
}
|
||||
}
|
||||
|
||||
// can only use set AFTER we initialize all the metatables
|
||||
if constexpr (std::is_default_constructible_v<T>) {
|
||||
if (no_default_constructor == nullopt) {
|
||||
storage.set(L, meta_function::construct, constructors<T()>());
|
||||
}
|
||||
}
|
||||
|
||||
// return the named metatable we left on top of the stack
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace sol::u_detail
|
||||
|
||||
#endif SOL_EXPERIMENTAL_USERTYPE_HPP
|
Loading…
Reference in New Issue
Block a user