// sol2 // The MIT License (MIT) // Copyright (c) 2013-2022 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_STACK_CORE_HPP #define SOL_STACK_CORE_HPP #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace sol { namespace detail { struct with_function_tag { }; struct as_reference_tag { }; template struct as_pointer_tag { }; template struct as_value_tag { }; template struct as_unique_tag { }; template struct as_table_tag { }; template inline constexpr bool is_tagged_v = meta::is_specialization_of_v || meta::is_specialization_of_v || meta::is_specialization_of_v || meta::is_specialization_of_v || std::is_same_v || std::is_same_v; using lua_reg_table = luaL_Reg[64]; using unique_destructor = void (*)(void*); using unique_tag = detail::inheritance_unique_cast_function; inline void* alloc_newuserdata(lua_State* L, std::size_t bytesize) { #if SOL_LUA_VERSION_I_ >= 504 return lua_newuserdatauv(L, bytesize, 1); #else return lua_newuserdata(L, bytesize); #endif } constexpr std::uintptr_t align(std::size_t alignment, std::uintptr_t ptr, std::size_t& space) { // this handles arbitrary alignments... // make this into a power-of-2-only? // actually can't: this is a C++14-compatible framework, // power of 2 alignment is C++17 std::uintptr_t offby = static_cast(ptr % alignment); std::uintptr_t padding = (alignment - offby) % alignment; ptr += padding; space -= padding; return ptr; } inline void* align(std::size_t alignment, void* ptr, std::size_t& space) { return reinterpret_cast(align(alignment, reinterpret_cast(ptr), space)); } constexpr std::uintptr_t align_one(std::size_t alignment, std::size_t size, std::uintptr_t ptr) { std::size_t space = (std::numeric_limits::max)(); return align(alignment, ptr, space) + size; } template constexpr std::size_t aligned_space_for(std::uintptr_t ptr) { std::uintptr_t end = ptr; ((end = align_one(alignof(Args), sizeof(Args), end)), ...); return static_cast(end - ptr); } template constexpr std::size_t aligned_space_for() { static_assert(sizeof...(Args) > 0); constexpr std::size_t max_arg_alignment = (std::max)({ alignof(Args)... }); if constexpr (max_arg_alignment <= alignof(std::max_align_t)) { // If all types are `good enough`, simply calculate alignment in case of the worst allocator std::size_t worst_required_size = 0; for (std::size_t ptr = 0; ptr < max_arg_alignment; ptr++) { worst_required_size = (std::max)(worst_required_size, aligned_space_for(ptr)); } return worst_required_size; } else { // For over-aligned types let's assume that every Arg in Args starts at the worst aligned address return (aligned_space_for(0x1) + ...); } } inline void* align_usertype_pointer(void* ptr) { using use_align = std::integral_constant::value > 1) #endif >; if (!use_align::value) { return ptr; } std::size_t space = (std::numeric_limits::max)(); return align(std::alignment_of::value, ptr, space); } template void* align_usertype_unique_destructor(void* ptr) { using use_align = std::integral_constant::value > 1) #endif >; if (!pre_aligned) { ptr = align_usertype_pointer(ptr); } if (!pre_shifted) { ptr = static_cast(static_cast(ptr) + sizeof(void*)); } if (!use_align::value) { return static_cast(static_cast(ptr) + 1); } std::size_t space = (std::numeric_limits::max)(); return align(std::alignment_of::value, ptr, space); } template void* align_usertype_unique_tag(void* ptr) { using use_align = std::integral_constant::value > 1) #endif >; if (!pre_aligned) { ptr = align_usertype_unique_destructor(ptr); } if (!pre_shifted) { ptr = static_cast(static_cast(ptr) + sizeof(unique_destructor)); } if (!use_align::value) { return ptr; } std::size_t space = (std::numeric_limits::max)(); return align(std::alignment_of::value, ptr, space); } template void* align_usertype_unique(void* ptr) { typedef std::integral_constant > 1) #endif > use_align; if (!pre_aligned) { ptr = align_usertype_unique_tag(ptr); } if (!pre_shifted) { ptr = static_cast(static_cast(ptr) + sizeof(unique_tag)); } if (!use_align::value) { return ptr; } std::size_t space = (std::numeric_limits::max)(); return align(std::alignment_of_v, ptr, space); } template void* align_user(void* ptr) { typedef std::integral_constant > 1) #endif > use_align; if (!use_align::value) { return ptr; } std::size_t space = (std::numeric_limits::max)(); return align(std::alignment_of_v, ptr, space); } template T** usertype_allocate_pointer(lua_State* L) { typedef std::integral_constant::value > 1) #endif > use_align; if (!use_align::value) { T** pointerpointer = static_cast(alloc_newuserdata(L, sizeof(T*))); return pointerpointer; } constexpr std::size_t initial_size = aligned_space_for(); std::size_t allocated_size = initial_size; void* unadjusted = alloc_newuserdata(L, initial_size); void* adjusted = align(std::alignment_of::value, unadjusted, allocated_size); if (adjusted == nullptr) { // trash allocator can burn in hell lua_pop(L, 1); // luaL_error(L, "if you are the one that wrote this allocator you should feel bad for doing a // worse job than malloc/realloc and should go read some books, yeah?"); luaL_error(L, "cannot properly align memory for '%s'", detail::demangle().data()); } return static_cast(adjusted); } inline bool attempt_alloc(lua_State* L, std::size_t ptr_align, std::size_t ptr_size, std::size_t value_align, std::size_t allocated_size, void*& pointer_adjusted, void*& data_adjusted) { void* adjusted = alloc_newuserdata(L, allocated_size); pointer_adjusted = align(ptr_align, adjusted, allocated_size); if (pointer_adjusted == nullptr) { lua_pop(L, 1); return false; } // subtract size of what we're going to allocate there allocated_size -= ptr_size; adjusted = static_cast(static_cast(pointer_adjusted) + ptr_size); data_adjusted = align(value_align, adjusted, allocated_size); if (data_adjusted == nullptr) { lua_pop(L, 1); return false; } return true; } inline bool attempt_alloc_unique(lua_State* L, std::size_t ptr_align, std::size_t ptr_size, std::size_t real_align, std::size_t allocated_size, void*& pointer_adjusted, void*& dx_adjusted, void*& id_adjusted, void*& data_adjusted) { void* adjusted = alloc_newuserdata(L, allocated_size); pointer_adjusted = align(ptr_align, adjusted, allocated_size); if (pointer_adjusted == nullptr) { lua_pop(L, 1); return false; } allocated_size -= ptr_size; adjusted = static_cast(static_cast(pointer_adjusted) + ptr_size); dx_adjusted = align(std::alignment_of_v, adjusted, allocated_size); if (dx_adjusted == nullptr) { lua_pop(L, 1); return false; } allocated_size -= sizeof(unique_destructor); adjusted = static_cast(static_cast(dx_adjusted) + sizeof(unique_destructor)); id_adjusted = align(std::alignment_of_v, adjusted, allocated_size); if (id_adjusted == nullptr) { lua_pop(L, 1); return false; } allocated_size -= sizeof(unique_tag); adjusted = static_cast(static_cast(id_adjusted) + sizeof(unique_tag)); data_adjusted = align(real_align, adjusted, allocated_size); if (data_adjusted == nullptr) { lua_pop(L, 1); return false; } return true; } template T* usertype_allocate(lua_State* L) { typedef std::integral_constant::value > 1 || std::alignment_of_v > 1) #endif > use_align; if (!use_align::value) { T** pointerpointer = static_cast(alloc_newuserdata(L, sizeof(T*) + sizeof(T))); T*& pointerreference = *pointerpointer; T* allocationtarget = reinterpret_cast(pointerpointer + 1); pointerreference = allocationtarget; return allocationtarget; } constexpr std::size_t initial_size = aligned_space_for(); void* pointer_adjusted; void* data_adjusted; bool result = attempt_alloc(L, std::alignment_of_v, sizeof(T*), std::alignment_of_v, initial_size, pointer_adjusted, data_adjusted); if (!result) { if (pointer_adjusted == nullptr) { luaL_error(L, "aligned allocation of userdata block (pointer section) for '%s' failed", detail::demangle().c_str()); } else { luaL_error(L, "aligned allocation of userdata block (data section) for '%s' failed", detail::demangle().c_str()); } return nullptr; } T** pointerpointer = reinterpret_cast(pointer_adjusted); T*& pointerreference = *pointerpointer; T* allocationtarget = reinterpret_cast(data_adjusted); pointerreference = allocationtarget; return allocationtarget; } template Real* usertype_unique_allocate(lua_State* L, T**& pref, unique_destructor*& dx, unique_tag*& id) { typedef std::integral_constant::value > 1 || std::alignment_of::value > 1 || std::alignment_of::value > 1 || std::alignment_of::value > 1) #endif > use_align; if (!use_align::value) { pref = static_cast(alloc_newuserdata(L, sizeof(T*) + sizeof(detail::unique_destructor) + sizeof(unique_tag) + sizeof(Real))); dx = static_cast(static_cast(pref + 1)); id = static_cast(static_cast(dx + 1)); Real* mem = static_cast(static_cast(id + 1)); return mem; } constexpr std::size_t initial_size = aligned_space_for(); void* pointer_adjusted = nullptr; void* dx_adjusted = nullptr; void* id_adjusted = nullptr; void* data_adjusted = nullptr; bool result = attempt_alloc_unique(L, std::alignment_of_v, sizeof(T*), std::alignment_of_v, initial_size, pointer_adjusted, dx_adjusted, id_adjusted, data_adjusted); if (!result) { if (pointer_adjusted == nullptr) { luaL_error(L, "aligned allocation of userdata block (pointer section) for '%s' failed", detail::demangle().c_str()); } else if (dx_adjusted == nullptr) { luaL_error(L, "aligned allocation of userdata block (deleter section) for '%s' failed", detail::demangle().c_str()); } else { luaL_error(L, "aligned allocation of userdata block (data section) for '%s' failed", detail::demangle().c_str()); } return nullptr; } pref = static_cast(pointer_adjusted); dx = static_cast(dx_adjusted); id = static_cast(id_adjusted); Real* mem = static_cast(data_adjusted); return mem; } template T* user_allocate(lua_State* L) { typedef std::integral_constant > 1) #endif > use_align; if (!use_align::value) { T* pointer = static_cast(alloc_newuserdata(L, sizeof(T))); return pointer; } constexpr std::size_t initial_size = aligned_space_for(); std::size_t allocated_size = initial_size; void* unadjusted = alloc_newuserdata(L, allocated_size); void* adjusted = align(std::alignment_of_v, unadjusted, allocated_size); if (adjusted == nullptr) { lua_pop(L, 1); luaL_error(L, "cannot properly align memory for '%s'", detail::demangle().data()); } return static_cast(adjusted); } template int usertype_alloc_destroy(lua_State* L) noexcept { void* memory = lua_touserdata(L, 1); memory = align_usertype_pointer(memory); T** pdata = static_cast(memory); T* data = *pdata; std::allocator alloc {}; std::allocator_traits>::destroy(alloc, data); return 0; } template int unique_destroy(lua_State* L) noexcept { void* memory = lua_touserdata(L, 1); memory = align_usertype_unique_destructor(memory); unique_destructor& dx = *static_cast(memory); memory = align_usertype_unique_tag(memory); (dx)(memory); return 0; } template int user_alloc_destroy(lua_State* L) noexcept { void* memory = lua_touserdata(L, 1); void* aligned_memory = align_user(memory); T* typed_memory = static_cast(aligned_memory); std::allocator alloc; std::allocator_traits>::destroy(alloc, typed_memory); return 0; } template void usertype_unique_alloc_destroy(void* memory) { void* aligned_memory = align_usertype_unique(memory); Real* typed_memory = static_cast(aligned_memory); std::allocator alloc; std::allocator_traits>::destroy(alloc, typed_memory); } template int cannot_destroy(lua_State* L) { return luaL_error(L, "cannot call the destructor for '%s': it is either hidden (protected/private) or removed with '= " "delete' and thusly this type is being destroyed without properly destroying, invoking undefined " "behavior: please bind a usertype and specify a custom destructor to define the behavior properly", detail::demangle().data()); } template void reserve(T&, std::size_t) { } template void reserve(std::vector& vec, std::size_t hint) { vec.reserve(hint); } template void reserve(std::basic_string& str, std::size_t hint) { str.reserve(hint); } inline bool property_always_true(meta_function) { return true; } struct properties_enrollment_allowed { int& times_through; std::bitset<64>& properties; automagic_enrollments& enrollments; properties_enrollment_allowed(int& times_through_, std::bitset<64>& properties_, automagic_enrollments& enrollments_) : times_through(times_through_), properties(properties_), enrollments(enrollments_) { } bool operator()(meta_function mf) const { bool p = properties[static_cast(mf)]; if (times_through > 0) { return p; } switch (mf) { case meta_function::length: return enrollments.length_operator && !p; case meta_function::pairs: return enrollments.pairs_operator && !p; case meta_function::call: return enrollments.call_operator && !p; case meta_function::less_than: return enrollments.less_than_operator && !p; case meta_function::less_than_or_equal_to: return enrollments.less_than_or_equal_to_operator && !p; case meta_function::equal_to: return enrollments.equal_to_operator && !p; default: break; } return !p; } }; struct indexed_insert { lua_reg_table& registration_table; int& index; indexed_insert(lua_reg_table& registration_table_, int& index_ref_) : registration_table(registration_table_), index(index_ref_) { } void operator()(meta_function meta_function_name_, lua_CFunction c_function_) { registration_table[index] = luaL_Reg { to_string(meta_function_name_).c_str(), c_function_ }; ++index; } }; } // namespace detail namespace stack { template struct field_getter; template struct probe_field_getter; template struct field_setter; template struct unqualified_getter; template struct qualified_getter; template struct qualified_interop_getter; template struct unqualified_interop_getter; template struct popper; template struct unqualified_pusher; template struct unqualified_checker; template struct qualified_checker; template struct unqualified_check_getter; template struct qualified_check_getter; struct probe { bool success; int levels; probe(bool s, int l) : success(s), levels(l) { } operator bool() const { return success; }; }; struct record { int last; int used; record() noexcept : last(), used() { } void use(int count) noexcept { last = count; used += count; } }; namespace stack_detail { template Function* get_function_pointer(lua_State*, int, record&) noexcept; template bool check_function_pointer(lua_State* L, int index, Handler&& handler, record& tracking) noexcept; } // namespace stack_detail } // namespace stack namespace meta { namespace meta_detail { template using adl_sol_lua_get_test_t = decltype(sol_lua_get(types(), static_cast(nullptr), -1, std::declval())); template using adl_sol_lua_interop_get_test_t = decltype(sol_lua_interop_get(types(), static_cast(nullptr), -1, static_cast(nullptr), std::declval())); template using adl_sol_lua_check_test_t = decltype(sol_lua_check(types(), static_cast(nullptr), -1, &no_panic, std::declval())); template using adl_sol_lua_interop_check_test_t = decltype(sol_lua_interop_check(types(), static_cast(nullptr), -1, type::none, &no_panic, std::declval())); template using adl_sol_lua_check_get_test_t = decltype(sol_lua_check_get(types(), static_cast(nullptr), -1, &no_panic, std::declval())); template using adl_sol_lua_push_test_t = decltype(sol_lua_push(static_cast(nullptr), std::declval()...)); template using adl_sol_lua_push_exact_test_t = decltype(sol_lua_push(types(), static_cast(nullptr), std::declval()...)); template inline constexpr bool is_adl_sol_lua_get_v = meta::is_detected_v; template inline constexpr bool is_adl_sol_lua_interop_get_v = meta::is_detected_v; template inline constexpr bool is_adl_sol_lua_check_v = meta::is_detected_v; template inline constexpr bool is_adl_sol_lua_interop_check_v = meta::is_detected_v; template inline constexpr bool is_adl_sol_lua_check_get_v = meta::is_detected_v; template inline constexpr bool is_adl_sol_lua_push_v = meta::is_detected_v; template inline constexpr bool is_adl_sol_lua_push_exact_v = meta::is_detected_v; }} // namespace meta::meta_detail namespace stack { namespace stack_detail { constexpr const char* not_enough_stack_space = "not enough space left on Lua stack"; constexpr const char* not_enough_stack_space_floating = "not enough space left on Lua stack for a floating point number"; constexpr const char* not_enough_stack_space_integral = "not enough space left on Lua stack for an integral number"; constexpr const char* not_enough_stack_space_string = "not enough space left on Lua stack for a string"; constexpr const char* not_enough_stack_space_meta_function_name = "not enough space left on Lua stack for the name of a meta_function"; constexpr const char* not_enough_stack_space_userdata = "not enough space left on Lua stack to create a sol2 userdata"; constexpr const char* not_enough_stack_space_generic = "not enough space left on Lua stack to push valuees"; constexpr const char* not_enough_stack_space_environment = "not enough space left on Lua stack to retrieve environment"; template struct strip { typedef T type; }; template struct strip> { typedef T& type; }; template struct strip> { typedef T& type; }; template struct strip> { typedef T type; }; template using strip_t = typename strip::type; template static int get_size_hint(C& c) { return static_cast(c.size()); } template static int get_size_hint(const std::forward_list&) { // forward_list makes me sad return static_cast(32); } template decltype(auto) unchecked_unqualified_get(lua_State* L, int index, record& tracking) { using Tu = meta::unqualified_t; if constexpr (meta::meta_detail::is_adl_sol_lua_get_v) { return sol_lua_get(types(), L, index, tracking); } else { unqualified_getter g {}; return g.get(L, index, tracking); } } template decltype(auto) unchecked_get(lua_State* L, int index, record& tracking) { if constexpr (meta::meta_detail::is_adl_sol_lua_get_v) { return sol_lua_get(types(), L, index, tracking); } else { qualified_getter g {}; return g.get(L, index, tracking); } } template decltype(auto) unqualified_interop_get(lua_State* L, int index, void* unadjusted_pointer, record& tracking) { using Tu = meta::unqualified_t; if constexpr (meta::meta_detail::is_adl_sol_lua_interop_get_v) { return sol_lua_interop_get(types(), L, index, unadjusted_pointer, tracking); } else { (void)L; (void)index; (void)unadjusted_pointer; (void)tracking; using Ti = stack_detail::strip_t; return std::pair { false, nullptr }; } } template decltype(auto) interop_get(lua_State* L, int index, void* unadjusted_pointer, record& tracking) { if constexpr (meta::meta_detail::is_adl_sol_lua_interop_get_v) { return sol_lua_interop_get(types(), L, index, unadjusted_pointer, tracking); } else { return unqualified_interop_get(L, index, unadjusted_pointer, tracking); } } template bool unqualified_interop_check(lua_State* L, int index, type index_type, Handler&& handler, record& tracking) { using Tu = meta::unqualified_t; if constexpr (meta::meta_detail::is_adl_sol_lua_interop_check_v) { return sol_lua_interop_check(types(), L, index, index_type, std::forward(handler), tracking); } else { (void)L; (void)index; (void)index_type; (void)handler; (void)tracking; return false; } } template bool interop_check(lua_State* L, int index, type index_type, Handler&& handler, record& tracking) { if constexpr (meta::meta_detail::is_adl_sol_lua_interop_check_v) { return sol_lua_interop_check(types(), L, index, index_type, std::forward(handler), tracking); } else { return unqualified_interop_check(L, index, index_type, std::forward(handler), tracking); } } using undefined_method_func = void (*)(stack_reference); struct undefined_metatable { lua_State* L; const char* key; undefined_method_func on_new_table; undefined_metatable(lua_State* l, const char* k, undefined_method_func umf) : L(l), key(k), on_new_table(umf) { } void operator()() const { if (luaL_newmetatable(L, key) == 1) { on_new_table(stack_reference(L, -1)); } lua_setmetatable(L, -2); } }; } // namespace stack_detail inline bool maybe_indexable(lua_State* L, int index = -1) { type t = type_of(L, index); return t == type::userdata || t == type::table; } inline int top(lua_State* L) { return lua_gettop(L); } inline bool is_main_thread(lua_State* L) { int ismainthread = lua_pushthread(L); lua_pop(L, 1); return ismainthread == 1; } inline void coroutine_create_guard(lua_State* L) { if (is_main_thread(L)) { return; } int stacksize = lua_gettop(L); if (stacksize < 1) { return; } if (type_of(L, 1) != type::function) { return; } // well now we're screwed... // we can clean the stack and pray it doesn't destroy anything? lua_pop(L, stacksize); } inline void clear(lua_State* L, int table_index) { lua_pushnil(L); while (lua_next(L, table_index) != 0) { // remove value lua_pop(L, 1); // duplicate key to protect form rawset lua_pushvalue(L, -1); // push new value lua_pushnil(L); // table_index%[key] = nil lua_rawset(L, table_index); } } inline void clear(reference& r) { auto pp = push_pop(r); int stack_index = pp.index_of(r); clear(r.lua_state(), stack_index); } inline void clear(stack_reference& r) { clear(r.lua_state(), r.stack_index()); } inline void clear(lua_State* L_, stateless_reference& r) { r.push(L_); int stack_index = absolute_index(L_, -1); clear(L_, stack_index); r.pop(L_); } inline void clear(lua_State* L_, stateless_stack_reference& r) { clear(L_, r.stack_index()); } template int push(lua_State* L, T&& t, Args&&... args) { using Tu = meta::unqualified_t; if constexpr (meta::meta_detail::is_adl_sol_lua_push_exact_v) { return sol_lua_push(types(), L, std::forward(t), std::forward(args)...); } else if constexpr (meta::meta_detail::is_adl_sol_lua_push_exact_v) { return sol_lua_push(types(), L, std::forward(t), std::forward(args)...); } else if constexpr (meta::meta_detail::is_adl_sol_lua_push_v) { return sol_lua_push(L, std::forward(t), std::forward(args)...); } else { unqualified_pusher p {}; return p.push(L, std::forward(t), std::forward(args)...); } } // overload allows to use a pusher of a specific type, but pass in any kind of args template ::value>> int push(lua_State* L, Arg&& arg, Args&&... args) { using Tu = meta::unqualified_t; if constexpr (meta::meta_detail::is_adl_sol_lua_push_exact_v) { return sol_lua_push(types(), L, std::forward(arg), std::forward(args)...); } else if constexpr (meta::meta_detail::is_adl_sol_lua_push_exact_v) { return sol_lua_push(types(), L, std::forward(arg), std::forward(args)...); } else if constexpr (meta::meta_detail::is_adl_sol_lua_push_v && !detail::is_tagged_v) { return sol_lua_push(L, std::forward(arg), std::forward(args)...); } else { unqualified_pusher p {}; return p.push(L, std::forward(arg), std::forward(args)...); } } template int push_userdata(lua_State* L, T&& t, Args&&... args) { using U = meta::unqualified_t; using Tr = meta::conditional_t, detail::as_pointer_tag>, meta::conditional_t, detail::as_unique_tag, detail::as_value_tag>>; return stack::push(L, std::forward(t), std::forward(args)...); } template int push_userdata(lua_State* L, Arg&& arg, Args&&... args) { using U = meta::unqualified_t; using Tr = meta::conditional_t, detail::as_pointer_tag>, meta::conditional_t, detail::as_unique_tag, detail::as_value_tag>>; return stack::push(L, std::forward(arg), std::forward(args)...); } namespace stack_detail { template int push_reference(lua_State* L, Arg&& arg, Args&&... args) { // clang-format off using use_reference_tag = meta::all< meta::neg> #if SOL_IS_OFF(SOL_FUNCTION_CALL_VALUE_SEMANTICS) , std::is_lvalue_reference, meta::neg>>, meta::neg>>, meta::neg>> #endif >; // clang-format on using Tr = meta::conditional_t>; return stack::push(L, std::forward(arg), std::forward(args)...); } } // namespace stack_detail template int push_reference(lua_State* L, T&& t, Args&&... args) { return stack_detail::push_reference(L, std::forward(t), std::forward(args)...); } template int push_reference(lua_State* L, Arg&& arg, Args&&... args) { return stack_detail::push_reference(L, std::forward(arg), std::forward(args)...); } inline int multi_push(lua_State*) { // do nothing return 0; } template int multi_push(lua_State* L, T&& t, Args&&... args) { int pushcount = push(L, std::forward(t)); void(detail::swallow { (pushcount += stack::push(L, std::forward(args)), 0)... }); return pushcount; } inline int multi_push_reference(lua_State*) { // do nothing return 0; } template int multi_push_reference(lua_State* L, T&& t, Args&&... args) { int pushcount = stack::push_reference(L, std::forward(t)); void(detail::swallow { (pushcount += stack::push_reference(L, std::forward(args)), 0)... }); return pushcount; } template bool unqualified_check(lua_State* L, int index, Handler&& handler, record& tracking) { using Tu = meta::unqualified_t; if constexpr (meta::meta_detail::is_adl_sol_lua_check_v) { return sol_lua_check(types(), L, index, std::forward(handler), tracking); } else { unqualified_checker> c{}; return c.check(L, index, std::forward(handler), tracking); } } template bool unqualified_check(lua_State* L, int index, Handler&& handler) { record tracking {}; return unqualified_check(L, index, std::forward(handler), tracking); } template bool unqualified_check(lua_State* L, int index = -lua_size>::value) { auto handler = &no_panic; return unqualified_check(L, index, handler); } template bool check(lua_State* L, int index, Handler&& handler, record& tracking) { if constexpr (meta::meta_detail::is_adl_sol_lua_check_v) { return sol_lua_check(types(), L, index, std::forward(handler), tracking); } else { using Tu = meta::unqualified_t; qualified_checker> c{}; return c.check(L, index, std::forward(handler), tracking); } } template bool check(lua_State* L, int index, Handler&& handler) { record tracking {}; return check(L, index, std::forward(handler), tracking); } template bool check(lua_State* L, int index = -lua_size>::value) { auto handler = &no_panic; return check(L, index, handler); } template bool check_usertype(lua_State* L, int index, type, Handler&& handler, record& tracking) { using Tu = meta::unqualified_t; using detail_t = meta::conditional_t, detail::as_pointer_tag, detail::as_value_tag>; return check(L, index, std::forward(handler), tracking); } template bool check_usertype(lua_State* L, int index, Handler&& handler, record& tracking) { using Tu = meta::unqualified_t; using detail_t = meta::conditional_t, detail::as_pointer_tag, detail::as_value_tag>; return check(L, index, std::forward(handler), tracking); } template bool check_usertype(lua_State* L, int index, Handler&& handler) { record tracking {}; return check_usertype(L, index, std::forward(handler), tracking); } template bool check_usertype(lua_State* L, int index = -lua_size>::value) { auto handler = &no_panic; return check_usertype(L, index, handler); } template decltype(auto) unqualified_check_get(lua_State* L, int index, Handler&& handler, record& tracking) { using Tu = meta::unqualified_t; if constexpr (meta::meta_detail::is_adl_sol_lua_check_get_v) { return sol_lua_check_get(types(), L, index, std::forward(handler), tracking); } else if constexpr (meta::meta_detail::is_adl_sol_lua_check_get_v) { return sol_lua_check_get(types(), L, index, std::forward(handler), tracking); } else { unqualified_check_getter cg {}; return cg.get(L, index, std::forward(handler), tracking); } } template decltype(auto) unqualified_check_get(lua_State* L, int index, Handler&& handler) { record tracking {}; return unqualified_check_get(L, index, handler, tracking); } template decltype(auto) unqualified_check_get(lua_State* L, int index = -lua_size>::value) { auto handler = &no_panic; return unqualified_check_get(L, index, handler); } template decltype(auto) check_get(lua_State* L, int index, Handler&& handler, record& tracking) { if constexpr (meta::meta_detail::is_adl_sol_lua_check_get_v) { return sol_lua_check_get(types(), L, index, std::forward(handler), tracking); } else { qualified_check_getter cg {}; return cg.get(L, index, std::forward(handler), tracking); } } template decltype(auto) check_get(lua_State* L, int index, Handler&& handler) { record tracking {}; return check_get(L, index, handler, tracking); } template decltype(auto) check_get(lua_State* L, int index = -lua_size>::value) { auto handler = &no_panic; return check_get(L, index, handler); } namespace stack_detail { template bool check_types(lua_State*, int, Handler&&, record&) { return true; } template bool check_types(lua_State* L, int firstargument, Handler&& handler, record& tracking) { if (!stack::check(L, firstargument + tracking.used, handler, tracking)) return false; return check_types(L, firstargument, std::forward(handler), tracking); } template bool check_types(types, lua_State* L, int index, Handler&& handler, record& tracking) { return check_types(L, index, std::forward(handler), tracking); } } // namespace stack_detail template bool multi_check(lua_State* L, int index, Handler&& handler, record& tracking) { return stack_detail::check_types(L, index, std::forward(handler), tracking); } template bool multi_check(lua_State* L, int index, Handler&& handler) { record tracking {}; return multi_check(L, index, std::forward(handler), tracking); } template bool multi_check(lua_State* L, int index) { return multi_check(L, index); } template auto unqualified_get(lua_State* L, int index, record& tracking) -> decltype(stack_detail::unchecked_unqualified_get(L, index, tracking)) { #if SOL_IS_ON(SOL_SAFE_GETTER) static constexpr bool is_op = meta::is_optional_v; if constexpr (is_op) { return stack_detail::unchecked_unqualified_get(L, index, tracking); } else { if (is_lua_reference::value) { return stack_detail::unchecked_unqualified_get(L, index, tracking); } auto op = unqualified_check_get(L, index, type_panic_c_str, tracking); return *std::move(op); } #else return stack_detail::unchecked_unqualified_get(L, index, tracking); #endif } template decltype(auto) unqualified_get(lua_State* L, int index = -lua_size>::value) { record tracking {}; return unqualified_get(L, index, tracking); } template auto get(lua_State* L, int index, record& tracking) -> decltype(stack_detail::unchecked_get(L, index, tracking)) { #if SOL_IS_ON(SOL_SAFE_GETTER) static constexpr bool is_op = meta::is_optional_v; if constexpr (is_op) { return stack_detail::unchecked_get(L, index, tracking); } else { if (is_lua_reference::value) { return stack_detail::unchecked_get(L, index, tracking); } auto op = check_get(L, index, type_panic_c_str, tracking); return *std::move(op); } #else return stack_detail::unchecked_get(L, index, tracking); #endif } template decltype(auto) get(lua_State* L, int index = -lua_size>::value) { record tracking {}; return get(L, index, tracking); } template decltype(auto) get_usertype(lua_State* L, int index, record& tracking) { using UT = meta::conditional_t::value, detail::as_pointer_tag>, detail::as_value_tag>; return get(L, index, tracking); } template decltype(auto) get_usertype(lua_State* L, int index = -lua_size_v>) { record tracking {}; return get_usertype(L, index, tracking); } template decltype(auto) pop(lua_State* L) { return popper {}.pop(L); } template void get_field(lua_State* L, Key&& key) { field_getter, global, raw> {}.get(L, std::forward(key)); } template void get_field(lua_State* L, Key&& key, int tableindex) { field_getter, global, raw> {}.get(L, std::forward(key), tableindex); } template void raw_get_field(lua_State* L, Key&& key) { get_field(L, std::forward(key)); } template void raw_get_field(lua_State* L, Key&& key, int tableindex) { get_field(L, std::forward(key), tableindex); } template probe probe_get_field(lua_State* L, Key&& key) { return probe_field_getter, C, global, raw> {}.get(L, std::forward(key)); } template probe probe_get_field(lua_State* L, Key&& key, int tableindex) { return probe_field_getter, C, global, raw> {}.get(L, std::forward(key), tableindex); } template probe probe_raw_get_field(lua_State* L, Key&& key) { return probe_get_field(L, std::forward(key)); } template probe probe_raw_get_field(lua_State* L, Key&& key, int tableindex) { return probe_get_field(L, std::forward(key), tableindex); } template void set_field(lua_State* L, Key&& key, Value&& value) { field_setter, global, raw> {}.set(L, std::forward(key), std::forward(value)); } template void set_field(lua_State* L, Key&& key, Value&& value, int tableindex) { field_setter, global, raw> {}.set(L, std::forward(key), std::forward(value), tableindex); } template void raw_set_field(lua_State* L, Key&& key, Value&& value) { set_field(L, std::forward(key), std::forward(value)); } template void raw_set_field(lua_State* L, Key&& key, Value&& value, int tableindex) { set_field(L, std::forward(key), std::forward(value), tableindex); } template void modify_unique_usertype_as(const stack_reference& obj, F&& f) { void* raw = lua_touserdata(obj.lua_state(), obj.stack_index()); void* ptr_memory = detail::align_usertype_pointer(raw); void* uu_memory = detail::align_usertype_unique(raw); T& uu = *static_cast(uu_memory); f(uu); *static_cast(ptr_memory) = static_cast(detail::unique_get(obj.lua_state(), uu)); } template void modify_unique_usertype(const stack_reference& obj, F&& f) { using bt = meta::bind_traits>; using T = typename bt::template arg_at<0>; using Tu = meta::unqualified_t; modify_unique_usertype_as(obj, std::forward(f)); } namespace stack_detail { template decltype(auto) check_get_arg(lua_State* L_, int index_, Handler&& handler_, record& tracking_) { if constexpr (meta::meta_detail::is_adl_sol_lua_check_access_v) { sol_lua_check_access(types>(), L_, index_, tracking_); } return check_get(L_, index_, std::forward(handler_), tracking_); } template decltype(auto) unchecked_get_arg(lua_State* L_, int index_, record& tracking_) { if constexpr (meta::meta_detail::is_adl_sol_lua_check_access_v) { sol_lua_check_access(types>(), L_, index_, tracking_); } return unchecked_get(L_, index_, tracking_); } } // namespace stack_detail } // namespace stack namespace detail { template lua_CFunction make_destructor(std::true_type) { if constexpr (is_unique_usertype_v) { return &unique_destroy; } else if constexpr (!std::is_pointer_v) { return &usertype_alloc_destroy; } else { return &cannot_destroy; } } template lua_CFunction make_destructor(std::false_type) { return &cannot_destroy; } template lua_CFunction make_destructor() { return make_destructor(std::is_destructible()); } struct no_comp { template bool operator()(A&&, B&&) const { return false; } }; template int is_check(lua_State* L) { return stack::push(L, stack::check(L, 1, &no_panic)); } template int member_default_to_string(std::true_type, lua_State* L) { decltype(auto) ts = stack::get(L, 1).to_string(); return stack::push(L, std::forward(ts)); } template int member_default_to_string(std::false_type, lua_State* L) { return luaL_error(L, "cannot perform to_string on '%s': no 'to_string' overload in namespace, 'to_string' member " "function, or operator<<(ostream&, ...) present", detail::demangle().data()); } template int adl_default_to_string(std::true_type, lua_State* L) { using namespace std; decltype(auto) ts = to_string(stack::get(L, 1)); return stack::push(L, std::forward(ts)); } template int adl_default_to_string(std::false_type, lua_State* L) { return member_default_to_string(meta::supports_to_string_member(), L); } template int oss_default_to_string(std::true_type, lua_State* L) { std::ostringstream oss; oss << stack::unqualified_get(L, 1); return stack::push(L, oss.str()); } template int oss_default_to_string(std::false_type, lua_State* L) { return adl_default_to_string(meta::supports_adl_to_string(), L); } template int default_to_string(lua_State* L) { return oss_default_to_string(meta::supports_op_left_shift(), L); } template int default_size(lua_State* L) { decltype(auto) self = stack::unqualified_get(L, 1); return stack::push(L, self.size()); } template int comparsion_operator_wrap(lua_State* L) { if constexpr (std::is_void_v) { return stack::push(L, false); } else { auto maybel = stack::unqualified_check_get(L, 1); if (!maybel) { return stack::push(L, false); } auto mayber = stack::unqualified_check_get(L, 2); if (!mayber) { return stack::push(L, false); } decltype(auto) l = *maybel; decltype(auto) r = *mayber; if constexpr (std::is_same_v) { std::equal_to<> op; return stack::push(L, op(detail::ptr(l), detail::ptr(r))); } else { if constexpr (std::is_same_v, Op> // clang-format hack || std::is_same_v, Op> // || std::is_same_v, Op>) { // if (detail::ptr(l) == detail::ptr(r)) { return stack::push(L, true); } } Op op; return stack::push(L, op(detail::deref(l), detail::deref(r))); } } } template void insert_default_registrations(IFx&& ifx, Fx&& fx); template struct get_is_primitive : is_lua_primitive { }; template struct get_is_primitive : meta::neg(), nullptr, -1, std::declval()))>> { }; template struct get_is_primitive : meta::neg>(), nullptr, -1, std::declval()))>> { }; template struct get_is_primitive : get_is_primitive { }; } // namespace detail template struct is_proxy_primitive : detail::get_is_primitive, meta::meta_detail::is_adl_sol_lua_get_v>> { }; } // namespace sol #endif // SOL_STACK_CORE_HPP