From 41ff375d6a210a94ad86dce145044466a129794a Mon Sep 17 00:00:00 2001 From: Bluub Date: Wed, 9 Jun 2021 12:08:40 +0200 Subject: [PATCH] using luabridge, binding MapInfo type and calling update --- CMakeLists.txt | 7 +- ai_scripts/john.lua | 5 +- lib/LuaBridge/LuaBridge.hpp | 7789 +++++++++++++++++ .../IAControllableComponent.cpp | 14 +- .../IAControllableComponent.hpp | 1 + sources/Map/Map.hpp | 36 +- sources/Map/MapInfo.cpp | 21 + sources/Map/MapInfo.hpp | 37 + .../IAControllable/IAControllableSystem.cpp | 24 +- 9 files changed, 7897 insertions(+), 37 deletions(-) create mode 100644 lib/LuaBridge/LuaBridge.hpp create mode 100644 sources/Map/MapInfo.cpp create mode 100644 sources/Map/MapInfo.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index d0dcb4fb..fc6cda92 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,9 +7,10 @@ set(CMAKE_CXX_STANDARD 20) # Find Lua find_package(Lua REQUIRED) #-> CHANGE -include_directories(bomberman ${LUA_INCLUDE_DIR}) #-> ADDED '{LUA_INCLUDE_DIR}' +include_directories(bomberman ${LUA_INCLUDE_DIR}) include_directories(bomberman lib/Ray/sources) include_directories(bomberman lib/wal/sources) +include_directories(bomberman lib/LuaBridge) include_directories(bomberman sources) add_subdirectory(${PROJECT_SOURCE_DIR}/lib/wal) @@ -80,13 +81,15 @@ set(SOURCES sources/System/IAControllable/IAControllableSystem.cpp sources/Component/Tag/TagComponent.cpp sources/Component/Tag/TagComponent.hpp + sources/Map/MapInfo.hpp + sources/Map/MapInfo.cpp ) add_executable(bomberman sources/main.cpp ${SOURCES} ) -target_include_directories(bomberman PUBLIC sources ${LUA_INCLUDE_DIR}) +target_include_directories(bomberman PUBLIC sources ${LUA_INCLUDE_DIR} lib/LuaBridge) target_link_libraries(bomberman PUBLIC wal ray ${LUA_LIBRARIES}) add_executable(unit_tests EXCLUDE_FROM_ALL diff --git a/ai_scripts/john.lua b/ai_scripts/john.lua index e6c274cf..5bec036a 100644 --- a/ai_scripts/john.lua +++ b/ai_scripts/john.lua @@ -2,6 +2,7 @@ function Sum(a,b) return a + b; end -function Update() +function Update(info) return 1, 1, false, false; -end + --return info.x, info.y, (info.z ~= 1), (info.type ~= 0); +end \ No newline at end of file diff --git a/lib/LuaBridge/LuaBridge.hpp b/lib/LuaBridge/LuaBridge.hpp new file mode 100644 index 00000000..d8e84159 --- /dev/null +++ b/lib/LuaBridge/LuaBridge.hpp @@ -0,0 +1,7789 @@ +// https://github.com/kunitoki/LuaBridge3 +// Copyright 2021, Lucio Asnaghi +// SPDX-License-Identifier: MIT + +// clang-format off + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Begin File: Source/LuaBridge/detail/Config.h + +// https://github.com/kunitoki/LuaBridge3 +// Copyright 2020, Lucio Asnaghi +// Copyright 2020, Dmitry Tarakanov +// Copyright 2019, George Tokmaji +// SPDX-License-Identifier: MIT + +#if !(__cplusplus >= 201703L || (defined(_MSC_VER) && _HAS_CXX17)) +#error LuaBridge 3 requires a compliant C++17 compiler, or C++17 has not been enabled ! +#endif + +#if defined(_MSC_VER) +#if _CPPUNWIND || _HAS_EXCEPTIONS +#define LUABRIDGE_HAS_EXCEPTIONS 1 +#else +#define LUABRIDGE_HAS_EXCEPTIONS 0 +#endif +#elif defined(__clang__) +#if __EXCEPTIONS && __has_feature(cxx_exceptions) +#define LUABRIDGE_HAS_EXCEPTIONS 1 +#else +#define LUABRIDGE_HAS_EXCEPTIONS 0 +#endif +#elif defined(__GNUC__) +#if defined(__cpp_exceptions) || defined(__EXCEPTIONS) +#define LUABRIDGE_HAS_EXCEPTIONS 1 +#else +#define LUABRIDGE_HAS_EXCEPTIONS 0 +#endif +#endif + +// End File: Source/LuaBridge/detail/Config.h + +// Begin File: Source/LuaBridge/detail/LuaHelpers.h + +// https://github.com/kunitoki/LuaBridge3 +// Copyright 2020, Lucio Asnaghi +// Copyright 2012, Vinnie Falco +// Copyright 2007, Nathan Reed +// SPDX-License-Identifier: MIT + +namespace luabridge { + +// These are for Lua versions prior to 5.2.0. +#if LUA_VERSION_NUM < 502 + +using lua_Unsigned = lua_Integer; + +inline int lua_absindex(lua_State* L, int idx) +{ + if (idx > LUA_REGISTRYINDEX && idx < 0) + return lua_gettop(L) + idx + 1; + else + return idx; +} + +inline void lua_rawgetp(lua_State* L, int idx, void const* p) +{ + idx = lua_absindex(L, idx); + lua_pushlightuserdata(L, const_cast(p)); + lua_rawget(L, idx); +} + +inline void lua_rawsetp(lua_State* L, int idx, void const* p) +{ + idx = lua_absindex(L, idx); + luaL_checkstack(L, 1, "not enough stack slots"); + lua_pushlightuserdata(L, const_cast(p)); + lua_insert(L, -2); + lua_rawset(L, idx); +} + +#define LUA_OPEQ 1 +#define LUA_OPLT 2 +#define LUA_OPLE 3 + +inline int lua_compare(lua_State* L, int idx1, int idx2, int op) +{ + switch (op) + { + case LUA_OPEQ: + return lua_equal(L, idx1, idx2); + break; + + case LUA_OPLT: + return lua_lessthan(L, idx1, idx2); + break; + + case LUA_OPLE: + return lua_equal(L, idx1, idx2) || lua_lessthan(L, idx1, idx2); + break; + + default: + return 0; + }; +} + +inline int get_length(lua_State* L, int idx) +{ + return int(lua_objlen(L, idx)); +} + +#else +inline int get_length(lua_State* L, int idx) +{ + lua_len(L, idx); + int len = int(luaL_checknumber(L, -1)); + lua_pop(L, 1); + return len; +} + +#endif + +#ifndef LUA_OK +#define LUABRIDGE_LUA_OK 0 +#else +#define LUABRIDGE_LUA_OK LUA_OK +#endif + +/** + * @brief Helper for unused vars. + */ +template +constexpr void unused(Args&&...) +{ +} + +/** + * @brief Helper to throw or return an error code. + */ +template +std::error_code throw_or_error_code(ErrorType error) +{ +#if LUABRIDGE_HAS_EXCEPTIONS + throw T(makeErrorCode(error).message().c_str()); +#else + return makeErrorCode(error); +#endif +} + +template +std::error_code throw_or_error_code(lua_State* L, ErrorType error) +{ +#if LUABRIDGE_HAS_EXCEPTIONS + throw T(L, makeErrorCode(error)); +#else + return (void)L, makeErrorCode(error); +#endif +} + +/** + * @brief Helper to throw or assert. + */ +template +void throw_or_assert(Args&&... args) +{ +#if LUABRIDGE_HAS_EXCEPTIONS + throw T(std::forward(args)...); +#else + unused(std::forward(args)...); + assert(false); +#endif +} + +/** + * @brief Helper to set unsigned. + */ +template +void pushunsigned(lua_State* L, T value) +{ +#if LUA_VERSION_NUM != 502 + lua_pushinteger(L, static_cast(value)); +#else + lua_pushunsigned(L, static_cast(value)); +#endif +} + +/** + * @brief Get a table value, bypassing metamethods. + */ +inline void rawgetfield(lua_State* L, int index, char const* key) +{ + assert(lua_istable(L, index)); + index = lua_absindex(L, index); + lua_pushstring(L, key); + lua_rawget(L, index); +} + +/** + * @brief Set a table value, bypassing metamethods. + */ +inline void rawsetfield(lua_State* L, int index, char const* key) +{ + assert(lua_istable(L, index)); + index = lua_absindex(L, index); + lua_pushstring(L, key); + lua_insert(L, -2); + lua_rawset(L, index); +} + +/** + * @brief Returns true if the value is a full userdata (not light). + */ +[[nodiscard]] inline bool isfulluserdata(lua_State* L, int index) +{ + return lua_isuserdata(L, index) && !lua_islightuserdata(L, index); +} + +/** + * @brief Test lua_State objects for global equality. + * + * This can determine if two different lua_State objects really point + * to the same global state, such as when using coroutines. + * + * @note This is used for assertions. + */ +[[nodiscard]] inline bool equalstates(lua_State* L1, lua_State* L2) +{ + return lua_topointer(L1, LUA_REGISTRYINDEX) == lua_topointer(L2, LUA_REGISTRYINDEX); +} + +/** + * @brief Return an aligned pointer of type T. + */ +template +[[nodiscard]] T* align(void* ptr) noexcept +{ + auto address = reinterpret_cast(ptr); + + auto offset = address % alignof(T); + auto aligned_address = (offset == 0) ? address : (address + alignof(T) - offset); + + return reinterpret_cast(aligned_address); +} + +/** + * @brief Return the space needed to align the type T on an unaligned address. + */ +template +[[nodiscard]] size_t maximum_space_needed_to_align() noexcept +{ + return sizeof(T) + alignof(T) - 1; +} + +/** + * @brief Allocate lua userdata taking into account alignment. + * + * Using this instead of lua_newuserdata directly prevents alignment warnings on 64bits platforms. + */ +template +void* lua_newuserdata_aligned(lua_State* L, Args&&... args) +{ + void* pointer = lua_newuserdata(L, maximum_space_needed_to_align()); + + T* aligned = align(pointer); + + new (aligned) T(std::forward(args)...); + + return pointer; +} + +/** + * @brief Deallocate lua userdata taking into account alignment. + */ +template +int lua_deleteuserdata_aligned(lua_State* L) +{ + assert(isfulluserdata(L, 1)); + + T* aligned = align(lua_touserdata(L, 1)); + aligned->~T(); + + return 0; +} + +/** + * @brief Helper to write a lua string error. + */ +inline void writestringerror(const char* fmt, const char* text) +{ + fprintf(stderr, fmt, text); + fflush(stderr); +} + +} // namespace luabridge + +// End File: Source/LuaBridge/detail/LuaHelpers.h + +// Begin File: Source/LuaBridge/detail/Errors.h + +// https://github.com/vinniefalco/LuaBridge +// Copyright 2021, Lucio Asnaghi +// SPDX-License-Identifier: MIT + +namespace luabridge { + +//================================================================================================= +/** + * @brief LuaBridge error codes. + */ +enum class ErrorCode +{ + ClassNotRegistered = 1, + + LuaFunctionCallFailed, +}; + +//================================================================================================= +namespace detail { +struct ErrorCategory : std::error_category +{ + const char* name() const noexcept override + { + return "luabridge"; + } + + std::string message(int ev) const override + { + switch (static_cast(ev)) + { + case ErrorCode::ClassNotRegistered: + return "The class is not registered in LuaBridge"; + + case ErrorCode::LuaFunctionCallFailed: + return "The lua function invocation raised an error"; + + default: + return "Unknown error"; + } + } + + static const ErrorCategory& getInstance() noexcept + { + static ErrorCategory category; + return category; + } +}; +} // namespace detail + +//================================================================================================= +/** + * @brief Construct an error code from the error enum. + */ +inline std::error_code makeErrorCode(ErrorCode e) +{ + return { static_cast(e), detail::ErrorCategory::getInstance() }; +} + +} // namespace luabridge + +namespace std { +template <> struct is_error_code_enum : true_type {}; +} // namespace std + +// End File: Source/LuaBridge/detail/Errors.h + +// Begin File: Source/LuaBridge/detail/LuaException.h + +// https://github.com/kunitoki/LuaBridge3 +// Copyright 2021, Lucio Asnaghi +// Copyright 2012, Vinnie Falco +// Copyright 2008, Nigel Atkinson +// SPDX-License-Identifier: MIT + +namespace luabridge { + +//================================================================================================ +class LuaException : public std::exception +{ +public: + //============================================================================================= + /** + * @brief Construct a LuaException after a lua_pcall(). + * + * Assumes the error string is on top of the stack, but provides a generic error message otherwise. + */ + LuaException(lua_State* L, std::error_code code) + : m_L(L) + , m_code(code) + { + } + + ~LuaException() noexcept override + { + } + + //============================================================================================= + /** + * @brief Return the error message. + */ + const char* what() const noexcept override + { + return m_what.c_str(); + } + + //============================================================================================= + /** + * @brief Throw an exception or raises a luaerror when exceptions are disabled. + * + * This centralizes all the exceptions thrown, so that we can set breakpoints before the stack is + * unwound, or otherwise customize the behavior. + */ + template + static void raise(const Exception& e) + { + assert(areExceptionsEnabled()); + +#if LUABRIDGE_HAS_EXCEPTIONS + throw e; +#else + unused(e); + + std::abort(); +#endif + } + + //============================================================================================= + /** + * @brief Check if exceptions are enabled. + */ + static bool areExceptionsEnabled() noexcept + { + return exceptionsEnabled(); + } + + /** + * @brief Initializes error handling. + * + * Subsequent Lua errors are translated to C++ exceptions, or logging and abort if exceptions are disabled. + */ + static void enableExceptions(lua_State* L) noexcept + { + exceptionsEnabled() = true; + + lua_atpanic(L, panicHandlerCallback); + } + +private: + struct FromLua {}; + + LuaException(lua_State* L, std::error_code code, FromLua) + : m_L(L) + , m_code(code) + { + whatFromStack(); + } + + void whatFromStack() + { + std::stringstream ss; + + const char* errorText = nullptr; + + if (lua_gettop(m_L) > 0) + { + errorText = lua_tostring(m_L, -1); + lua_pop(m_L, 1); + } + + ss << (errorText ? errorText : "Unknown error") << " (code=" << m_code.message() << ")"; + + m_what = std::move(ss).str(); + } + + static int panicHandlerCallback(lua_State* L) + { +#if LUABRIDGE_HAS_EXCEPTIONS + throw LuaException(L, makeErrorCode(ErrorCode::LuaFunctionCallFailed), FromLua{}); +#else + unused(L); + + std::abort(); +#endif + } + + static bool& exceptionsEnabled() + { + static bool areExceptionsEnabled = false; + return areExceptionsEnabled; + } + + lua_State* m_L = nullptr; + std::error_code m_code; + std::string m_what; +}; + +//================================================================================================= +/** + * @brief Initializes error handling using C++ exceptions. + * + * Subsequent Lua errors are translated to C++ exceptions. It aborts the application if called when no exceptions. + */ +inline void enableExceptions(lua_State* L) noexcept +{ +#if LUABRIDGE_HAS_EXCEPTIONS + LuaException::enableExceptions(L); +#else + unused(L); + + assert(false); // Never call this function when exceptions are not enabled. +#endif +} + +} // namespace luabridge + +// End File: Source/LuaBridge/detail/LuaException.h + +// Begin File: Source/LuaBridge/detail/ClassInfo.h + +// https://github.com/kunitoki/LuaBridge3 +// Copyright 2020, Lucio Asnaghi +// Copyright 2020, Dmitry Tarakanov +// Copyright 2012, Vinnie Falco +// SPDX-License-Identifier: MIT + +namespace luabridge { +namespace detail { + +//================================================================================================= +/** + * @brief A unique key for a type name in a metatable. + */ +inline const void* getTypeKey() noexcept +{ + return reinterpret_cast(0x71); +} + +//================================================================================================= +/** + * @brief The key of a const table in another metatable. + */ +inline const void* getConstKey() noexcept +{ + return reinterpret_cast(0xc07); +} + +//================================================================================================= +/** + * @brief The key of a class table in another metatable. + */ +inline const void* getClassKey() noexcept +{ + return reinterpret_cast(0xc1a); +} + +//================================================================================================= +/** + * @brief The key of a propget table in another metatable. + */ +inline const void* getPropgetKey() noexcept +{ + return reinterpret_cast(0x6e7); +} + +//================================================================================================= +/** + * @brief The key of a propset table in another metatable. + */ +inline const void* getPropsetKey() noexcept +{ + return reinterpret_cast(0x5e7); +} + +//================================================================================================= +/** + * @brief The key of a static table in another metatable. + */ +inline const void* getStaticKey() noexcept +{ + return reinterpret_cast(0x57a); +} + +//================================================================================================= +/** + * @brief The key of a parent table in another metatable. + */ +inline const void* getParentKey() noexcept +{ + return reinterpret_cast(0xdad); +} + +//================================================================================================= +/** + * @brief Get the key for the static table in the Lua registry. + * + * The static table holds the static data members, static properties, and static member functions for a class. + */ +template +const void* getStaticRegistryKey() noexcept +{ + static char value; + return std::addressof(value); +} + +//================================================================================================= +/** + * @brief Get the key for the class table in the Lua registry. + * + * The class table holds the data members, properties, and member functions of a class. Read-only data and properties, and const + * member functions are also placed here (to save a lookup in the const table). + */ +template +const void* getClassRegistryKey() noexcept +{ + static char value; + return std::addressof(value); +} + +//================================================================================================= +/** + * @brief Get the key for the const table in the Lua registry. + * + * The const table holds read-only data members and properties, and const member functions of a class. + */ +template +const void* getConstRegistryKey() noexcept +{ + static char value; + return std::addressof(value); +} + +} // namespace detail +} // namespace luabridge + +// End File: Source/LuaBridge/detail/ClassInfo.h + +// Begin File: Source/LuaBridge/detail/TypeTraits.h + +// https://github.com/kunitoki/LuaBridge3 +// Copyright 2020, Lucio Asnaghi +// Copyright 2019, Dmitry Tarakanov +// Copyright 2012, Vinnie Falco +// SPDX-License-Identifier: MIT + +namespace luabridge { + +//================================================================================================= +/** + * @brief Container traits. + * + * Unspecialized ContainerTraits has the isNotContainer typedef for SFINAE. All user defined containers must supply an appropriate + * specialization for ContinerTraits (without the alias isNotContainer). The containers that come with LuaBridge also come with the + * appropriate ContainerTraits specialization. + * + * @note See the corresponding declaration for details. + * + * A specialization of ContainerTraits for some generic type ContainerType looks like this: + * + * @code + * + * template + * struct ContainerTraits> + * { + * using Type = T; + * + * static ContainerType construct(T* c) + * { + * return c; // Implementation-dependent on ContainerType + * } + * + * static T* get(const ContainerType& c) + * { + * return c.get(); // Implementation-dependent on ContainerType + * } + * }; + * + * @endcode + */ +template +struct ContainerTraits +{ + using IsNotContainer = bool; + + using Type = T; +}; + +/** + * @brief Register shared_ptr support as container. + * + * @tparam T Class that is hold by the shared_ptr, must inherit from std::enable_shared_from_this. + */ +template +struct ContainerTraits> +{ + static_assert(std::is_base_of_v, T>); + + using Type = T; + + static std::shared_ptr construct(T* t) + { + return t->shared_from_this(); + } + + static T* get(const std::shared_ptr& c) + { + return c.get(); + } +}; + +namespace detail { + +//================================================================================================= +/** + * @brief Determine if type T is a container. + * + * To be considered a container, there must be a specialization of ContainerTraits with the required fields. + */ +template +class IsContainer +{ +private: + typedef char yes[1]; // sizeof (yes) == 1 + typedef char no[2]; // sizeof (no) == 2 + + template + static constexpr no& test(typename C::IsNotContainer*); + + template + static constexpr yes& test(...); + +public: + static constexpr bool value = sizeof(test>(0)) == sizeof(yes); +}; + +} // namespace detail +} // namespace luabridge + +// End File: Source/LuaBridge/detail/TypeTraits.h + +// Begin File: Source/LuaBridge/detail/Userdata.h + +// https://github.com/kunitoki/LuaBridge3 +// Copyright 2020, Lucio Asnaghi +// Copyright 2019, Dmitry Tarakanov +// Copyright 2012, Vinnie Falco +// SPDX-License-Identifier: MIT + +namespace luabridge { +namespace detail { + +//================================================================================================= +/** + * @brief Return the identity pointer for our lightuserdata tokens. + * + * Because of Lua's dynamic typing and our improvised system of imposing C++ class structure, there is the possibility that executing + * scripts may knowingly or unknowingly cause invalid data to get passed to the C functions created by LuaBridge. + * + * In particular, our security model addresses the following: + * + * 1. Scripts cannot create a userdata (ignoring the debug lib). + * + * 2. Scripts cannot create a lightuserdata (ignoring the debug lib). + * + * 3. Scripts cannot set the metatable on a userdata. + */ + +/** + * @brief Interface to a class pointer retrievable from a userdata. + */ +class Userdata +{ +private: + //============================================================================================= + /** + * @brief Validate and retrieve a Userdata on the stack. + * + * The Userdata must exactly match the corresponding class table or const table, or else a Lua error is raised. This is used for the + * __gc metamethod. + */ + static Userdata* getExactClass(lua_State* L, int index, const void* classKey) + { + return (void)classKey, static_cast(lua_touserdata(L, lua_absindex(L, index))); + } + + //============================================================================================= + /** + * @brief Validate and retrieve a Userdata on the stack. + * + * The Userdata must be derived from or the same as the given base class, identified by the key. If canBeConst is false, generates + * an error if the resulting Userdata represents to a const object. We do the type check first so that the error message is informative. + */ + static Userdata* getClass(lua_State* L, + int index, + const void* registryConstKey, + const void* registryClassKey, + bool canBeConst) + { + index = lua_absindex(L, index); + + lua_getmetatable(L, index); // Stack: object metatable (ot) | nil + if (!lua_istable(L, -1)) + { + lua_rawgetp(L, LUA_REGISTRYINDEX, registryClassKey); // Stack: registry metatable (rt) | nil + return throwBadArg(L, index); + } + + lua_rawgetp(L, -1, getConstKey()); // Stack: ot | nil, const table (co) | nil + assert(lua_istable(L, -1) || lua_isnil(L, -1)); + + // If const table is NOT present, object is const. Use non-const registry table + // if object cannot be const, so constness validation is done automatically. + // E.g. nonConstFn (constObj) + // -> canBeConst = false, isConst = true + // -> 'Class' registry table, 'const Class' object table + // -> 'expected Class, got const Class' + bool isConst = lua_isnil(L, -1); // Stack: ot | nil, nil, rt + if (isConst && canBeConst) + { + lua_rawgetp(L, LUA_REGISTRYINDEX, registryConstKey); // Stack: ot, nil, rt + } + else + { + lua_rawgetp(L, LUA_REGISTRYINDEX, registryClassKey); // Stack: ot, co, rt + } + + lua_insert(L, -3); // Stack: rt, ot, co | nil + lua_pop(L, 1); // Stack: rt, ot + + for (;;) + { + if (lua_rawequal(L, -1, -2)) // Stack: rt, ot + { + lua_pop(L, 2); // Stack: - + return static_cast(lua_touserdata(L, index)); + } + + // Replace current metatable with it's base class. + lua_rawgetp(L, -1, getParentKey()); // Stack: rt, ot, parent ot (pot) | nil + + if (lua_isnil(L, -1)) // Stack: rt, ot, nil + { + // Drop the object metatable because it may be some parent metatable + lua_pop(L, 2); // Stack: rt + return throwBadArg(L, index); + } + + lua_remove(L, -2); // Stack: rt, pot + } + + // no return + } + + static bool isInstance(lua_State* L, int index, const void* registryClassKey) + { + index = lua_absindex(L, index); + + int result = lua_getmetatable(L, index); // Stack: object metatable (ot) | nothing + if (result == 0) + return false; // Nothing was pushed on the stack + + if (!lua_istable(L, -1)) + { + lua_pop(L, 1); // Stack: - + return false; + } + + lua_rawgetp(L, LUA_REGISTRYINDEX, registryClassKey); // Stack: ot, rt + lua_insert(L, -2); // Stack: rt, ot + + for (;;) + { + if (lua_rawequal(L, -1, -2)) // Stack: rt, ot + { + lua_pop(L, 2); // Stack: - + return true; + } + + // Replace current metatable with it's base class. + lua_rawgetp(L, -1, getParentKey()); // Stack: rt, ot, parent ot (pot) | nil + + if (lua_isnil(L, -1)) // Stack: rt, ot, nil + { + lua_pop(L, 3); // Stack: - + return false; + } + + lua_remove(L, -2); // Stack: rt, pot + } + } + + static Userdata* throwBadArg(lua_State* L, int index) + { + assert(lua_istable(L, -1) || lua_isnil(L, -1)); // Stack: rt | nil + + const char* expected = 0; + if (lua_isnil(L, -1)) // Stack: nil + { + expected = "unregistered class"; + } + else + { + lua_rawgetp(L, -1, getTypeKey()); // Stack: rt, registry type + expected = lua_tostring(L, -1); + } + + const char* got = 0; + if (lua_isuserdata(L, index)) + { + lua_getmetatable(L, index); // Stack: ..., ot | nil + if (lua_istable(L, -1)) // Stack: ..., ot + { + lua_rawgetp(L, -1, getTypeKey()); // Stack: ..., ot, object type | nil + if (lua_isstring(L, -1)) + { + got = lua_tostring(L, -1); + } + } + } + + if (!got) + { + got = lua_typename(L, lua_type(L, index)); + } + + luaL_argerror(L, index, lua_pushfstring(L, "%s expected, got %s", expected, got)); + return 0; + } + +public: + virtual ~Userdata() {} + + //============================================================================================= + /** + * @brief Returns the Userdata* if the class on the Lua stack matches. + * + * If the class does not match, a Lua error is raised. + * + * @tparam T A registered user class. + * + * @param L A Lua state. + * @param index The index of an item on the Lua stack. + * + * @return A userdata pointer if the class matches. + */ + template + static Userdata* getExact(lua_State* L, int index) + { + return getExactClass(L, index, detail::getClassRegistryKey()); + } + + //============================================================================================= + /** + * @brief Get a pointer to the class from the Lua stack. + * + * If the object is not the class or a subclass, or it violates the const-ness, a Lua error is raised. + * + * @tparam T A registered user class. + * + * @param L A Lua state. + * @param index The index of an item on the Lua stack. + * @param canBeConst TBD + * + * @return A pointer if the class and constness match. + */ + template + static T* get(lua_State* L, int index, bool canBeConst) + { + if (lua_isnil(L, index)) + return nullptr; + + return static_cast(getClass(L, + index, + detail::getConstRegistryKey(), + detail::getClassRegistryKey(), + canBeConst) + ->getPointer()); + } + + template + static bool isInstance(lua_State* L, int index) + { + return isInstance(L, index, detail::getClassRegistryKey()); + } + +protected: + Userdata() = default; + + /** + * @brief Get an untyped pointer to the contained class. + */ + void* getPointer() const noexcept + { + return m_p; + } + + void* m_p = nullptr; // subclasses must set this +}; + +//================================================================================================= +/** + * @brief Wraps a class object stored in a Lua userdata. + * + * The lifetime of the object is managed by Lua. The object is constructed inside the userdata using placement new. + */ +template +class UserdataValue : public Userdata +{ +public: + /** + * @brief Push a T via placement new. + * + * The caller is responsible for calling placement new using the returned uninitialized storage. + * + * @param L A Lua state. + * + * @return An object referring to the newly created userdata value. + */ + static UserdataValue* place(lua_State* L, std::error_code& ec) + { + auto* ud = new (lua_newuserdata(L, sizeof(UserdataValue))) UserdataValue(); + + lua_rawgetp(L, LUA_REGISTRYINDEX, detail::getClassRegistryKey()); + + if (!lua_istable(L, -1)) + { + lua_pop(L, 1); // possibly: a nil + + ud->~UserdataValue(); + + ec = throw_or_error_code(L, ErrorCode::ClassNotRegistered); + + return nullptr; + } + + lua_setmetatable(L, -2); + + return ud; + } + + /** + * @brief Push T via copy construction from U. + * + * @tparam U A container type. + * + * @param L A Lua state. + * @param u A container object l-value reference. + * @param ec Error code that will be set in case of failure to push on the lua stack. + */ + template + static bool push(lua_State* L, const U& u, std::error_code& ec) + { + auto* ud = place(L, ec); + + if (!ud) + return false; + + new (ud->getObject()) U(u); + + ud->commit(); + + return true; + } + + /** + * @brief Push T via move construction from U. + * + * @tparam U A container type. + * + * @param L A Lua state. + * @param u A container object r-value reference. + * @param ec Error code that will be set in case of failure to push on the lua stack. + */ + template + static bool push(lua_State* L, U&& u, std::error_code& ec) + { + auto* ud = place(L, ec); + + if (!ud) + return false; + + new (ud->getObject()) U(std::move(u)); + + ud->commit(); + + return true; + } + + /** + * @brief Confirm object construction. + */ + void commit() noexcept + { + m_p = getObject(); + } + + T* getObject() noexcept + { + // If this fails to compile it means you forgot to provide + // a Container specialization for your container! + return reinterpret_cast(&m_storage); + } + +private: + /** + * @brief Used for placement construction. + */ + UserdataValue() noexcept + { + m_p = nullptr; + } + + ~UserdataValue() + { + if (getPointer() != nullptr) + { + getObject()->~T(); + } + } + + UserdataValue(const UserdataValue&); + UserdataValue operator=(const UserdataValue&); + + std::aligned_storage_t m_storage; +}; + +//================================================================================================= +/** + * @brief Wraps a pointer to a class object inside a Lua userdata. + * + * The lifetime of the object is managed by C++. + */ +class UserdataPtr : public Userdata +{ +public: + /** + * @brief Push non-const pointer to object. + * + * @tparam T A user registered class. + * + * @param L A Lua state. + * @param p A pointer to the user class instance. + * @param ec Error code that will be set in case of failure to push on the lua stack. + */ + template + static bool push(lua_State* L, T* p, std::error_code& ec) + { + if (p) + return push(L, p, getClassRegistryKey(), ec); + + lua_pushnil(L); + return true; + } + + /** + * @brief Push const pointer to object. + * + * @tparam T A user registered class. + * + * @param L A Lua state. + * @param p A pointer to the user class instance. + * @param ec Error code that will be set in case of failure to push on the lua stack. + */ + template + static bool push(lua_State* L, const T* p, std::error_code& ec) + { + if (p) + return push(L, p, getConstRegistryKey(), ec); + + lua_pushnil(L); + return true; + } + +private: + /** + * @brief Push a pointer to object using metatable key. + */ + static bool push(lua_State* L, const void* p, const void* key, std::error_code& ec) + { + auto* ptr = new (lua_newuserdata(L, sizeof(UserdataPtr))) UserdataPtr(const_cast(p)); + + lua_rawgetp(L, LUA_REGISTRYINDEX, key); + + if (!lua_istable(L, -1)) + { + lua_pop(L, 1); // possibly: a nil + + ptr->~UserdataPtr(); + + ec = throw_or_error_code(L, ErrorCode::ClassNotRegistered); + + return false; + } + + lua_setmetatable(L, -2); + + return true; + } + + explicit UserdataPtr(void* p) + { + // Can't construct with a null pointer! + assert(p != nullptr); + + m_p = p; + } + + UserdataPtr(const UserdataPtr&); + UserdataPtr operator=(const UserdataPtr&); +}; + +//============================================================================ +/** + * @brief Wraps a container that references a class object. + * + * The template argument C is the container type, ContainerTraits must be specialized on C or else a compile error will result. + */ +template +class UserdataShared : public Userdata +{ +public: + ~UserdataShared() = default; + + /** + * @brief Construct from a container to the class or a derived class. + * + * @tparam U A container type. + * + * @param u A container object reference. + */ + template + explicit UserdataShared(const U& u) : m_c(u) + { + m_p = const_cast(reinterpret_cast((ContainerTraits::get(m_c)))); + } + + /** + * @brief Construct from a pointer to the class or a derived class. + * + * @tparam U A container type. + * + * @param u A container object pointer. + */ + template + explicit UserdataShared(U* u) : m_c(u) + { + m_p = const_cast(reinterpret_cast((ContainerTraits::get(m_c)))); + } + +private: + UserdataShared(const UserdataShared&); + UserdataShared& operator=(const UserdataShared&); + + C m_c; +}; + +//================================================================================================= +/** + * @brief SFINAE helper for non-const objects. + */ +template +struct UserdataSharedHelper +{ + using T = std::remove_const_t::Type>; + + static bool push(lua_State* L, const C& c, std::error_code& ec) + { + if (ContainerTraits::get(c) != nullptr) + { + auto* us = new (lua_newuserdata(L, sizeof(UserdataShared))) UserdataShared(c); + + lua_rawgetp(L, LUA_REGISTRYINDEX, getClassRegistryKey()); + + if (!lua_istable(L, -1)) + { + lua_pop(L, 1); // possibly: a nil + + us->~UserdataShared(); + + ec = throw_or_error_code(L, ErrorCode::ClassNotRegistered); + + return false; + } + + lua_setmetatable(L, -2); + } + else + { + lua_pushnil(L); + } + + return true; + } + + static bool push(lua_State* L, T* t, std::error_code& ec) + { + if (t) + { + auto* us = new (lua_newuserdata(L, sizeof(UserdataShared))) UserdataShared(t); + + lua_rawgetp(L, LUA_REGISTRYINDEX, getClassRegistryKey()); + + if (!lua_istable(L, -1)) + { + lua_pop(L, 1); // possibly: a nil + + us->~UserdataShared(); + + ec = throw_or_error_code(L, ErrorCode::ClassNotRegistered); + + return false; + } + + lua_setmetatable(L, -2); + } + else + { + lua_pushnil(L); + } + + return true; + } +}; + +/** + * @brief SFINAE helper for const objects. + */ +template +struct UserdataSharedHelper +{ + using T = std::remove_const_t::Type>; + + static bool push(lua_State* L, const C& c, std::error_code& ec) + { + if (ContainerTraits::get(c) != nullptr) + { + auto* us = new (lua_newuserdata(L, sizeof(UserdataShared))) UserdataShared(c); + + lua_rawgetp(L, LUA_REGISTRYINDEX, getConstRegistryKey()); + + if (!lua_istable(L, -1)) + { + lua_pop(L, 1); // possibly: a nil + + us->~UserdataShared(); + + ec = throw_or_error_code(L, ErrorCode::ClassNotRegistered); + + return false; + } + + lua_setmetatable(L, -2); + } + else + { + lua_pushnil(L); + } + + return true; + } + + static bool push(lua_State* L, T* t, std::error_code& ec) + { + if (t) + { + auto* us = new (lua_newuserdata(L, sizeof(UserdataShared))) UserdataShared(t); + + lua_rawgetp(L, LUA_REGISTRYINDEX, getConstRegistryKey()); + + if (!lua_istable(L, -1)) + { + lua_pop(L, 1); // possibly: a nil + + us->~UserdataShared(); + + ec = throw_or_error_code(L, ErrorCode::ClassNotRegistered); + + return false; + } + + lua_setmetatable(L, -2); + } + else + { + lua_pushnil(L); + } + + return true; + } +}; + +//================================================================================================= +/** + * @brief Pass by container. + * + * The container controls the object lifetime. Typically this will be a lifetime shared by C++ and Lua using a reference count. Because of type + * erasure, containers like std::shared_ptr will not work, unless the type hold by them is derived from std::enable_shared_from_this. + */ +template +struct StackHelper +{ + using ReturnType = std::remove_const_t::Type>; + + static bool push(lua_State* L, const T& t, std::error_code& ec) + { + return UserdataSharedHelper::Type>>::push(L, t, ec); + } + + static T get(lua_State* L, int index) + { + return ContainerTraits::construct(Userdata::get(L, index, true)); + } +}; + +/** + * @brief Pass by value. + * + * Lifetime is managed by Lua. A C++ function which accesses a pointer or reference to an object outside the activation record in which it was + * retrieved may result in undefined behavior if Lua garbage collected it. + */ +template +struct StackHelper +{ + static bool push(lua_State* L, const T& t, std::error_code& ec) + { + return UserdataValue::push(L, t, ec); + } + + static bool push(lua_State* L, T&& t, std::error_code& ec) + { + return UserdataValue::push(L, std::move(t), ec); + } + + static T const& get(lua_State* L, int index) + { + return *Userdata::get(L, index, true); + } +}; + +//================================================================================================= +/** + * @brief Lua stack conversions for pointers and references to class objects. + * + * Lifetime is managed by C++. Lua code which remembers a reference to the value may result in undefined behavior if C++ destroys the object. + * The handling of the const and volatile qualifiers happens in UserdataPtr. + */ +template +struct RefStackHelper +{ + using ReturnType = C; + using T = std::remove_const_t::Type>; + + static inline bool push(lua_State* L, const C& t, std::error_code& ec) + { + return UserdataSharedHelper::Type>>::push(L, t, ec); + } + + static ReturnType get(lua_State* L, int index) + { + return ContainerTraits::construct(Userdata::get(L, index, true)); + } +}; + +template +struct RefStackHelper +{ + using ReturnType = T&; + + static bool push(lua_State* L, const T& t, std::error_code& ec) + { + return UserdataPtr::push(L, &t, ec); + } + + static ReturnType get(lua_State* L, int index) + { + T* t = Userdata::get(L, index, true); + + if (!t) + luaL_error(L, "nil passed to reference"); + + return *t; + } +}; + +//================================================================================================= +/** + * @brief Trait class that selects whether to return a user registered class object by value or by reference. + */ +template +struct UserdataGetter +{ + using ReturnType = T*; + + static ReturnType get(lua_State* L, int index) + { + return Userdata::get(L, index, false); + } +}; + +template +struct UserdataGetter> +{ + using ReturnType = T; + + static ReturnType get(lua_State* L, int index) + { + return StackHelper::value>::get(L, index); + } +}; + +} // namespace detail + +//================================================================================================= +/** + * @brief Lua stack conversions for class objects passed by value. + */ +template +struct Stack +{ + using IsUserdata = void; + + using Getter = detail::UserdataGetter; + using ReturnType = typename Getter::ReturnType; + + static bool push(lua_State* L, const T& value, std::error_code& ec) + { + return detail::StackHelper::value>::push(L, value, ec); + } + + static bool push(lua_State* L, T&& value, std::error_code& ec) + { + return detail::StackHelper::value>::push(L, std::move(value), ec); + } + + static ReturnType get(lua_State* L, int index) + { + return Getter::get(L, index); + } + + static bool isInstance(lua_State* L, int index) + { + return detail::Userdata::isInstance(L, index); + } +}; + +namespace detail { + +//================================================================================================= +/** + * @brief Trait class indicating whether the parameter type must be a user registered class. + * + * The trait checks the existence of member type Stack::IsUserdata specialization for detection. + */ +template +struct IsUserdata : std::false_type +{ +}; + +template +struct IsUserdata::IsUserdata>> : std::true_type +{ +}; + +//================================================================================================= +/** + * @brief Trait class that selects a specific push/get implemenation. + */ +template +struct StackOpSelector; + +// pointer +template +struct StackOpSelector +{ + using ReturnType = T*; + + static bool push(lua_State* L, T* value, std::error_code& ec) { return UserdataPtr::push(L, value, ec); } + + static T* get(lua_State* L, int index) { return Userdata::get(L, index, false); } + + static bool isInstance(lua_State* L, int index) { return Userdata::isInstance(L, index); } +}; + +// pointer to const +template +struct StackOpSelector +{ + using ReturnType = const T*; + + static bool push(lua_State* L, const T* value, std::error_code& ec) { return UserdataPtr::push(L, value, ec); } + + static const T* get(lua_State* L, int index) { return Userdata::get(L, index, true); } + + static bool isInstance(lua_State* L, int index) { return Userdata::isInstance(L, index); } +}; + +// l-value reference +template +struct StackOpSelector +{ + using Helper = RefStackHelper::value>; + using ReturnType = typename Helper::ReturnType; + + static bool push(lua_State* L, T& value, std::error_code& ec) { return UserdataPtr::push(L, &value, ec); } + + static ReturnType get(lua_State* L, int index) { return Helper::get(L, index); } + + static bool isInstance(lua_State* L, int index) { return Userdata::isInstance(L, index); } +}; + +// l-value reference to const +template +struct StackOpSelector +{ + using Helper = RefStackHelper::value>; + using ReturnType = typename Helper::ReturnType; + + static bool push(lua_State* L, const T& value, std::error_code& ec) { return Helper::push(L, value, ec); } + + static ReturnType get(lua_State* L, int index) { return Helper::get(L, index); } + + static bool isInstance(lua_State* L, int index) { return Userdata::isInstance(L, index); } +}; + +} // namespace detail +} // namespace luabridge + +// End File: Source/LuaBridge/detail/Userdata.h + +// Begin File: Source/LuaBridge/detail/Stack.h + +// https://github.com/kunitoki/LuaBridge3 +// Copyright 2020, Lucio Asnaghi +// Copyright 2019, Dmitry Tarakanov +// Copyright 2012, Vinnie Falco +// Copyright 2007, Nathan Reed +// SPDX-License-Identifier: MIT + +namespace luabridge { + +//================================================================================================= +/** + * @brief Lua stack traits for C++ types. + * + * @tparam T A C++ type. + */ +template +struct Stack; + +//================================================================================================= +/** + * @brief Specialization for void type. + */ +template <> +struct Stack +{ + static bool push(lua_State*, std::error_code&) + { + return true; + } +}; + +//================================================================================================= +/** + * @brief Specialization for nullptr_t. + */ +template <> +struct Stack +{ + static bool push(lua_State* L, std::nullptr_t, std::error_code&) + { + lua_pushnil(L); + return true; + } + + static std::nullptr_t get(lua_State*, int) + { + return nullptr; + } + + static bool isInstance(lua_State* L, int index) + { + return lua_isnil(L, index); + } +}; + +//================================================================================================= +/** + * @brief Receive the lua_State* as an argument. + */ +template <> +struct Stack +{ + static lua_State* get(lua_State* L, int) + { + return L; + } +}; + +//================================================================================================= +/** + * @brief Stack specialization for a lua_CFunction. + */ +template <> +struct Stack +{ + static bool push(lua_State* L, lua_CFunction f, std::error_code&) + { + lua_pushcfunction(L, f); + return true; + } + + static lua_CFunction get(lua_State* L, int index) + { + return lua_tocfunction(L, index); + } + + static bool isInstance(lua_State* L, int index) + { + return lua_iscfunction(L, index); + } +}; + +//================================================================================================= +/** + * @brief Stack specialization for `bool`. + */ +template <> +struct Stack +{ + static bool push(lua_State* L, bool value, std::error_code&) + { + lua_pushboolean(L, value ? 1 : 0); + return true; + } + + static bool get(lua_State* L, int index) + { + return lua_toboolean(L, index) ? true : false; + } + + static bool isInstance(lua_State* L, int index) + { + return lua_isboolean(L, index); + } +}; + +//================================================================================================= +/** + * @brief Stack specialization for `std::byte`. + */ +template <> +struct Stack +{ + static bool push(lua_State* L, std::byte value, std::error_code&) + { + pushunsigned(L, std::to_integer(value)); + return true; + } + + static std::byte get(lua_State* L, int index) + { + return static_cast(luaL_checkinteger(L, index)); + } + + static bool isInstance(lua_State* L, int index) + { + return lua_type(L, index) == LUA_TNUMBER; + } +}; + +//================================================================================================= +/** + * @brief Stack specialization for `char`. + */ +template <> +struct Stack +{ + static bool push(lua_State* L, char value, std::error_code&) + { + lua_pushlstring(L, &value, 1); + return true; + } + + static char get(lua_State* L, int index) + { + return luaL_checkstring(L, index)[0]; + } + + static bool isInstance(lua_State* L, int index) + { + return lua_type(L, index) == LUA_TSTRING; + } +}; + +//================================================================================================= +/** + * @brief Stack specialization for `unsigned char`. + */ +template <> +struct Stack +{ + static bool push(lua_State* L, unsigned char value, std::error_code&) + { + pushunsigned(L, value); + return true; + } + + static unsigned char get(lua_State* L, int index) + { + return static_cast(luaL_checkinteger(L, index)); + } + + static bool isInstance(lua_State* L, int index) + { + return lua_type(L, index) == LUA_TNUMBER; + } +}; + +//================================================================================================= +/** + Stack specialization for `short`. +*/ +template <> +struct Stack +{ + static bool push(lua_State* L, short value, std::error_code&) + { + lua_pushinteger(L, static_cast(value)); + return true; + } + + static short get(lua_State* L, int index) + { + return static_cast(luaL_checkinteger(L, index)); + } + + static bool isInstance(lua_State* L, int index) + { + return lua_type(L, index) == LUA_TNUMBER; + } +}; + +//================================================================================================= +/** + * @brief Stack specialization for `unsigned short`. + */ +template <> +struct Stack +{ + static bool push(lua_State* L, unsigned short value, std::error_code&) + { + pushunsigned(L, value); + return true; + } + + static unsigned short get(lua_State* L, int index) + { + return static_cast(luaL_checkinteger(L, index)); + } + + static bool isInstance(lua_State* L, int index) + { + return lua_type(L, index) == LUA_TNUMBER; + } +}; + +//================================================================================================= +/** + * @brief Stack specialization for `int`. + */ +template <> +struct Stack +{ + static bool push(lua_State* L, int value, std::error_code&) + { + lua_pushinteger(L, static_cast(value)); + return true; + } + + static int get(lua_State* L, int index) + { + return static_cast(luaL_checkinteger(L, index)); + } + + static bool isInstance(lua_State* L, int index) + { + return lua_type(L, index) == LUA_TNUMBER; + } +}; + +//================================================================================================= +/** + * @brief Stack specialization for `unsigned int`. + */ +template <> +struct Stack +{ + static bool push(lua_State* L, unsigned int value, std::error_code&) + { + pushunsigned(L, value); + return true; + } + + static unsigned int get(lua_State* L, int index) + { + return static_cast(luaL_checkinteger(L, index)); + } + + static bool isInstance(lua_State* L, int index) + { + return lua_type(L, index) == LUA_TNUMBER; + } +}; + +//================================================================================================= +/** + * @brief Stack specialization for `long`. + */ +template <> +struct Stack +{ + static bool push(lua_State* L, long value, std::error_code&) + { + lua_pushinteger(L, static_cast(value)); + return true; + } + + static long get(lua_State* L, int index) + { + return static_cast(luaL_checkinteger(L, index)); + } + + static bool isInstance(lua_State* L, int index) + { + return lua_type(L, index) == LUA_TNUMBER; + } +}; + +//================================================================================================= +/** + * @brief Stack specialization for `unsigned long`. + */ +template <> +struct Stack +{ + static bool push(lua_State* L, unsigned long value, std::error_code&) + { + pushunsigned(L, value); + return true; + } + + static unsigned long get(lua_State* L, int index) + { + return static_cast(luaL_checkinteger(L, index)); + } + + static bool isInstance(lua_State* L, int index) + { + return lua_type(L, index) == LUA_TNUMBER; + } +}; + +//================================================================================================= +/** + * @brief Stack specialization for `long long`. + */ +template <> +struct Stack +{ + static bool push(lua_State* L, long long value, std::error_code&) + { + lua_pushinteger(L, static_cast(value)); + return true; + } + + static long long get(lua_State* L, int index) + { + return static_cast(luaL_checkinteger(L, index)); + } + + static bool isInstance(lua_State* L, int index) + { + return lua_type(L, index) == LUA_TNUMBER; + } +}; + +//================================================================================================= +/** + * @brief Stack specialization for `unsigned long long`. + */ +template <> +struct Stack +{ + static bool push(lua_State* L, unsigned long long value, std::error_code&) + { + pushunsigned(L, value); + return true; + } + + static unsigned long long get(lua_State* L, int index) + { + return static_cast(luaL_checkinteger(L, index)); + } + + static bool isInstance(lua_State* L, int index) + { + return lua_type(L, index) == LUA_TNUMBER; + } +}; + +//================================================================================================= +/** + * @brief Stack specialization for `float`. + */ +template <> +struct Stack +{ + static bool push(lua_State* L, float value, std::error_code&) + { + lua_pushnumber(L, static_cast(value)); + return true; + } + + static float get(lua_State* L, int index) + { + return static_cast(luaL_checknumber(L, index)); + } + + static bool isInstance(lua_State* L, int index) + { + return lua_type(L, index) == LUA_TNUMBER; + } +}; + +//================================================================================================= +/** + * @brief Stack specialization for `double`. + */ +template <> +struct Stack +{ + static bool push(lua_State* L, double value, std::error_code&) + { + lua_pushnumber(L, static_cast(value)); + return true; + } + + static double get(lua_State* L, int index) + { + return static_cast(luaL_checknumber(L, index)); + } + + static bool isInstance(lua_State* L, int index) + { + return lua_type(L, index) == LUA_TNUMBER; + } +}; + +//================================================================================================= +/** + * @brief Stack specialization for `long double`. + */ +template <> +struct Stack +{ + static bool push(lua_State* L, long double value, std::error_code&) + { + lua_pushnumber(L, static_cast(value)); + return true; + } + + static long double get(lua_State* L, int index) + { + return static_cast(luaL_checknumber(L, index)); + } + + static bool isInstance(lua_State* L, int index) + { + return lua_type(L, index) == LUA_TNUMBER; + } +}; + +//================================================================================================= +/** + * @brief Stack specialization for `const char*`. + */ +template <> +struct Stack +{ + static bool push(lua_State* L, const char* str, std::error_code&) + { + if (str != nullptr) + lua_pushstring(L, str); + else + lua_pushnil(L); + + return true; + } + + static const char* get(lua_State* L, int index) + { + return lua_isnil(L, index) ? nullptr : luaL_checkstring(L, index); + } + + static bool isInstance(lua_State* L, int index) + { + return lua_isnil(L, index) || lua_type(L, index) == LUA_TSTRING; + } +}; + +//================================================================================================= +/** + * @brief Stack specialization for `const char[N]` literals. + */ +namespace detail { +template +struct IsStringLiteral : std::is_same>]>> +{ +}; +} // namespace detail + +template +struct Stack::value>> +{ + static bool push(lua_State* L, const char (&str)[std::extent_v>], std::error_code&) + { + lua_pushlstring(L, str, std::extent_v>); + return true; + } +}; + +//================================================================================================= +/** + * @brief Stack specialization for `std::string_view`. + */ +template <> +struct Stack +{ + static bool push(lua_State* L, std::string_view str, std::error_code&) + { + lua_pushlstring(L, str.data(), str.size()); + return true; + } + + static std::string_view get(lua_State* L, int index) + { + return lua_isnil(L, index) ? std::string_view() : luaL_checkstring(L, index); + } + + static bool isInstance(lua_State* L, int index) + { + return lua_isnil(L, index) || lua_type(L, index) == LUA_TSTRING; + } +}; + +//================================================================================================= +/** + * @brief Stack specialization for `std::string`. + */ +template <> +struct Stack +{ + static bool push(lua_State* L, const std::string& str, std::error_code&) + { + lua_pushlstring(L, str.data(), str.size()); + return true; + } + + static std::string get(lua_State* L, int index) + { + std::size_t len; + if (lua_type(L, index) == LUA_TSTRING) + { + const char* str = lua_tolstring(L, index, &len); + return std::string(str, len); + } + + // Lua reference manual: + // If the value is a number, then lua_tolstring also changes the actual value in the stack + // to a string. (This change confuses lua_next when lua_tolstring is applied to keys during + // a table traversal.) + lua_pushvalue(L, index); + const char* str = lua_tolstring(L, -1, &len); + std::string string(str, len); + lua_pop(L, 1); // Pop the temporary string + return string; + } + + static bool isInstance(lua_State* L, int index) + { + return lua_type(L, index) == LUA_TSTRING; + } +}; + +//================================================================================================= +/** + * @brief Stack specialization for `std::tuple`. + */ +template +struct Stack> +{ + static bool push(lua_State* L, const std::tuple& t, std::error_code& ec) + { + lua_createtable(L, static_cast(Size), 0); + + return push_element(L, t, ec); + } + + static std::tuple get(lua_State* L, int index) + { + if (!lua_istable(L, index)) + luaL_error(L, "#%d argment must be a table", index); + + if (get_length(L, index) != Size) + luaL_error(L, "table size should be %d but is %d", Size, get_length(L, index)); + + std::tuple value; + + int absIndex = lua_absindex(L, index); + lua_pushnil(L); + + pop_element(L, absIndex, value); + + return value; + } + + static bool isInstance(lua_State* L, int index) + { + return lua_type(L, index) == LUA_TTABLE; + } + +private: + static constexpr std::size_t Size = std::tuple_size_v>; + + template + static auto push_element(lua_State*, const std::tuple&, std::error_code&) + -> std::enable_if_t + { + return true; + } + + template + static auto push_element(lua_State* L, const std::tuple& t, std::error_code& ec) + -> std::enable_if_t + { + using T = std::tuple_element_t>; + + lua_pushinteger(L, static_cast(Index + 1)); + + std::error_code push_ec; + bool result = Stack::push(L, std::get(t), push_ec); + if (! result) + { + lua_pushnil(L); + lua_settable(L, -3); + ec = push_ec; + return false; + } + + lua_settable(L, -3); + + return push_element(L, t, ec); + } + + template + static auto pop_element(lua_State*, int, std::tuple&) + -> std::enable_if_t + { + } + + template + static auto pop_element(lua_State* L, int absIndex, std::tuple& t) + -> std::enable_if_t + { + using T = std::tuple_element_t>; + + if (lua_next(L, absIndex) == 0) + return; + + std::get(t) = Stack::get(L, -1); + lua_pop(L, 1); + + pop_element(L, absIndex, t); + } +}; + +namespace detail { + +template +struct StackOpSelector +{ + using ReturnType = T; + + static bool push(lua_State* L, T& value, std::error_code& ec) { return Stack::push(L, value, ec); } + + static ReturnType get(lua_State* L, int index) { return Stack::get(L, index); } + + static bool isInstance(lua_State* L, int index) { return Stack::isInstance(L, index); } +}; + +template +struct StackOpSelector +{ + using ReturnType = T; + + static bool push(lua_State* L, const T& value, std::error_code& ec) { return Stack::push(L, value, ec); } + + static ReturnType get(lua_State* L, int index) { return Stack::get(L, index); } + + static bool isInstance(lua_State* L, int index) { return Stack::isInstance(L, index); } +}; + +template +struct StackOpSelector +{ + using ReturnType = T; + + static bool push(lua_State* L, T* value, std::error_code& ec) { return Stack::push(L, *value, ec); } + + static ReturnType get(lua_State* L, int index) { return Stack::get(L, index); } + + static bool isInstance(lua_State* L, int index) { return Stack::isInstance(L, index); } +}; + +template +struct StackOpSelector +{ + using ReturnType = T; + + static bool push(lua_State* L, const T* value, std::error_code& ec) { return Stack::push(L, *value, ec); } + + static ReturnType get(lua_State* L, int index) { return Stack::get(L, index); } + + static bool isInstance(lua_State* L, int index) { return Stack::isInstance(L, index); } +}; + +} // namespace detail + +template +struct Stack::value>> +{ + using Helper = detail::StackOpSelector::value>; + using ReturnType = typename Helper::ReturnType; + + static bool push(lua_State* L, T& value, std::error_code& ec) { return Helper::push(L, value, ec); } + + static ReturnType get(lua_State* L, int index) { return Helper::get(L, index); } + + static bool isInstance(lua_State* L, int index) { return Helper::template isInstance(L, index); } +}; + +template +struct Stack::value>> +{ + using Helper = detail::StackOpSelector::value>; + using ReturnType = typename Helper::ReturnType; + + static bool push(lua_State* L, const T& value, std::error_code& ec) { return Helper::push(L, value, ec); } + + static ReturnType get(lua_State* L, int index) { return Helper::get(L, index); } + + static bool isInstance(lua_State* L, int index) { return Helper::template isInstance(L, index); } +}; + +template +struct Stack +{ + using Helper = detail::StackOpSelector::value>; + using ReturnType = typename Helper::ReturnType; + + static bool push(lua_State* L, T* value, std::error_code& ec) { return Helper::push(L, value, ec); } + + static ReturnType get(lua_State* L, int index) { return Helper::get(L, index); } + + static bool isInstance(lua_State* L, int index) { return Helper::template isInstance(L, index); } +}; + +template +struct Stack +{ + using Helper = detail::StackOpSelector::value>; + using ReturnType = typename Helper::ReturnType; + + static bool push(lua_State* L, const T* value, std::error_code& ec) { return Helper::push(L, value, ec); } + + static ReturnType get(lua_State* L, int index) { return Helper::get(L, index); } + + static bool isInstance(lua_State* L, int index) { return Helper::template isInstance(L, index); } +}; + +//------------------------------------------------------------------------------ +/** + * @brief Push an object onto the Lua stack. + */ +template +bool push(lua_State* L, T t, std::error_code& ec) +{ + return Stack::push(L, t, ec); +} + +//------------------------------------------------------------------------------ +/** + * @brief Check whether an object on the Lua stack is of type T. + */ +template +bool isInstance(lua_State* L, int index) +{ + return Stack::isInstance(L, index); +} + +} // namespace luabridge + +// End File: Source/LuaBridge/detail/Stack.h + +// Begin File: Source/LuaBridge/Map.h + +// https://github.com/kunitoki/LuaBridge3 +// Copyright 2020, Lucio Asnaghi +// Copyright 2018, Dmitry Tarakanov +// SPDX-License-Identifier: MIT + +namespace luabridge { + +//================================================================================================= +/** + * @brief Stack specialization for `std::map`. + */ +template +struct Stack> +{ + using Type = std::map; + + static bool push(lua_State* L, const Type& map, std::error_code& ec) + { + const int initialStackSize = lua_gettop(L); + + lua_createtable(L, 0, static_cast(map.size())); + + for (auto it = map.begin(); it != map.end(); ++it) + { + std::error_code errorCodeKey; + if (! Stack::push(L, it->first, errorCodeKey)) + { + ec = errorCodeKey; + lua_pop(L, lua_gettop(L) - initialStackSize); + return false; + } + + std::error_code errorCodeValue; + if (! Stack::push(L, it->second, errorCodeValue)) + { + ec = errorCodeValue; + lua_pop(L, lua_gettop(L) - initialStackSize); + return false; + } + + lua_settable(L, -3); + } + + return true; + } + + static Type get(lua_State* L, int index) + { + if (!lua_istable(L, index)) + luaL_error(L, "#%d argument must be a table", index); + + Type map; + + int absIndex = lua_absindex(L, index); + lua_pushnil(L); + + while (lua_next(L, absIndex) != 0) + { + map.emplace(Stack::get(L, -2), Stack::get(L, -1)); + lua_pop(L, 1); + } + + return map; + } + + static bool isInstance(lua_State* L, int index) + { + return lua_istable(L, index); + } +}; + +} // namespace luabridge + +// End File: Source/LuaBridge/Map.h + +// Begin File: Source/LuaBridge/UnorderedMap.h + +// https://github.com/kunitoki/LuaBridge3 +// Copyright 2020, Lucio Asnaghi +// Copyright 2019, Dmitry Tarakanov +// SPDX-License-Identifier: MIT + +namespace luabridge { + +//================================================================================================= +/** + * @brief Stack specialization for `std::unordered_map`. + */ +template +struct Stack> +{ + using Type = std::unordered_map; + + static bool push(lua_State* L, const Type& map, std::error_code& ec) + { + const int initialStackSize = lua_gettop(L); + + lua_createtable(L, 0, static_cast(map.size())); + + for (auto it = map.begin(); it != map.end(); ++it) + { + std::error_code errorCodeKey; + if (! Stack::push(L, it->first, errorCodeKey)) + { + ec = errorCodeKey; + lua_pop(L, lua_gettop(L) - initialStackSize); + return false; + } + + std::error_code errorCodeValue; + if (! Stack::push(L, it->second, errorCodeValue)) + { + ec = errorCodeValue; + lua_pop(L, lua_gettop(L) - initialStackSize); + return false; + } + + lua_settable(L, -3); + } + + return true; + } + + static Type get(lua_State* L, int index) + { + if (!lua_istable(L, index)) + luaL_error(L, "#%d argument must be a table", index); + + Type map; + + int absIndex = lua_absindex(L, index); + lua_pushnil(L); + + while (lua_next(L, absIndex) != 0) + { + map.emplace(Stack::get(L, -2), Stack::get(L, -1)); + lua_pop(L, 1); + } + + return map; + } + + static bool isInstance(lua_State* L, int index) + { + return lua_istable(L, index); + } +}; + +} // namespace luabridge + +// End File: Source/LuaBridge/UnorderedMap.h + +// Begin File: Source/LuaBridge/Optional.h + +// https://github.com/kunitoki/LuaBridge3 +// Copyright 2020, Lucio Asnaghi +// SPDX-License-Identifier: MIT + +namespace luabridge { + +//================================================================================================= +/** + * @brief Stack specialization for `std::optional`. + */ +template +struct Stack> +{ + using Type = std::optional; + + static bool push(lua_State* L, const Type& value, std::error_code& ec) + { + if (value) + return Stack::push(L, *value, ec); + + lua_pushnil(L); + return true; + } + + static Type get(lua_State* L, int index) + { + if (lua_type(L, index) == LUA_TNIL) + return std::nullopt; + + return Stack::get(L, index); + } + + static bool isInstance(lua_State* L, int index) + { + return lua_isnil(L, index) || Stack::isInstance(L, index); + } +}; + +} // namespace luabridge + +// End File: Source/LuaBridge/Optional.h + +// Begin File: Source/LuaBridge/detail/FuncTraits.h + +// https://github.com/kunitoki/LuaBridge3 +// Copyright 2020, Lucio Asnaghi +// Copyright 2020, Dmitry Tarakanov +// Copyright 2019, George Tokmaji +// Copyright 2012, Vinnie Falco +// SPDX-License-Identifier: MIT + +namespace luabridge { +namespace detail { + +//================================================================================================= +/** + * @brief Generic function traits. + * + * @tparam IsMember True if the function is a member function pointer. + * @tparam IsConst True if the function is const. + * @tparam R Return type of the function. + * @tparam Args Arguments types as variadic parameter pack. + */ +template +struct function_traits_base +{ + using result_type = R; + + using argument_types = std::tuple; + + static constexpr auto arity = sizeof...(Args); + + static constexpr auto is_member = IsMember; + + static constexpr auto is_const = IsConst; +}; + +template +struct function_traits_impl; + +template +struct function_traits_impl : function_traits_base +{ +}; + +template +struct function_traits_impl : function_traits_base +{ +}; + +template +struct function_traits_impl : function_traits_base +{ +}; + +template +struct function_traits_impl : function_traits_base +{ +}; + +template +struct function_traits_impl : function_traits_base +{ +}; + +template +struct function_traits_impl : function_traits_base +{ +}; + +template +struct function_traits_impl : function_traits_base +{ +}; + +template +struct function_traits_impl : function_traits_base +{ +}; + +#if _MSC_VER && _M_IX86 // Windows: WINAPI (a.k.a. __stdcall) function pointers (32bit only). +template +struct function_traits_impl : function_traits_base +{ +}; + +template +struct function_traits_impl : function_traits_base +{ +}; + +template +struct function_traits_impl : function_traits_base +{ +}; + +template +struct function_traits_impl : function_traits_base +{ +}; + +template +struct function_traits_impl : function_traits_base +{ +}; + +template +struct function_traits_impl : function_traits_base +{ +}; + +template +struct function_traits_impl : function_traits_base +{ +}; + +template +struct function_traits_impl : function_traits_base +{ +}; +#endif + +template +struct functor_traits_impl : function_traits_impl +{ +}; + +//================================================================================================= +/** + * @brief Traits class for callable objects (e.g. function pointers, lambdas) + * + * @tparam F Callable object. + */ +template +struct function_traits : std::conditional_t, + detail::functor_traits_impl, + detail::function_traits_impl> +{ +}; + +//================================================================================================= +/** + * @brief Deduces the return type of a callble object. + * + * @tparam F Callable object. + */ +template +using function_result_t = typename function_traits::result_type; + +/** + * @brief Deduces the argument type of a callble object. + * + * @tparam I Argument index. + * @tparam F Callable object. + */ +template +using function_argument_t = std::tuple_element_t::argument_types>; + +/** + * @brief Deduces the arguments type of a callble object. + * + * @tparam F Callable object. + */ +template +using function_arguments_t = typename function_traits::argument_types; + +/** + * @brief An integral constant expression that gives the number of arguments accepted by the callable object. + * + * @tparam F Callable object. + */ +template +static constexpr std::size_t function_arity_v = function_traits::arity; + +/** + * @brief An boolean constant expression that checks if the callable object is a member function. + * + * @tparam F Callable object. + */ +template +static constexpr bool function_is_member_v = function_traits::is_member; + +/** + * @brief An boolean constant expression that checks if the callable object is const. + * + * @tparam F Callable object. + */ +template +static constexpr bool function_is_const_v = function_traits::is_const; + +//================================================================================================= +/** + * @brief Detect if we are a `std::function`. + * + * @tparam F Callable object. + */ +template +struct is_std_function : std::false_type +{ +}; + +template +struct is_std_function> : std::true_type +{ +}; + +template +struct is_std_function> : std::true_type +{ +}; + +template +static constexpr bool is_std_function_v = is_std_function::value; + +//================================================================================================= +/** + * @brief Reconstruct a function signature from return type and args. + */ +template +struct to_std_function_type +{ +}; + +template +struct to_std_function_type> +{ + using type = std::function; +}; + +template +using to_std_function_type_t = typename to_std_function_type::type; + +//================================================================================================= +/** + * @brief Simple make_tuple alternative that doesn't decay the types. + * + * @tparam Types Argument types that will compose the tuple. + */ +template +constexpr auto tupleize(Types&&... types) +{ + return std::tuple(std::forward(types)...); +} + +//================================================================================================= +/** + * @brief Make argument lists extracting them from the lua state, starting at a stack index. + * + * @tparam ArgsPack Arguments pack to extract from the lua stack. + * @tparam Start Start index where stack variables are located in the lua stack. + */ +template +auto make_arguments_list_impl(lua_State* L, std::index_sequence) +{ + return tupleize(Stack>::get(L, Start + Indices)...); +} + +template +auto make_arguments_list(lua_State* L) +{ + return make_arguments_list_impl(L, std::make_index_sequence>()); +} + +//================================================================================================= +/** + * @brief Helpers for iterating through tuple arguments, pushing each argument to the lua stack. + */ +template +auto push_arguments(lua_State*, std::tuple, std::error_code&) + -> std::enable_if_t +{ + return Index + 1; +} + +template +auto push_arguments(lua_State* L, std::tuple t, std::error_code& ec) + -> std::enable_if_t +{ + using T = std::tuple_element_t>; + + std::error_code pec; + bool result = Stack::push(L, std::get(t), pec); + if (! result) + { + ec = pec; + return Index + 1; + } + + return push_arguments(L, std::move(t), ec); +} + +//================================================================================================= +/** + * @brief Helpers for iterating through tuple arguments, popping each argument from the lua stack. + */ +template +auto pop_arguments(lua_State*, std::tuple&) + -> std::enable_if_t +{ + return sizeof...(Types); +} + +template +auto pop_arguments(lua_State* L, std::tuple& t) + -> std::enable_if_t +{ + using T = std::tuple_element_t>; + + std::get(t) = Stack::get(L, Start - Index); + + return pop_arguments(L, t); +} + +//================================================================================================= +/** + * @brief Remove first type from tuple. + */ +template +struct remove_first_type +{ +}; + +template +struct remove_first_type> +{ + using type = std::tuple; +}; + +template +using remove_first_type_t = typename remove_first_type::type; + +//================================================================================================= +/** + * @brief Function generator. + */ +template +struct function +{ + template + static int call(lua_State* L, F func) + { +#if LUABRIDGE_HAS_EXCEPTIONS + try + { +#endif + std::error_code ec; + bool result = Stack::push(L, std::apply(func, make_arguments_list(L)), ec); + if (! result) + return luaL_error(L, ec.message().c_str()); + + return 1; + +#if LUABRIDGE_HAS_EXCEPTIONS + } + catch (const std::exception& e) + { + return luaL_error(L, e.what()); + } + catch (...) + { + return luaL_error(L, "Error while calling function"); + } +#endif + } + + template + static int call(lua_State* L, T* ptr, F func) + { +#if LUABRIDGE_HAS_EXCEPTIONS + try + { +#endif + auto f = [ptr, func](auto&&... args) -> ReturnType { return (ptr->*func)(std::forward(args)...); }; + + std::error_code ec; + bool result = Stack::push(L, std::apply(f, make_arguments_list(L)), ec); + if (! result) + return luaL_error(L, ec.message().c_str()); + + return 1; + +#if LUABRIDGE_HAS_EXCEPTIONS + } + catch (const std::exception& e) + { + return luaL_error(L, e.what()); + } + catch (...) + { + return luaL_error(L, "Error while calling method"); + } +#endif + } +}; + +template +struct function +{ + template + static int call(lua_State* L, F func) + { +#if LUABRIDGE_HAS_EXCEPTIONS + try + { +#endif + std::apply(func, make_arguments_list(L)); + + return 0; + +#if LUABRIDGE_HAS_EXCEPTIONS + } + catch (const std::exception& e) + { + return luaL_error(L, e.what()); + } + catch (...) + { + return luaL_error(L, "Error while calling function"); + } +#endif + } + + template + static int call(lua_State* L, T* ptr, F func) + { +#if LUABRIDGE_HAS_EXCEPTIONS + try + { +#endif + auto f = [ptr, func](auto&&... args) { (ptr->*func)(std::forward(args)...); }; + + std::apply(f, make_arguments_list(L)); + + return 0; + +#if LUABRIDGE_HAS_EXCEPTIONS + } + catch (const std::exception& e) + { + return luaL_error(L, e.what()); + } + catch (...) + { + return luaL_error(L, "Error while calling method"); + } +#endif + } +}; + +//================================================================================================= +/** + * @brief Constructor generators. + * + * These templates call operator new with the contents of a type/value list passed to the constructor. Two versions of call() are provided. + * One performs a regular new, the other performs a placement new. + */ +template +struct constructor; + +template +struct constructor +{ + using empty = std::tuple<>; + + static T* call(const empty&) + { + return new T; + } + + static T* call(void* ptr, const empty&) + { + return new (ptr) T; + } +}; + +template +struct constructor +{ + static T* call(const Args& args) + { + auto alloc = [](auto&&... args) { return new T{ std::forward(args)... }; }; + + return std::apply(alloc, args); + } + + static T* call(void* ptr, const Args& args) + { + auto alloc = [ptr](auto&&... args) { return new (ptr) T{ std::forward(args)... }; }; + + return std::apply(alloc, args); + } +}; + +//================================================================================================= +/** + * @brief Factory generators. + */ +template +struct factory +{ + template + static T* call(void* ptr, const F& func, const Args& args) + { + auto alloc = [ptr, &func](auto&&... args) { return func(ptr, std::forward(args)...); }; + + return std::apply(alloc, args); + } + + template + static T* call(void* ptr, const F& func) + { + return func(ptr); + } +}; + +} // namespace detail +} // namespace luabridge + +// End File: Source/LuaBridge/detail/FuncTraits.h + +// Begin File: Source/LuaBridge/detail/CFunctions.h + +// https://github.com/kunitoki/LuaBridge3 +// Copyright 2020, Lucio Asnaghi +// Copyright 2019, Dmitry Tarakanov +// Copyright 2012, Vinnie Falco +// SPDX-License-Identifier: MIT + +namespace luabridge { +namespace detail { + +//================================================================================================= +/** + * @brief __index metamethod for a namespace or class static and non-static members. + * + * Retrieves functions from metatables and properties from propget tables. Looks through the class hierarchy if inheritance is present. + */ +inline int index_metamethod(lua_State* L) +{ + assert(lua_istable(L, 1) || lua_isuserdata(L, 1)); // Stack (further not shown): table | userdata, name + + lua_getmetatable(L, 1); // Stack: class/const table (mt) + assert(lua_istable(L, -1)); + + for (;;) + { + lua_pushvalue(L, 2); // Stack: mt, field name + lua_rawget(L, -2); // Stack: mt, field | nil + + if (lua_iscfunction(L, -1)) // Stack: mt, field + { + lua_remove(L, -2); // Stack: field + return 1; + } + + assert(lua_isnil(L, -1)); // Stack: mt, nil + lua_pop(L, 1); // Stack: mt + + lua_rawgetp(L, -1, getPropgetKey()); // Stack: mt, propget table (pg) + assert(lua_istable(L, -1)); + + lua_pushvalue(L, 2); // Stack: mt, pg, field name + lua_rawget(L, -2); // Stack: mt, pg, getter | nil + lua_remove(L, -2); // Stack: mt, getter | nil + + if (lua_iscfunction(L, -1)) // Stack: mt, getter + { + lua_remove(L, -2); // Stack: getter + lua_pushvalue(L, 1); // Stack: getter, table | userdata + lua_call(L, 1, 1); // Stack: value + return 1; + } + + assert(lua_isnil(L, -1)); // Stack: mt, nil + lua_pop(L, 1); // Stack: mt + + // It may mean that the field may be in const table and it's constness violation. + // Don't check that, just return nil + + // Repeat the lookup in the parent metafield, + // or return nil if the field doesn't exist. + lua_rawgetp(L, -1, getParentKey()); // Stack: mt, parent mt | nil + + if (lua_isnil(L, -1)) // Stack: mt, nil + { + lua_remove(L, -2); // Stack: nil + return 1; + } + + // Removethe metatable and repeat the search in the parent one. + assert(lua_istable(L, -1)); // Stack: mt, parent mt + lua_remove(L, -2); // Stack: parent mt + } + + // no return +} + +//================================================================================================= +/** + * @brief __newindex metamethod for non-static members. + * + * Retrieves properties from propset tables. + */ +inline int newindex_metamethod(lua_State* L, bool pushSelf) +{ + assert(lua_istable(L, 1) || lua_isuserdata(L, 1)); // Stack (further not shown): table | userdata, name, new value + + lua_getmetatable(L, 1); // Stack: metatable (mt) + assert(lua_istable(L, -1)); + + for (;;) + { + lua_rawgetp(L, -1, getPropsetKey()); // Stack: mt, propset table (ps) | nil + + if (lua_isnil(L, -1)) // Stack: mt, nil + { + lua_pop(L, 2); // Stack: - + return luaL_error(L, "No member named '%s'", lua_tostring(L, 2)); + } + + assert(lua_istable(L, -1)); + + lua_pushvalue(L, 2); // Stack: mt, ps, field name + lua_rawget(L, -2); // Stack: mt, ps, setter | nil + lua_remove(L, -2); // Stack: mt, setter | nil + + if (lua_iscfunction(L, -1)) // Stack: mt, setter + { + lua_remove(L, -2); // Stack: setter + if (pushSelf) + lua_pushvalue(L, 1); // Stack: setter, table | userdata + lua_pushvalue(L, 3); // Stack: setter, table | userdata, new value + lua_call(L, pushSelf ? 2 : 1, 0); // Stack: - + return 0; + } + + assert(lua_isnil(L, -1)); // Stack: mt, nil + lua_pop(L, 1); // Stack: mt + + lua_rawgetp(L, -1, getParentKey()); // Stack: mt, parent mt | nil + + if (lua_isnil(L, -1)) // Stack: mt, nil + { + lua_pop(L, 1); // Stack: - + return luaL_error(L, "No writable member '%s'", lua_tostring(L, 2)); + } + + assert(lua_istable(L, -1)); // Stack: mt, parent mt + lua_remove(L, -2); // Stack: parent mt + // Repeat the search in the parent + } + + // no return +} + +//================================================================================================= +/** + * @brief __newindex metamethod for objects. + */ +inline int newindex_object_metamethod(lua_State* L) +{ + return newindex_metamethod(L, true); +} + +//================================================================================================= +/** + * @brief __newindex metamethod for namespace or class static members. + * + * Retrieves properties from propset tables. + */ +inline int newindex_static_metamethod(lua_State* L) +{ + return newindex_metamethod(L, false); +} + +//================================================================================================= +/** + * @brief lua_CFunction to report an error writing to a read-only value. + * + * The name of the variable is in the first upvalue. + */ +inline int read_only_error(lua_State* L) +{ + std::string s; + + s = s + "'" + lua_tostring(L, lua_upvalueindex(1)) + "' is read-only"; + + return luaL_error(L, s.c_str()); +} + +//================================================================================================= +/** + * @brief __gc metamethod for a class. + */ +template +static int gc_metamethod(lua_State* L) +{ + Userdata* ud = Userdata::getExact(L, 1); + assert(ud); + + ud->~Userdata(); + + return 0; +} + +//================================================================================================= + +template +struct property_getter; + +/** + * @brief lua_CFunction to get a variable. + * + * This is used for global variables or class static data members. The pointer to the data is in the first upvalue. + */ +template +struct property_getter +{ + static int call(lua_State* L) + { + assert(lua_islightuserdata(L, lua_upvalueindex(1))); + + T* ptr = static_cast(lua_touserdata(L, lua_upvalueindex(1))); + assert(ptr != nullptr); + + std::error_code ec; + if (! Stack::push(L, *ptr, ec)) + luaL_error(L, ec.message().c_str()); + + return 1; + } +}; + +#if 0 +template +struct property_getter, void> +{ + static int call(lua_State* L) + { + assert(lua_islightuserdata(L, lua_upvalueindex(1))); + + std::reference_wrapper* ptr = static_cast*>(lua_touserdata(L, lua_upvalueindex(1))); + assert(ptr != nullptr); + + std::error_code ec; + if (! Stack::push(L, ptr->get(), ec)) + luaL_error(L, ec.message().c_str()); + + return 1; + } +}; +#endif + +/** + * @brief lua_CFunction to get a class data member. + * + * The pointer-to-member is in the first upvalue. The class userdata object is at the top of the Lua stack. + */ +template +struct property_getter +{ + static int call(lua_State* L) + { + C* c = Userdata::get(L, 1, true); + + T C::** mp = static_cast(lua_touserdata(L, lua_upvalueindex(1))); + +#if LUABRIDGE_HAS_EXCEPTIONS + try + { +#endif + std::error_code ec; + if (! Stack::push(L, c->**mp, ec)) + luaL_error(L, ec.message().c_str()); + +#if LUABRIDGE_HAS_EXCEPTIONS + } + catch (const std::exception& e) + { + luaL_error(L, e.what()); + } + catch (...) + { + luaL_error(L, "Error while getting property"); + } +#endif + + return 1; + } +}; + +/** + * @brief Helper function to push a property getter on a table at a specific index. + */ +inline void add_property_getter(lua_State* L, const char* name, int tableIndex) +{ + assert(name != nullptr); + assert(lua_istable(L, tableIndex)); + assert(lua_iscfunction(L, -1)); // Stack: getter + + lua_rawgetp(L, tableIndex, getPropgetKey()); // Stack: getter, propget table (pg) + lua_pushvalue(L, -2); // Stack: getter, pg, getter + rawsetfield(L, -2, name); // Stack: getter, pg + lua_pop(L, 2); // Stack: - +} + +//================================================================================================= + +template +struct property_setter; + +/** + * @brief lua_CFunction to set a variable. + * + * This is used for global variables or class static data members. The pointer to the data is in the first upvalue. + */ +template +struct property_setter +{ + static int call(lua_State* L) + { + assert(lua_islightuserdata(L, lua_upvalueindex(1))); + + T* ptr = static_cast(lua_touserdata(L, lua_upvalueindex(1))); + assert(ptr != nullptr); + + *ptr = Stack::get(L, 1); + + return 0; + } +}; + +#if 0 +template +struct property_setter, void> +{ + static int call(lua_State* L) + { + assert(lua_islightuserdata(L, lua_upvalueindex(1))); + + std::reference_wrapper* ptr = static_cast*>(lua_touserdata(L, lua_upvalueindex(1))); + assert(ptr != nullptr); + + ptr->get() = Stack::get(L, 1); + + return 0; + } +}; +#endif + +/** + * @brief lua_CFunction to set a class data member. + * + * The pointer-to-member is in the first upvalue. The class userdata object is at the top of the Lua stack. + */ +template +struct property_setter +{ + static int call(lua_State* L) + { + C* c = Userdata::get(L, 1, false); + + T C::** mp = static_cast(lua_touserdata(L, lua_upvalueindex(1))); + +#if LUABRIDGE_HAS_EXCEPTIONS + try + { +#endif + c->** mp = Stack::get(L, 2); + +#if LUABRIDGE_HAS_EXCEPTIONS + } + catch (const std::exception& e) + { + luaL_error(L, e.what()); + } + catch (...) + { + luaL_error(L, "Error while setting property"); + } +#endif + + return 0; + } +}; + +/** + * @brief Helper function to push a property setter on a table at a specific index. + */ +inline void add_property_setter(lua_State* L, const char* name, int tableIndex) +{ + assert(name != nullptr); + assert(lua_istable(L, tableIndex)); + assert(lua_iscfunction(L, -1)); // Stack: setter + + lua_rawgetp(L, tableIndex, getPropsetKey()); // Stack: setter, propset table (ps) + lua_pushvalue(L, -2); // Stack: setter, ps, setter + rawsetfield(L, -2, name); // Stack: setter, ps + lua_pop(L, 2); // Stack: - +} + +//================================================================================================= +/** + * @brief lua_CFunction to call a class member function with a return value. + * + * The member function pointer is in the first upvalue. The class userdata object is at the top of the Lua stack. + */ +template +int invoke_member_function(lua_State* L) +{ + using FnTraits = detail::function_traits; + + assert(isfulluserdata(L, lua_upvalueindex(1))); + + T* ptr = Userdata::get(L, 1, false); + + const F& func = *static_cast(lua_touserdata(L, lua_upvalueindex(1))); + assert(func != nullptr); + + return function::call(L, ptr, func); +} + +template +int invoke_const_member_function(lua_State* L) +{ + using FnTraits = detail::function_traits; + + assert(isfulluserdata(L, lua_upvalueindex(1))); + + const T* ptr = Userdata::get(L, 1, true); + + const F& func = *static_cast(lua_touserdata(L, lua_upvalueindex(1))); + assert(func != nullptr); + + return function::call(L, ptr, func); +} + +//================================================================================================= +/** + * @brief lua_CFunction to call a class member lua_CFunction. + * + * The member function pointer is in the first upvalue. The object userdata ('this') value is at top ot the Lua stack. + */ +template +int invoke_member_cfunction(lua_State* L) +{ + using F = int (T::*)(lua_State * L); + + assert(isfulluserdata(L, lua_upvalueindex(1))); + + T* t = Userdata::get(L, 1, false); + + const F& func = *static_cast(lua_touserdata(L, lua_upvalueindex(1))); + assert(func != nullptr); + + return (t->*func)(L); +} + +template +int invoke_const_member_cfunction(lua_State* L) +{ + using F = int (T::*)(lua_State * L) const; + + assert(isfulluserdata(L, lua_upvalueindex(1))); + + const T* t = Userdata::get(L, 1, true); + + const F& func = *static_cast(lua_touserdata(L, lua_upvalueindex(1))); + assert(func != nullptr); + + return (t->*func)(L); +} + +//================================================================================================= +/** + * @brief lua_CFunction to call on a object via function pointer. + * + * The proxy function pointer (lightuserdata) is in the first upvalue. The class userdata object is at the top of the Lua stack. + */ +template +int invoke_proxy_function(lua_State* L) +{ + using FnTraits = detail::function_traits; + + assert(lua_islightuserdata(L, lua_upvalueindex(1))); + + auto func = reinterpret_cast(lua_touserdata(L, lua_upvalueindex(1))); + assert(func != nullptr); + + return function::call(L, func); +} + +//================================================================================================= +/** + * @brief lua_CFunction to call on a object via functor (lambda wrapped in a std::function). + * + * The proxy std::function (lightuserdata) is in the first upvalue. The class userdata object is at the top of the Lua stack. + */ +template +int invoke_proxy_functor(lua_State* L) +{ + using FnTraits = detail::function_traits; + + assert(isfulluserdata(L, lua_upvalueindex(1))); + + auto& func = *align(lua_touserdata(L, lua_upvalueindex(1))); + + return function::call(L, func); +} + +} // namespace detail +} // namespace luabridge + +// End File: Source/LuaBridge/detail/CFunctions.h + +// Begin File: Source/LuaBridge/detail/LuaRef.h + +// https://github.com/kunitoki/LuaBridge3 +// Copyright 2020, Lucio Asnaghi +// Copyright 2019, George Tokmaji +// Copyright 2018, Dmitry Tarakanov +// Copyright 2012, Vinnie Falco +// Copyright 2008, Nigel Atkinson +// SPDX-License-Identifier: MIT + +namespace luabridge { + +class LuaResult; + +//================================================================================================= +/** + * @brief Type tag for representing LUA_TNIL. + * + * Construct one of these using `LuaNil ()` to represent a Lua nil. This is faster than creating a reference in the registry to nil. + * Example: + * + * @code + * LuaRef t (LuaRef::createTable (L)); + * ... + * t ["k"] = LuaNil (); // assign nil + * @endcode + */ +struct LuaNil +{ +}; + +/** + * @brief Stack specialization for LuaNil. + */ +template <> +struct Stack +{ + static bool push(lua_State* L, const LuaNil&, std::error_code&) + { + lua_pushnil(L); + return true; + } + + static bool isInstance(lua_State* L, int index) + { + return lua_type(L, index) == LUA_TNIL; + } +}; + +//================================================================================================= +/** + * @brief Base class for Lua variables and table item reference classes. + */ +template +class LuaRefBase +{ +protected: + //============================================================================================= + /** + * @brief Pop the Lua stack. + * + * Pops the specified number of stack items on destruction. We use this when returning objects, to avoid an explicit temporary + * variable, since the destructor executes after the return statement. For example: + * + * @code + * template + * U cast (lua_State* L) + * { + * StackPop p (L, 1); + * ... + * return U (); // Destructor called after this line + * } + * @endcode + * + * @note The `StackPop` object must always be a named local variable. + */ + class StackPop + { + public: + /** + * @brief Create a StackPop object. + * + * @param L A Lua state. + * @param count The number of stack entries to pop on destruction. + */ + StackPop(lua_State* L, int count) + : m_L(L) + , m_count(count) { + } + + /** + * @brief Destroy a StackPop object. + * + * In case an exception is in flight before the destructor is called, stack is potentially cleared by lua. So we never pop more than + * the actual size of the stack. + */ + ~StackPop() + { + const int stackSize = lua_gettop(m_L); + + lua_pop(m_L, stackSize < m_count ? stackSize : m_count); + } + + /** + * @brief Set a new number to pop. + * + * @param newCount The new number of stack entries to pop on destruction. + */ + void popCount(int newCount) + { + m_count = newCount; + } + + private: + lua_State* m_L = nullptr; + int m_count = 0; + }; + + friend struct Stack; + + //============================================================================================= + /** + * @brief Type tag for stack construction. + */ + struct FromStack + { + }; + + LuaRefBase(lua_State* L) + : m_L(L) + { + } + + //============================================================================================= + /** + * @brief Create a reference to this reference. + * + * @returns An index in the Lua registry. + */ + int createRef() const + { + impl().push(); + + return luaL_ref(m_L, LUA_REGISTRYINDEX); + } + +public: + //============================================================================================= + /** + * @brief Convert to a string using lua_tostring function. + * + * @returns A string representation of the referred Lua value. + */ + std::string tostring() const + { + StackPop p(m_L, 1); + + lua_getglobal(m_L, "tostring"); + + impl().push(); + + lua_call(m_L, 1, 1); + + const char* str = lua_tostring(m_L, -1); + return str != nullptr ? str : ""; + } + + //============================================================================================= + /** + * @brief Print a text description of the value to a stream. + * + * This is used for diagnostics. + * + * @param os An output stream. + */ + void print(std::ostream& os) const + { + switch (type()) + { + case LUA_TNIL: + os << "nil"; + break; + + case LUA_TNUMBER: + os << cast(); + break; + + case LUA_TBOOLEAN: + os << (cast() ? "true" : "false"); + break; + + case LUA_TSTRING: + os << '"' << cast() << '"'; + break; + + case LUA_TTABLE: + os << "table: " << tostring(); + break; + + case LUA_TFUNCTION: + os << "function: " << tostring(); + break; + + case LUA_TUSERDATA: + os << "userdata: " << tostring(); + break; + + case LUA_TTHREAD: + os << "thread: " << tostring(); + break; + + case LUA_TLIGHTUSERDATA: + os << "lightuserdata: " << tostring(); + break; + + default: + os << "unknown"; + break; + } + } + + //============================================================================================= + /** + * @brief Insert a Lua value or table item reference to a stream. + * + * @param os An output stream. + * @param ref A Lua reference. + * + * @returns The output stream. + */ + friend std::ostream& operator<<(std::ostream& os, const LuaRefBase& ref) + { + ref.print(os); + return os; + } + + //============================================================================================= + /** + * @brief Retrieve the lua_State associated with the reference. + * + * @returns A Lua state. + */ + lua_State* state() const + { + return m_L; + } + + //============================================================================================= + /** + * @brief Place the object onto the Lua stack. + * + * @param L A Lua state. + */ + void push(lua_State* L) const + { + assert(equalstates(L, m_L)); + (void) L; + + impl().push(); + } + + //============================================================================================= + /** + * @brief Pop the top of Lua stack and assign it to the reference. + * + * @param L A Lua state. + */ + void pop(lua_State* L) + { + assert(equalstates(L, m_L)); + (void) L; + + impl().pop(); + } + + //============================================================================================= + /** + * @brief Return the Lua type of the referred value. + * + * This invokes lua_type(). + * + * @returns The type of the referred value. + * + * @see lua_type() + */ + int type() const + { + StackPop p(m_L, 1); + + impl().push(); + + const int refType = lua_type(m_L, -1); + + return refType; + } + + /** + * @brief Indicate whether it is a nil reference. + * + * @returns True if this is a nil reference, false otherwise. + */ + bool isNil() const { return type() == LUA_TNIL; } + + /** + * @brief Indicate whether it is a reference to a boolean. + * + * @returns True if it is a reference to a boolean, false otherwise. + */ + bool isBool() const { return type() == LUA_TBOOLEAN; } + + /** + * @brief Indicate whether it is a reference to a number. + * + * @returns True if it is a reference to a number, false otherwise. + */ + bool isNumber() const { return type() == LUA_TNUMBER; } + + /** + * @brief Indicate whether it is a reference to a string. + * + * @returns True if it is a reference to a string, false otherwise. + */ + bool isString() const { return type() == LUA_TSTRING; } + + /** + * @brief Indicate whether it is a reference to a table. + * + * @returns True if it is a reference to a table, false otherwise. + */ + bool isTable() const { return type() == LUA_TTABLE; } + + /** + * @brief Indicate whether it is a reference to a function. + * + * @returns True if it is a reference to a function, false otherwise. + */ + bool isFunction() const { return type() == LUA_TFUNCTION; } + + /** + * @brief Indicate whether it is a reference to a full userdata. + * + * @returns True if it is a reference to a full userdata, false otherwise. + */ + bool isUserdata() const { return type() == LUA_TUSERDATA; } + + /** + * @brief Indicate whether it is a reference to a lua thread (coroutine). + * + * @returns True if it is a reference to a lua thread, false otherwise. + */ + bool isThread() const { return type() == LUA_TTHREAD; } + + /** + * @brief Indicate whether it is a reference to a light userdata. + * + * @returns True if it is a reference to a light userdata, false otherwise. + */ + bool isLightUserdata() const { return type() == LUA_TLIGHTUSERDATA; } + + /** + * @brief Indicate whether it is a callable. + * + * @returns True if it is a callable, false otherwise. + */ + bool isCallable() const + { + if (isFunction()) + return true; + + auto metatable = getMetatable(); + return metatable.isTable() && metatable["__call"].isFunction(); + } + + //============================================================================================= + /** + * @brief Perform an explicit conversion to the type T. + * + * @returns A value of the type T converted from this reference. + */ + template + T cast() const + { + StackPop p(m_L, 1); + + impl().push(); + + return Stack::get(m_L, -1); + } + + //============================================================================================= + /** + * @brief Indicate if this reference is convertible to the type T. + * + * @returns True if the referred value is convertible to the type T, false otherwise. + */ + template + bool isInstance() const + { + StackPop p(m_L, 1); + + impl().push(); + + return Stack::isInstance(m_L, -1); + } + + //============================================================================================= + /** + * @brief Type cast operator. + * + * @returns A value of the type T converted from this reference. + */ + template + operator T() const + { + return cast(); + } + + //============================================================================================= + /** + * @brief Get the metatable for the LuaRef. + * + * @returns A LuaRef holding the metatable of the lua object. + */ + LuaRef getMetatable() const + { + if (isNil()) + return LuaRef(m_L); + + StackPop p(m_L, 2); + + impl().push(); + + if (! lua_getmetatable(m_L, -1)) + { + p.popCount(1); + return LuaRef(m_L); + } + + return LuaRef::fromStack(m_L); + } + + //============================================================================================= + /** + * @brief Compare this reference with a specified value using lua_compare(). + * + * This invokes metamethods. + * + * @param rhs A value to compare with. + * + * @returns True if the referred value is equal to the specified one. + */ + template + bool operator==(T rhs) const + { + StackPop p(m_L, 2); + + impl().push(); + + std::error_code ec; + if (! Stack::push(m_L, rhs, ec)) + { + p.popCount(1); + return false; + } + + return lua_compare(m_L, -2, -1, LUA_OPEQ) == 1; + } + + /** + * @brief Compare this reference with a specified value using lua_compare(). + * + * This invokes metamethods. + * + * @param rhs A value to compare with. + * + * @returns True if the referred value is not equal to the specified one. + */ + template + bool operator!=(T rhs) const + { + return !(*this == rhs); + } + + /** + * @brief Compare this reference with a specified value using lua_compare(). + * + * This invokes metamethods. + * + * @param rhs A value to compare with. + * + * @returns True if the referred value is less than the specified one. + */ + template + bool operator<(T rhs) const + { + StackPop p(m_L, 2); + + impl().push(); + + std::error_code ec; + if (! Stack::push(m_L, rhs, ec)) + { + p.popCount(1); + return false; + } + + const int lhsType = lua_type(m_L, -2); + const int rhsType = lua_type(m_L, -1); + if (lhsType != rhsType) + return lhsType < rhsType; + + return lua_compare(m_L, -2, -1, LUA_OPLT) == 1; + } + + /** + * @brief Compare this reference with a specified value using lua_compare(). + * + * This invokes metamethods. + * + * @param rhs A value to compare with. + * + * @returns True if the referred value is less than or equal to the specified one. + */ + template + bool operator<=(T rhs) const + { + StackPop p(m_L, 2); + + impl().push(); + + std::error_code ec; + if (! Stack::push(m_L, rhs, ec)) + { + p.popCount(1); + return false; + } + + const int lhsType = lua_type(m_L, -2); + const int rhsType = lua_type(m_L, -1); + if (lhsType != rhsType) + return lhsType <= rhsType; + + return lua_compare(m_L, -2, -1, LUA_OPLE) == 1; + } + + /** + * @brief Compare this reference with a specified value using lua_compare(). + * + * This invokes metamethods. + * + * @param rhs A value to compare with. + * + * @returns True if the referred value is greater than the specified one. + */ + template + bool operator>(T rhs) const + { + StackPop p(m_L, 2); + + impl().push(); + + std::error_code ec; + if (! Stack::push(m_L, rhs, ec)) + { + p.popCount(1); + return false; + } + + const int lhsType = lua_type(m_L, -2); + const int rhsType = lua_type(m_L, -1); + if (lhsType != rhsType) + return lhsType > rhsType; + + return lua_compare(m_L, -1, -2, LUA_OPLT) == 1; + } + + /** + * @brief Compare this reference with a specified value using lua_compare(). + * + * This invokes metamethods. + * + * @param rhs A value to compare with. + * + * @returns True if the referred value is greater than or equal to the specified one. + */ + template + bool operator>=(T rhs) const + { + StackPop p(m_L, 2); + + impl().push(); + + std::error_code ec; + if (! Stack::push(m_L, rhs, ec)) + { + p.popCount(1); + return false; + } + + const int lhsType = lua_type(m_L, -2); + const int rhsType = lua_type(m_L, -1); + if (lhsType != rhsType) + return lhsType >= rhsType; + + return lua_compare(m_L, -1, -2, LUA_OPLE) == 1; + } + + /** + * @brief Compare this reference with a specified value using lua_compare(). + * + * This does not invoke metamethods. + * + * @param rhs A value to compare with. + * + * @returns True if the referred value is equal to the specified one. + */ + template + bool rawequal(T v) const + { + StackPop p(m_L, 2); + + impl().push(); + + std::error_code ec; + if (! Stack::push(m_L, v, ec)) + { + p.popCount(1); + return false; + } + + return lua_rawequal(m_L, -1, -2) == 1; + } + + //============================================================================================= + /** + * @brief Append a value to a referred table. + * + * If the table is a sequence this will add another element to it. + * + * @param v A value to append to the table. + */ + template + void append(T v) const + { + StackPop p(m_L, 1); + + impl().push(); + + std::error_code ec; + if (! Stack::push(m_L, v, ec)) + return; + + luaL_ref(m_L, -2); + } + + //============================================================================================= + /** + * @brief Return the length of a referred array. + * + * This is identical to applying the Lua # operator. + * + * @returns The length of the referred array. + */ + int length() const + { + StackPop p(m_L, 1); + + impl().push(); + + return get_length(m_L, -1); + } + + //============================================================================================= + /** + * @brief Call Lua code. + * + * The return value is provided as a LuaRef (which may be LUA_REFNIL). + * + * If an error occurs, a LuaException is thrown (only if exceptions are enabled). + * + * @returns A result of the call. + */ + template + LuaResult operator()(Args&&... args) const; + +protected: + lua_State* m_L = nullptr; + +private: + const Impl& impl() const { return static_cast(*this); } + + Impl& impl() { return static_cast(*this); } +}; + +//================================================================================================= +/** + * @brief Lightweight reference to a Lua object. + * + * The reference is maintained for the lifetime of the C++ object. + */ +class LuaRef : public LuaRefBase +{ + //============================================================================================= + /** + * @brief A proxy for representing table values. + */ + class TableItem : public LuaRefBase + { + friend class LuaRef; + + public: + //========================================================================================= + /** + * @brief Construct a TableItem from a table value. + * + * The table is in the registry, and the key is at the top of the stack. + * The key is popped off the stack. + * + * @param L A lua state. + * @param tableRef The index of a table in the Lua registry. + */ + TableItem(lua_State* L, int tableRef) + : LuaRefBase(L) + , m_tableRef(LUA_NOREF) + , m_keyRef(luaL_ref(L, LUA_REGISTRYINDEX)) + { + lua_rawgeti(m_L, LUA_REGISTRYINDEX, tableRef); + m_tableRef = luaL_ref(L, LUA_REGISTRYINDEX); + } + + //========================================================================================= + /** + * @brief Create a TableItem via copy constructor. + * + * It is best to avoid code paths that invoke this, because it creates an extra temporary Lua reference. Typically this is done by + * passing the TableItem parameter as a `const` reference. + * + * @param other Another Lua table item reference. + */ + TableItem(const TableItem& other) + : LuaRefBase(other.m_L) + , m_tableRef(LUA_NOREF) + , m_keyRef(LUA_NOREF) + { + lua_rawgeti(m_L, LUA_REGISTRYINDEX, other.m_tableRef); + m_tableRef = luaL_ref(m_L, LUA_REGISTRYINDEX); + + lua_rawgeti(m_L, LUA_REGISTRYINDEX, other.m_keyRef); + m_keyRef = luaL_ref(m_L, LUA_REGISTRYINDEX); + } + + //========================================================================================= + /** + * @brief Destroy the proxy. + * + * This does not destroy the table value. + */ + ~TableItem() + { + if (m_keyRef != LUA_NOREF) + luaL_unref(m_L, LUA_REGISTRYINDEX, m_keyRef); + + if (m_tableRef != LUA_NOREF) + luaL_unref(m_L, LUA_REGISTRYINDEX, m_tableRef); + } + + //========================================================================================= + /** + * @brief Assign a new value to this table key. + * + * This may invoke metamethods. + * + * @tparam T The type of a value to assing. + * + * @param v A value to assign. + * + * @returns This reference. + */ + template + TableItem& operator=(T v) + { + StackPop p(m_L, 1); + lua_rawgeti(m_L, LUA_REGISTRYINDEX, m_tableRef); + lua_rawgeti(m_L, LUA_REGISTRYINDEX, m_keyRef); + + std::error_code ec; + if (! Stack::push(m_L, v, ec)) + return *this; + + lua_settable(m_L, -3); + return *this; + } + + //========================================================================================= + /** + * @brief Assign a new value to this table key. + * + * The assignment is raw, no metamethods are invoked. + * + * @tparam T The type of a value to assing. + * + * @param v A value to assign. + * + * @returns This reference. + */ + template + TableItem& rawset(T v) + { + StackPop p(m_L, 1); + lua_rawgeti(m_L, LUA_REGISTRYINDEX, m_tableRef); + lua_rawgeti(m_L, LUA_REGISTRYINDEX, m_keyRef); + + std::error_code ec; + if (! Stack::push(m_L, v, ec)) + return *this; + + lua_rawset(m_L, -3); + return *this; + } + + //========================================================================================= + /** + * @brief Push the value onto the Lua stack. + */ + using LuaRefBase::push; + + void push() const + { + lua_rawgeti(m_L, LUA_REGISTRYINDEX, m_tableRef); + lua_rawgeti(m_L, LUA_REGISTRYINDEX, m_keyRef); + lua_gettable(m_L, -2); + lua_remove(m_L, -2); // remove the table + } + + //========================================================================================= + /** + * @brief Access a table value using a key. + * + * This invokes metamethods. + * + * @tparam T The type of a key. + * + * @param key A key value. + * + * @returns A Lua table item reference. + */ + template + TableItem operator[](T key) const + { + return LuaRef(*this)[key]; + } + + //========================================================================================= + /** + * @brief Access a table value using a key. + * + * The operation is raw, metamethods are not invoked. The result is passed by value and may not be modified. + * + * @tparam T The type of a key. + * + * @param key A key value. + * + * @returns A Lua value reference. + */ + template + LuaRef rawget(T key) const + { + return LuaRef(*this).rawget(key); + } + + private: + int m_tableRef; + int m_keyRef; + }; + + friend struct Stack; + friend struct Stack; + + //========================================================================================= + /** + * @brief Create a reference to an object at the top of the Lua stack and pop it. + * + * This constructor is private and not invoked directly. Instead, use the `fromStack` function. + * + * @param L A Lua state. + * + * @note The object is popped. + */ + LuaRef(lua_State* L, FromStack) + : LuaRefBase(L) + , m_ref(luaL_ref(m_L, LUA_REGISTRYINDEX)) + { + } + + //========================================================================================= + /** + * @brief Create a reference to an object on the Lua stack. + * + * This constructor is private and not invoked directly. Instead, use the `fromStack` function. + * + * @param L A Lua state. + * + * @param index The index of the value on the Lua stack. + * + * @note The object is not popped. + */ + LuaRef(lua_State* L, int index, FromStack) + : LuaRefBase(L) + , m_ref(LUA_NOREF) + { + lua_pushvalue(m_L, index); + m_ref = luaL_ref(m_L, LUA_REGISTRYINDEX); + } + +public: + //============================================================================================= + /** + * @brief Create an invalid reference that will be treated as nil. + * + * The Lua reference may be assigned later. + * + * @param L A Lua state. + */ + LuaRef(lua_State* L) + : LuaRefBase(L) + , m_ref(LUA_NOREF) + { + } + + //============================================================================================= + /** + * @brief Push a value onto a Lua stack and return a reference to it. + * + * @param L A Lua state. + * @param v A value to push. + */ + template + LuaRef(lua_State* L, T v) + : LuaRefBase(L) + , m_ref(LUA_NOREF) + { + std::error_code ec; + if (! Stack::push(m_L, v, ec)) + return; + + m_ref = luaL_ref(m_L, LUA_REGISTRYINDEX); + } + + //============================================================================================= + /** + * @brief Create a reference to a table item. + * + * @param v A table item reference. + */ + LuaRef(const TableItem& v) + : LuaRefBase(v.state()) + , m_ref(v.createRef()) + { + } + + //============================================================================================= + /** + * @brief Create a new reference to an existing Lua value. + * + * @param other An existing reference. + */ + LuaRef(const LuaRef& other) + : LuaRefBase(other.m_L) + , m_ref(other.createRef()) + { + } + + //============================================================================================= + /** + * @brief Destroy a reference. + * + * The corresponding Lua registry reference will be released. + * + * @note If the state refers to a thread, it is the responsibility of the caller to ensure that the thread still exists when the LuaRef is destroyed. + */ + ~LuaRef() + { + if (m_ref != LUA_NOREF) + luaL_unref(m_L, LUA_REGISTRYINDEX, m_ref); + } + + //============================================================================================= + /** + * @brief Return a reference to a top Lua stack item. + * + * The stack item is not popped. + * + * @param L A Lua state. + * + * @returns A reference to a value on the top of a Lua stack. + */ + static LuaRef fromStack(lua_State* L) + { + return LuaRef(L, FromStack()); + } + + //============================================================================================= + /** + * @brief Return a reference to a Lua stack item with a specified index. + * + * The stack item is not removed. + * + * @param L A Lua state. + * @param index An index in the Lua stack. + * + * @returns A reference to a value in a Lua stack. + */ + static LuaRef fromStack(lua_State* L, int index) + { + lua_pushvalue(L, index); + return LuaRef(L, FromStack()); + } + + //============================================================================================= + /** + * @brief Create a new empty table on the top of a Lua stack and return a reference to it. + * + * @param L A Lua state. + * + * @returns A reference to the newly created table. + * + * @see luabridge::newTable() + */ + static LuaRef newTable(lua_State* L) + { + lua_newtable(L); + return LuaRef(L, FromStack()); + } + + //============================================================================================= + /** + * @brief Return a reference to a named global Lua variable. + * + * @param L A Lua state. + * @param name The name of a global variable. + * + * @returns A reference to the Lua variable. + * + * @see luabridge::getGlobal() + */ + static LuaRef getGlobal(lua_State* L, const char* name) + { + lua_getglobal(L, name); + return LuaRef(L, FromStack()); + } + + //============================================================================================= + /** + * @brief Indicate whether it is an invalid reference. + * + * @returns True if this is an invalid reference, false otherwise. + */ + bool isValid() const { return m_ref != LUA_NOREF; } + + //============================================================================================= + /** + * @brief Assign another LuaRef to this LuaRef. + * + * @param rhs A reference to assign from. + * + * @returns This reference. + */ + LuaRef& operator=(const LuaRef& rhs) + { + LuaRef ref(rhs); + swap(ref); + return *this; + } + + //============================================================================================= + /** + * @brief Assign a table item reference. + * + * @param rhs A table item reference. + * + * @returns This reference. + */ + LuaRef& operator=(const LuaRef::TableItem& rhs) + { + LuaRef ref(rhs); + swap(ref); + return *this; + } + + //============================================================================================= + /** + * @brief Assign nil to this reference. + * + * @returns This reference. + */ + LuaRef& operator=(const LuaNil&) + { + LuaRef ref(m_L); + swap(ref); + return *this; + } + + //============================================================================================= + /** + * @brief Assign a different value to this reference. + * + * @param rhs A value to assign. + * + * @returns This reference. + */ + template + LuaRef& operator=(T rhs) + { + LuaRef ref(m_L, rhs); + swap(ref); + return *this; + } + + //============================================================================================= + /** + * @brief Place the object onto the Lua stack. + */ + using LuaRefBase::push; + + void push() const + { + lua_rawgeti(m_L, LUA_REGISTRYINDEX, m_ref); + } + + //============================================================================================= + /** + * @brief Pop the top of Lua stack and assign the ref to m_ref + */ + void pop() + { + luaL_unref(m_L, LUA_REGISTRYINDEX, m_ref); + m_ref = luaL_ref(m_L, LUA_REGISTRYINDEX); + } + + //============================================================================================= + /** + * @brief Access a table value using a key. + * + * This invokes metamethods. + * + * @param key A key in the table. + * + * @returns A reference to the table item. + */ + template + TableItem operator[](T key) const + { + std::error_code ec; + if (! Stack::push(m_L, key, ec)) + return TableItem(m_L, m_ref); + + return TableItem(m_L, m_ref); + } + + //============================================================================================= + /** + * @brief Access a table value using a key. + * + * The operation is raw, metamethods are not invoked. The result is passed by value and may not be modified. + * + * @param key A key in the table. + * + * @returns A reference to the table item. + */ + template + LuaRef rawget(T key) const + { + StackPop(m_L, 1); + + push(m_L); + + std::error_code ec; + if (! Stack::push(m_L, key, ec)) + return LuaRef(m_L); + + lua_rawget(m_L, -2); + return LuaRef(m_L, FromStack()); + } + +private: + void swap(LuaRef& other) + { + using std::swap; + + swap(m_L, other.m_L); + swap(m_ref, other.m_ref); + } + + int m_ref = LUA_NOREF; +}; + +//================================================================================================= +/** + * @brief Stack specialization for `LuaRef`. + */ +template <> +struct Stack +{ + static bool push(lua_State* L, const LuaRef& v, std::error_code&) + { + return v.push(L), true; + } + + static LuaRef get(lua_State* L, int index) + { + return LuaRef::fromStack(L, index); + } +}; + +//================================================================================================= +/** + * @brief Stack specialization for `TableItem`. + */ +template <> +struct Stack +{ + static bool push(lua_State* L, const LuaRef::TableItem& v, std::error_code&) + { + return v.push(L), true; + } +}; + +//================================================================================================= +/** + * @brief Create a reference to a new, empty table. + * + * This is a syntactic abbreviation for LuaRef::newTable (). + */ +inline LuaRef newTable(lua_State* L) +{ + return LuaRef::newTable(L); +} + +//================================================================================================= +/** + * @brief Create a reference to a value in the global table. + * + * This is a syntactic abbreviation for LuaRef::getGlobal (). + */ +inline LuaRef getGlobal(lua_State* L, const char* name) +{ + return LuaRef::getGlobal(L, name); +} + +//================================================================================================= +/** + * @brief C++ like cast syntax. + */ +template +T cast(const LuaRef& ref) +{ + return ref.cast(); +} + +} // namespace luabridge + +// End File: Source/LuaBridge/detail/LuaRef.h + +// Begin File: Source/LuaBridge/detail/Invoke.h + +// https://github.com/kunitoki/LuaBridge3 +// Copyright 2021, Lucio Asnaghi +// SPDX-License-Identifier: MIT + +namespace luabridge { + +//================================================================================================= +/** + * @brief Result of a lua invocation. + */ +class LuaResult +{ +public: + /** + * @brief Get if the result was ok and didn't raise a lua error. + */ + explicit operator bool() const noexcept + { + return !m_ec; + } + + /** + * @brief Return if the invocation was ok and didn't raise a lua error. + */ + bool wasOk() const noexcept + { + return !m_ec; + } + + /** + * @brief Return if the invocation did raise a lua error. + */ + bool hasFailed() const noexcept + { + return !!m_ec; + } + + /** + * @brief Return the error code, if any. + * + * In case the invcation didn't raise any lua error, the value returned equals to a + * default constructed std::error_code. + */ + std::error_code errorCode() const noexcept + { + return m_ec; + } + + /** + * @brief Return the error message, if any. + */ + std::string errorMessage() const noexcept + { + if (std::holds_alternative(m_data)) + { + const auto& message = std::get(m_data); + return message.empty() ? m_ec.message() : message; + } + + return {}; + } + + /** + * @brief Return the number of return values. + */ + std::size_t size() const noexcept + { + if (std::holds_alternative>(m_data)) + return std::get>(m_data).size(); + + return 0; + } + + /** + * @brief Get a return value at a specific index. + */ + LuaRef operator[](std::size_t index) const + { + assert(m_ec == std::error_code()); + + if (std::holds_alternative>(m_data)) + { + const auto& values = std::get>(m_data); + + assert(index < values.size()); + return values[index]; + } + + return LuaRef(m_L); + } + +private: + template + friend LuaResult call(const LuaRef&, Args&&...); + + static LuaResult errorFromStack(lua_State* L, std::error_code ec) + { + auto errorString = lua_tostring(L, -1); + lua_pop(L, 1); + + return LuaResult(L, ec, errorString ? errorString : ec.message()); + } + + static LuaResult valuesFromStack(lua_State* L, int stackTop) + { + std::vector values; + + const int numReturnedValues = lua_gettop(L) - stackTop; + if (numReturnedValues > 0) + { + values.reserve(numReturnedValues); + + for (int index = numReturnedValues; index > 0; --index) + values.emplace_back(LuaRef::fromStack(L, -index)); + + lua_pop(L, numReturnedValues); + } + + return LuaResult(L, std::move(values)); + } + + LuaResult(lua_State* L, std::error_code ec, std::string_view errorString) + : m_L(L) + , m_ec(ec) + , m_data(std::string(errorString)) + { + } + + explicit LuaResult(lua_State* L, std::vector values) noexcept + : m_L(L) + , m_data(std::move(values)) + { + } + + lua_State* m_L = nullptr; + std::error_code m_ec; + std::variant, std::string> m_data; +}; + +//================================================================================================= +/** + * @brief Safely call Lua code. + * + * These overloads allow Lua code to be called throught lua_pcall. The return value is provided as + * a LuaResult which will hold the return values or an error if the call failed. + * + * If an error occurs, a LuaException is thrown or if exceptions are disabled the FunctionResult will + * contain a error code and evaluate false. + * + * @note The function might throw a LuaException if the application is compiled with exceptions on + * and they are enabled globally by calling `enableExceptions` in two cases: + * - one of the arguments passed cannot be pushed in the stack, for example a unregistered C++ class + * - the lua invaction calls the panic handler, which is converted to a C++ exception + * + * @return A result object. +*/ +template +LuaResult call(const LuaRef& object, Args&&... args) +{ + lua_State* L = object.state(); + const int stackTop = lua_gettop(L); + + object.push(); + + { + std::error_code ec; + auto pushedArgs = detail::push_arguments(L, std::forward_as_tuple(args...), ec); + if (ec) + { + lua_pop(L, static_cast(pushedArgs) + 1); + return LuaResult(L, ec, ec.message()); + } + } + + int code = lua_pcall(L, sizeof...(Args), LUA_MULTRET, 0); + if (code != LUABRIDGE_LUA_OK) + { + auto ec = makeErrorCode(ErrorCode::LuaFunctionCallFailed); + +#if LUABRIDGE_HAS_EXCEPTIONS + if (LuaException::areExceptionsEnabled()) + LuaException::raise(LuaException(L, ec)); +#else + return LuaResult::errorFromStack(L, ec); +#endif + } + + return LuaResult::valuesFromStack(L, stackTop); +} + +//============================================================================================= +/** + * @brief Wrapper for lua_pcall that throws if exceptions are enabled. + */ +inline int pcall(lua_State* L, int nargs = 0, int nresults = 0, int msgh = 0) +{ + const int code = lua_pcall(L, nargs, nresults, msgh); + +#if LUABRIDGE_HAS_EXCEPTIONS + if (code != LUABRIDGE_LUA_OK && LuaException::areExceptionsEnabled()) + LuaException::raise(LuaException(L, makeErrorCode(ErrorCode::LuaFunctionCallFailed))); +#endif + + return code; +} + +//============================================================================================= +template +template +LuaResult LuaRefBase::operator()(Args&&... args) const +{ + return call(*this, std::forward(args)...); +} + +} // namespace luabridge + +// End File: Source/LuaBridge/detail/Invoke.h + +// Begin File: Source/LuaBridge/detail/Iterator.h + +// https://github.com/kunitoki/LuaBridge3 +// Copyright 2020, Lucio Asnaghi +// Copyright 2018, Dmitry Tarakanov +// Copyright 2012, Vinnie Falco +// SPDX-License-Identifier: MIT + +namespace luabridge { + +//================================================================================================= +/** + * @brief Iterator class to allow table iteration. + * + * @see Range class. + */ +class Iterator +{ +public: + explicit Iterator(const LuaRef& table, bool isEnd = false) + : m_L(table.state()) + , m_table(table) + , m_key(table.state()) // m_key is nil + , m_value(table.state()) // m_value is nil + { + if (! isEnd) + { + next(); // get the first (key, value) pair from table + } + } + + /** + * @brief Return an associated Lua state. + * + * @return A Lua state. + */ + lua_State* state() const noexcept + { + return m_L; + } + + /** + * @brief Dereference the iterator. + * + * @return A key-value pair for a current table entry. + */ + std::pair operator*() const + { + return std::make_pair(m_key, m_value); + } + + /** + * @brief Return the value referred by the iterator. + * + * @return A value for the current table entry. + */ + LuaRef operator->() const + { + return m_value; + } + + /** + * @brief Compare two iterators. + * + * @param rhs Another iterator. + * + * @return True if iterators point to the same entry of the same table, false otherwise. + */ + bool operator!=(const Iterator& rhs) const + { + assert(m_L == rhs.m_L); + + return ! m_table.rawequal(rhs.m_table) || ! m_key.rawequal(rhs.m_key); + } + + /** + * @brief Move the iterator to the next table entry. + * + * @return This iterator. + */ + Iterator& operator++() + { + if (isNil()) + { + // if the iterator reaches the end, do nothing + return *this; + } + else + { + next(); + return *this; + } + } + + /** + * @brief Check if the iterator points after the last table entry. + * + * @return True if there are no more table entries to iterate, false otherwise. + */ + bool isNil() const noexcept + { + return m_key.isNil(); + } + + /** + * @brief Return the key for the current table entry. + * + * @return A reference to the entry key. + */ + LuaRef key() const + { + return m_key; + } + + /** + * @brief Return the key for the current table entry. + * + * @return A reference to the entry value. + */ + LuaRef value() const + { + return m_value; + } + +private: + // Don't use postfix increment, it is less efficient + Iterator operator++(int); + + void next() + { + m_table.push(); + m_key.push(); + + if (lua_next(m_L, -2)) + { + m_value.pop(); + m_key.pop(); + } + else + { + m_key = LuaNil(); + m_value = LuaNil(); + } + + lua_pop(m_L, 1); + } + + lua_State* m_L = nullptr; + LuaRef m_table; + LuaRef m_key; + LuaRef m_value; +}; + +//================================================================================================= +/** + * @brief Range class taking two table iterators. + */ +class Range +{ +public: + Range(const Iterator& begin, const Iterator& end) + : m_begin(begin) + , m_end(end) + { + } + + const Iterator& begin() const noexcept + { + return m_begin; + } + + const Iterator& end() const noexcept + { + return m_end; + } + +private: + Iterator m_begin; + Iterator m_end; +}; + +//================================================================================================= +/** + * @brief Return a range for the Lua table reference. + * + * @return A range suitable for range-based for statement. + */ +inline Range pairs(const LuaRef& table) +{ + return Range{ Iterator(table, false), Iterator(table, true) }; +} + +} // namespace luabridge + +// End File: Source/LuaBridge/detail/Iterator.h + +// Begin File: Source/LuaBridge/detail/Security.h + +// https://github.com/vinniefalco/LuaBridge +// Copyright 2021, Lucio Asnaghi +// Copyright 2012, Vinnie Falco +// SPDX-License-Identifier: MIT + +namespace luabridge { + +//================================================================================================= +/** + * @brief Security options. + */ +class Security +{ +public: + static bool hideMetatables() noexcept + { + return getSettings().hideMetatables; + } + + static void setHideMetatables(bool shouldHide) noexcept + { + getSettings().hideMetatables = shouldHide; + } + +private: + struct Settings + { + Settings() noexcept + : hideMetatables(true) + { + } + + bool hideMetatables; + }; + + static Settings& getSettings() noexcept + { + static Settings settings; + return settings; + } +}; + +//================================================================================================= +/** + * @brief Get a global value from the lua_State. + * + * @note This works on any type specialized by `Stack`, including `LuaRef` and its table proxies. +*/ +template +T getGlobal(lua_State* L, const char* name) +{ + lua_getglobal(L, name); + + auto result = luabridge::Stack::get(L, -1); + + lua_pop(L, 1); + + return result; +} + +//================================================================================================= +/** + * @brief Set a global value in the lua_State. + * + * @note This works on any type specialized by `Stack`, including `LuaRef` and its table proxies. +*/ +template +bool setGlobal(lua_State* L, T&& t, const char* name) +{ + std::error_code ec; + if (push(L, std::forward(t), ec)) + { + lua_setglobal(L, name); + return true; + } + + return false; +} + +//================================================================================================= +/** + * @brief Change whether or not metatables are hidden (on by default). + */ +inline void setHideMetatables(bool shouldHide) noexcept +{ + Security::setHideMetatables(shouldHide); +} + +} // namespace luabridge + +// End File: Source/LuaBridge/detail/Security.h + +// Begin File: Source/LuaBridge/detail/Namespace.h + +// https://github.com/kunitoki/LuaBridge3 +// Copyright 2020, Lucio Asnaghi +// Copyright 2019, Dmitry Tarakanov +// Copyright 2012, Vinnie Falco +// Copyright 2007, Nathan Reed +// SPDX-License-Identifier: MIT + +namespace luabridge { +namespace detail { + +//================================================================================================= +/** + * @brief Base for class and namespace registration. + * + * Maintains Lua stack in the proper state. Once beginNamespace, beginClass or deriveClass is called the parent object upon its destruction + * may no longer clear the Lua stack. + * + * Then endNamespace or endClass is called, a new parent is created and the child transfers the responsibility for clearing stack to it. + * + * So there can be maximum one "active" registrar object. + */ +class Registrar +{ +protected: + Registrar(lua_State* L) + : L(L) + , m_stackSize(0) + { + } + + Registrar(lua_State* L, int skipStackPops) + : L(L) + , m_stackSize(0) + , m_skipStackPops(skipStackPops) + { + } + + Registrar(const Registrar& rhs) + : L(rhs.L) + , m_stackSize(std::exchange(rhs.m_stackSize, 0)) + , m_skipStackPops(std::exchange(rhs.m_skipStackPops, 0)) + { + } + + Registrar& operator=(const Registrar& rhs) + { + using std::swap; + + Registrar tmp(rhs); + + swap(m_stackSize, tmp.m_stackSize); + + return *this; + } + + ~Registrar() + { + const int popsCount = m_stackSize - m_skipStackPops; + if (popsCount > 0) + { + assert(popsCount <= lua_gettop(L)); + + lua_pop(L, popsCount); + } + } + + void assertIsActive() const + { + if (m_stackSize == 0) + { + throw_or_assert("Unable to continue registration"); + } + } + + lua_State* const L = nullptr; + int mutable m_stackSize = 0; + int mutable m_skipStackPops = 0; +}; + +} // namespace detail + +//================================================================================================= +/** + * @brief Provides C++ to Lua registration capabilities. + * + * This class is not instantiated directly, call `getGlobalNamespace` to start the registration process. + */ +class Namespace : public detail::Registrar +{ + //============================================================================================= +#if 0 + /** + Error reporting. + + VF: This function looks handy, why aren't we using it? + */ + static int luaError (lua_State* L, std::string message) + { + assert (lua_isstring (L, lua_upvalueindex (1))); + std::string s; + + // Get information on the caller's caller to format the message, + // so the error appears to originate from the Lua source. + lua_Debug ar; + int result = lua_getstack (L, 2, &ar); + if (result != 0) + { + lua_getinfo (L, "Sl", &ar); + s = ar.short_src; + if (ar.currentline != -1) + { + // poor mans int to string to avoid . + lua_pushnumber (L, ar.currentline); + s = s + ":" + lua_tostring (L, -1) + ": "; + lua_pop (L, 1); + } + } + + s = s + message; + + return luaL_error (L, s.c_str ()); + } +#endif + + //============================================================================================= + /** + * @brief Factored base to reduce template instantiations. + */ + class ClassBase : public detail::Registrar + { + public: + explicit ClassBase(Namespace& parent) + : Registrar(parent) + { + } + + using Registrar::operator=; + + protected: + //========================================================================================= + /** + * @brief Create the const table. + */ + void createConstTable(const char* name, bool trueConst = true) + { + assert(name != nullptr); + + std::string type_name = std::string(trueConst ? "const " : "") + name; + + // Stack: namespace table (ns) + lua_newtable(L); // Stack: ns, const table (co) + lua_pushvalue(L, -1); // Stack: ns, co, co + lua_setmetatable(L, -2); // co.__metatable = co. Stack: ns, co + + lua_pushstring(L, type_name.c_str()); + lua_rawsetp(L, -2, detail::getTypeKey()); // co [typeKey] = name. Stack: ns, co + + lua_pushcfunction(L, &detail::index_metamethod); + rawsetfield(L, -2, "__index"); + + lua_pushcfunction(L, &detail::newindex_object_metamethod); + rawsetfield(L, -2, "__newindex"); + + lua_newtable(L); + lua_rawsetp(L, -2, detail::getPropgetKey()); + + if (Security::hideMetatables()) + { + lua_pushnil(L); + rawsetfield(L, -2, "__metatable"); + } + } + + //========================================================================================= + /** + * @brief Create the class table. + * + * The Lua stack should have the const table on top. + */ + void createClassTable(const char* name) + { + assert(name != nullptr); + + // Stack: namespace table (ns), const table (co) + + // Class table is the same as const table except the propset table + createConstTable(name, false); // Stack: ns, co, cl + + lua_newtable(L); // Stack: ns, co, cl, propset table (ps) + lua_rawsetp(L, -2, detail::getPropsetKey()); // cl [propsetKey] = ps. Stack: ns, co, cl + + lua_pushvalue(L, -2); // Stack: ns, co, cl, co + lua_rawsetp(L, -2, detail::getConstKey()); // cl [constKey] = co. Stack: ns, co, cl + + lua_pushvalue(L, -1); // Stack: ns, co, cl, cl + lua_rawsetp(L, -3, detail::getClassKey()); // co [classKey] = cl. Stack: ns, co, cl + } + + //========================================================================================= + /** + * @brief Create the static table. + */ + void createStaticTable(const char* name) + { + assert(name != nullptr); + + // Stack: namespace table (ns), const table (co), class table (cl) + lua_newtable(L); // Stack: ns, co, cl, visible static table (vst) + lua_newtable(L); // Stack: ns, co, cl, st, static metatable (st) + lua_pushvalue(L, -1); // Stack: ns, co, cl, vst, st, st + lua_setmetatable(L, -3); // st.__metatable = mt. Stack: ns, co, cl, vst, st + lua_insert(L, -2); // Stack: ns, co, cl, st, vst + rawsetfield(L, -5, name); // ns [name] = vst. Stack: ns, co, cl, st + +#if 0 + lua_pushlightuserdata (L, this); + lua_pushcclosure (L, &tostringMetaMethod, 1); + rawsetfield (L, -2, "__tostring"); +#endif + + lua_pushcfunction(L, &detail::index_metamethod); + rawsetfield(L, -2, "__index"); + + lua_pushcfunction(L, &detail::newindex_static_metamethod); + rawsetfield(L, -2, "__newindex"); + + lua_newtable(L); // Stack: ns, co, cl, st, proget table (pg) + lua_rawsetp(L, -2, detail::getPropgetKey()); // st [propgetKey] = pg. Stack: ns, co, cl, st + + lua_newtable(L); // Stack: ns, co, cl, st, propset table (ps) + lua_rawsetp(L, -2, detail::getPropsetKey()); // st [propsetKey] = pg. Stack: ns, co, cl, st + + lua_pushvalue(L, -2); // Stack: ns, co, cl, st, cl + lua_rawsetp(L, -2, detail::getClassKey()); // st [classKey] = cl. Stack: ns, co, cl, st + + if (Security::hideMetatables()) + { + lua_pushnil(L); + rawsetfield(L, -2, "__metatable"); + } + } + + //========================================================================================= + /** + * @brief lua_CFunction to construct a class object wrapped in a container. + */ + template + static int ctorContainerProxy(lua_State* L) + { + using T = typename ContainerTraits::Type; + + T* object = detail::constructor::call(detail::make_arguments_list(L)); + + std::error_code ec; + if (! detail::UserdataSharedHelper::push(L, object, ec)) + luaL_error(L, ec.message().c_str()); + + return 1; + } + + //========================================================================================= + /** + * @brief lua_CFunction to construct a class object in-place in the userdata. + */ + template + static int ctorPlacementProxy(lua_State* L) + { + std::error_code ec; + detail::UserdataValue* value = detail::UserdataValue::place(L, ec); + if (! value) + luaL_error(L, ec.message().c_str()); + + detail::constructor::call(value->getObject(), detail::make_arguments_list(L)); + + value->commit(); + + return 1; + } + + //========================================================================================= + /** + * @brief Asserts on stack state. + */ + void assertStackState() const + { + // Stack: const table (co), class table (cl), static table (st) + assert(lua_istable(L, -3)); + assert(lua_istable(L, -2)); + assert(lua_istable(L, -1)); + } + }; + + //============================================================================================= + /** + * @brief Provides a class registration in a lua_State. + * + * After construction the Lua stack holds these objects: + * -1 static table + * -2 class table + * -3 const table + * -4 enclosing namespace table + */ + template + class Class : public ClassBase + { + public: + //========================================================================================= + + /** + * @brief Register a new class or add to an existing class registration. + * + * @param name The new class name. + * @param parent A parent namespace object. + */ + Class(const char* name, Namespace& parent) + : ClassBase(parent) + { + assert(name != nullptr); + assert(lua_istable(L, -1)); // Stack: namespace table (ns) + + rawgetfield(L, -1, name); // Stack: ns, static table (st) | nil + + if (lua_isnil(L, -1)) // Stack: ns, nil + { + lua_pop(L, 1); // Stack: ns + + createConstTable(name); // Stack: ns, const table (co) + lua_pushcfunction(L, &detail::gc_metamethod); // Stack: ns, co, function + rawsetfield(L, -2, "__gc"); // co ["__gc"] = function. Stack: ns, co + ++m_stackSize; + + createClassTable(name); // Stack: ns, co, class table (cl) + lua_pushcfunction(L, &detail::gc_metamethod); // Stack: ns, co, cl, function + rawsetfield(L, -2, "__gc"); // cl ["__gc"] = function. Stack: ns, co, cl + ++m_stackSize; + + createStaticTable(name); // Stack: ns, co, cl, st + ++m_stackSize; + + // Map T back to its tables. + lua_pushvalue(L, -1); // Stack: ns, co, cl, st, st + lua_rawsetp(L, LUA_REGISTRYINDEX, detail::getStaticRegistryKey()); // Stack: ns, co, cl, st + lua_pushvalue(L, -2); // Stack: ns, co, cl, st, cl + lua_rawsetp(L, LUA_REGISTRYINDEX, detail::getClassRegistryKey()); // Stack: ns, co, cl, st + lua_pushvalue(L, -3); // Stack: ns, co, cl, st, co + lua_rawsetp(L, LUA_REGISTRYINDEX, detail::getConstRegistryKey()); // Stack: ns, co, cl, st + } + else + { + assert(lua_istable(L, -1)); // Stack: ns, st + ++m_stackSize; + + // Map T back from its stored tables + + lua_rawgetp(L, LUA_REGISTRYINDEX, detail::getConstRegistryKey()); // Stack: ns, st, co + lua_insert(L, -2); // Stack: ns, co, st + ++m_stackSize; + + lua_rawgetp(L, LUA_REGISTRYINDEX, detail::getClassRegistryKey()); // Stack: ns, co, st, cl + lua_insert(L, -2); // Stack: ns, co, cl, st + ++m_stackSize; + } + } + + //========================================================================================= + /** + * @brief Derive a new class. + * + * @param name The class name. + * @param parent A parent namespace object. + * @param staticKey Key where the class is stored. + */ + Class(const char* name, Namespace& parent, void const* const staticKey) + : ClassBase(parent) + { + assert(name != nullptr); + assert(lua_istable(L, -1)); // Stack: namespace table (ns) + + createConstTable(name); // Stack: ns, const table (co) + lua_pushcfunction(L, &detail::gc_metamethod); // Stack: ns, co, function + rawsetfield(L, -2, "__gc"); // co ["__gc"] = function. Stack: ns, co + ++m_stackSize; + + createClassTable(name); // Stack: ns, co, class table (cl) + lua_pushcfunction(L, &detail::gc_metamethod); // Stack: ns, co, cl, function + rawsetfield(L, -2, "__gc"); // cl ["__gc"] = function. Stack: ns, co, cl + ++m_stackSize; + + createStaticTable(name); // Stack: ns, co, cl, st + ++m_stackSize; + + lua_rawgetp( L, LUA_REGISTRYINDEX, staticKey); // Stack: ns, co, cl, st, parent st (pst) | nil + if (lua_isnil(L, -1)) // Stack: ns, co, cl, st, nil + { + ++m_stackSize; + + throw_or_assert("Base class is not registered"); + return; + } + + assert(lua_istable(L, -1)); // Stack: ns, co, cl, st, pst + + lua_rawgetp(L, -1, detail::getClassKey()); // Stack: ns, co, cl, st, pst, parent cl (pcl) + assert(lua_istable(L, -1)); + + lua_rawgetp(L, -1, detail::getConstKey()); // Stack: ns, co, cl, st, pst, pcl, parent co (pco) + assert(lua_istable(L, -1)); + + lua_rawsetp(L, -6, detail::getParentKey()); // co [parentKey] = pco. Stack: ns, co, cl, st, pst, pcl + lua_rawsetp(L, -4, detail::getParentKey()); // cl [parentKey] = pcl. Stack: ns, co, cl, st, pst + lua_rawsetp(L, -2, detail::getParentKey()); // st [parentKey] = pst. Stack: ns, co, cl, st + + lua_pushvalue(L, -1); // Stack: ns, co, cl, st, st + lua_rawsetp(L, LUA_REGISTRYINDEX, detail::getStaticRegistryKey()); // Stack: ns, co, cl, st + lua_pushvalue(L, -2); // Stack: ns, co, cl, st, cl + lua_rawsetp(L, LUA_REGISTRYINDEX, detail::getClassRegistryKey()); // Stack: ns, co, cl, st + lua_pushvalue(L, -3); // Stack: ns, co, cl, st, co + lua_rawsetp(L, LUA_REGISTRYINDEX, detail::getConstRegistryKey()); // Stack: ns, co, cl, st + } + + //========================================================================================= + /** + * @brief Continue registration in the enclosing namespace. + * + * @returns A parent registration object. + */ + Namespace endClass() + { + assert(m_stackSize > 3); + + m_stackSize -= 3; + lua_pop(L, 3); + return Namespace(*this); + } + + //========================================================================================= + /** + * @brief Add or replace a static property. + * + * @tparam U The type of the property. + * + * @param name The property name. + * @param value A property value pointer. + * @param isWritable True for a read-write, false for read-only property. + * + * @returns This class registration object. + */ + template + Class& addStaticProperty(const char* name, U* value, bool isWritable = true) + { + assert(name != nullptr); + assertStackState(); // Stack: const table (co), class table (cl), static table (st) + + lua_pushlightuserdata(L, value); // Stack: co, cl, st, pointer + lua_pushcclosure(L, &detail::property_getter::call, 1); // Stack: co, cl, st, getter + detail::add_property_getter(L, name, -2); // Stack: co, cl, st + + if (isWritable) + { + lua_pushlightuserdata(L, value); // Stack: co, cl, st, ps, pointer + lua_pushcclosure(L, &detail::property_setter::call, 1); // Stack: co, cl, st, ps, setter + } + else + { + lua_pushstring(L, name); // Stack: co, cl, st, name + lua_pushcclosure(L, &detail::read_only_error, 1); // Stack: co, cl, st, function + } + + detail::add_property_setter(L, name, -2); // Stack: co, cl, st + + return *this; + } + + //========================================================================================= + /** + * @brief Add or replace a static property member. + * + * @tparam U The type of the property. + * + * @param name The property name. + * @param get A property getter function pointer. + * @param set A property setter function pointer, optional, nullable. Omit or pass nullptr for a read-only property. + * + * @returns This class registration object. + */ + template + Class& addStaticProperty(const char* name, U (*get)(), void (*set)(U) = nullptr) + { + assert(name != nullptr); + assertStackState(); // Stack: const table (co), class table (cl), static table (st) + + lua_pushlightuserdata(L, reinterpret_cast(get)); // Stack: co, cl, st, function ptr + lua_pushcclosure(L, &detail::invoke_proxy_function, 1); // Stack: co, cl, st, getter + detail::add_property_getter(L, name, -2); // Stack: co, cl, st + + if (set != nullptr) + { + lua_pushlightuserdata(L, reinterpret_cast(set)); // Stack: co, cl, st, function ptr + lua_pushcclosure(L, &detail::invoke_proxy_function, 1); // Stack: co, cl, st, setter + } + else + { + lua_pushstring(L, name); // Stack: co, cl, st, ps, name + lua_pushcclosure(L, &detail::read_only_error, 1); // Stack: co, cl, st, function + } + + detail::add_property_setter(L, name, -2); // Stack: co, cl, st + + return *this; + } + + //========================================================================================= + /** + * @brief Add or replace a static member function. + */ + template >> + Class& addStaticFunction(const char* name, Function fp) + { + assert(name != nullptr); + assertStackState(); // Stack: const table (co), class table (cl), static table (st) + + lua_pushlightuserdata(L, reinterpret_cast(fp)); // Stack: co, cl, st, function ptr + lua_pushcclosure(L, &detail::invoke_proxy_function, 1); // co, cl, st, function + rawsetfield(L, -2, name); // co, cl, st + + return *this; + } + + //========================================================================================= + /** + * @brief Add or replace a static member function for constructible by std::function. + */ + template >> + Class addStaticFunction(const char* name, Function function) + { + using FnType = decltype(function); + + assert(name != nullptr); + assertStackState(); // Stack: const table (co), class table (cl), static table (st) + + lua_newuserdata_aligned(L, std::move(function)); // Stack: co, cl, st, function userdata (ud) + lua_newtable(L); // Stack: co, cl, st, ud, ud metatable (mt) + lua_pushcfunction(L, &lua_deleteuserdata_aligned); // Stack: co, cl, st, ud, mt, gc function + rawsetfield(L, -2, "__gc"); // Stack: co, cl, st, ud, mt + lua_setmetatable(L, -2); // Stack: co, cl, st, ud + lua_pushcclosure(L, &detail::invoke_proxy_functor, 1); // Stack: co, cl, st, function + rawsetfield(L, -2, name); // Stack: co, cl, st + + return *this; + } + + //========================================================================================= + /** + * @brief Add or replace a lua_CFunction. + * + * @param name The name of the function. + * @param fp A C-function pointer. + * + * @returns This class registration object. + */ + Class& addStaticFunction(const char* name, lua_CFunction fp) + { + assert(name != nullptr); + assertStackState(); // Stack: const table (co), class table (cl), static table (st) + + lua_pushcfunction(L, fp); // co, cl, st, function + rawsetfield(L, -2, name); // co, cl, st + + return *this; + } + + //========================================================================================= + /** + * @brief Add or replace a property member. + */ + template + Class& addProperty(const char* name, U V::*mp, bool isWritable = true) + { + static_assert(std::is_base_of_v); + + using MemberPtrType = decltype(mp); + + assert(name != nullptr); + assertStackState(); // Stack: const table (co), class table (cl), static table (st) + + new (lua_newuserdata(L, sizeof(MemberPtrType))) MemberPtrType(mp); // Stack: co, cl, st, field ptr + lua_pushcclosure(L, &detail::property_getter::call, 1); // Stack: co, cl, st, getter + lua_pushvalue(L, -1); // Stack: co, cl, st, getter, getter + detail::add_property_getter(L, name, -5); // Stack: co, cl, st, getter + detail::add_property_getter(L, name, -3); // Stack: co, cl, st + + if (isWritable) + { + new (lua_newuserdata(L, sizeof(MemberPtrType))) MemberPtrType(mp); // Stack: co, cl, st, field ptr + lua_pushcclosure(L, &detail::property_setter::call, 1); // Stack: co, cl, st, setter + detail::add_property_setter(L, name, -3); // Stack: co, cl, st + } + + return *this; + } + + //========================================================================================= + /** + * @brief Add or replace a property member. + */ + template + Class& addProperty(const char* name, TG (T::*get)() const, void (T::*set)(TS) = nullptr) + { + using GetType = TG (T::*)() const; + using SetType = void (T::*)(TS); + + assert(name != nullptr); + assertStackState(); // Stack: const table (co), class table (cl), static table (st) + + new (lua_newuserdata(L, sizeof(GetType))) GetType(get); // Stack: co, cl, st, funcion ptr + lua_pushcclosure(L, &detail::invoke_const_member_function, 1); // Stack: co, cl, st, getter + lua_pushvalue(L, -1); // Stack: co, cl, st, getter, getter + detail::add_property_getter(L, name, -5); // Stack: co, cl, st, getter + detail::add_property_getter(L, name, -3); // Stack: co, cl, st + + if (set != nullptr) + { + new (lua_newuserdata(L, sizeof(SetType))) SetType(set); // Stack: co, cl, st, function ptr + lua_pushcclosure(L, &detail::invoke_member_function, 1); // Stack: co, cl, st, setter + detail::add_property_setter(L, name, -3); // Stack: co, cl, st + } + + return *this; + } + + //========================================================================================= + /** + * @brief Add or replace a property member. + */ + template + Class& addProperty(const char* name, TG (T::*get)(lua_State*) const, void (T::*set)(TS, lua_State*) = nullptr) + { + using GetType = TG (T::*)(lua_State*) const; + using SetType = void (T::*)(TS, lua_State*); + + assert(name != nullptr); + assertStackState(); // Stack: const table (co), class table (cl), static table (st) + + new (lua_newuserdata(L, sizeof(GetType))) GetType(get); // Stack: co, cl, st, funcion ptr + lua_pushcclosure(L, &detail::invoke_const_member_function, 1); // Stack: co, cl, st, getter + lua_pushvalue(L, -1); // Stack: co, cl, st, getter, getter + detail::add_property_getter(L, name, -5); // Stack: co, cl, st, getter + detail::add_property_getter(L, name, -3); // Stack: co, cl, st + + if (set != nullptr) + { + new (lua_newuserdata(L, sizeof(SetType))) SetType(set); // Stack: co, cl, st, function ptr + lua_pushcclosure(L, &detail::invoke_member_function, 1); // Stack: co, cl, st, setter + detail::add_property_setter(L, name, -3); // Stack: co, cl, st + } + + return *this; + } + + //========================================================================================= + /** + * @brief Add or replace a property member, by proxy. + * + * When a class is closed for modification and does not provide (or cannot provide) the function signatures necessary to implement + * get or set for a property, this will allow non-member functions act as proxies. + * + * Both the get and the set functions require a T const* and T* in the first argument respectively. + */ + template + Class& addProperty(const char* name, TG (*get)(T const*), void (*set)(T*, TS) = nullptr) + { + assert(name != nullptr); + assertStackState(); // Stack: const table (co), class table (cl), static table (st) + + lua_pushlightuserdata(L, reinterpret_cast(get)); // Stack: co, cl, st, function ptr + lua_pushcclosure(L, &detail::invoke_proxy_function, 1); // Stack: co, cl, st, getter + lua_pushvalue(L, -1); // Stack: co, cl, st,, getter, getter + detail::add_property_getter(L, name, -5); // Stack: co, cl, st, getter + detail::add_property_getter(L, name, -3); // Stack: co, cl, st + + if (set != nullptr) + { + lua_pushlightuserdata( L, reinterpret_cast(set)); // Stack: co, cl, st, function ptr + lua_pushcclosure(L, &detail::invoke_proxy_function, 1); // Stack: co, cl, st, setter + detail::add_property_setter(L, name, -3); // Stack: co, cl, st + } + + return *this; + } + + //========================================================================================= + /** + * @brief Add or replace a property member, by proxy C-function. + * + * When a class is closed for modification and does not provide (or cannot provide) the function signatures necessary to implement + * get or set for a property, this will allow non-member functions act as proxies. + * + * The object userdata ('this') value is at the index 1. + * The new value for set function is at the index 2. + */ + Class& addProperty(const char* name, lua_CFunction get, lua_CFunction set = nullptr) + { + assert(name != nullptr); + assertStackState(); // Stack: const table (co), class table (cl), static table (st) + + lua_pushcfunction(L, get); + lua_pushvalue(L, -1); // Stack: co, cl, st,, getter, getter + detail::add_property_getter(L, name, -5); // Stack: co, cl, st,, getter + detail::add_property_getter(L, name, -3); // Stack: co, cl, st, + + if (set != nullptr) + { + lua_pushcfunction(L, set); + detail::add_property_setter(L, name, -3); // Stack: co, cl, st, + } + + return *this; + } + + //========================================================================================= + /** + * @brief Add or replace a property member, by constructible by std::function. + */ + template >> + Class& addProperty(const char* name, Getter get) + { + using FirstArg = detail::function_argument_t<0, Getter>; + static_assert(std::is_same_v>, T>); + + assert(name != nullptr); + assertStackState(); // Stack: const table (co), class table (cl), static table (st) + + using GetType = decltype(get); + + lua_newuserdata_aligned(L, std::move(get)); // Stack: co, cl, st, function userdata (ud) + lua_newtable(L); // Stack: co, cl, st, ud, ud metatable (mt) + lua_pushcfunction(L, &lua_deleteuserdata_aligned); // Stack: co, cl, st, ud, mt, gc function + rawsetfield(L, -2, "__gc"); // Stack: co, cl, st, ud, mt + lua_setmetatable(L, -2); // Stack: co, cl, st, ud + lua_pushcclosure(L, &detail::invoke_proxy_functor, 1); // Stack: co, cl, st, getter + lua_pushvalue(L, -1); // Stack: co, cl, st, getter, getter + detail::add_property_getter(L, name, -4); // Stack: co, cl, st, getter + detail::add_property_getter(L, name, -4); // Stack: co, cl, st + + return *this; + } + + template && !std::is_pointer_v>> + Class& addProperty(const char* name, Getter get, Setter set) + { + addProperty(name, std::move(get)); + + using FirstArg = detail::function_argument_t<0, Setter>; + static_assert(std::is_same_v>, T>); + + assert(name != nullptr); + assertStackState(); // Stack: const table (co), class table (cl), static table (st) + + using SetType = decltype(set); + + lua_newuserdata_aligned(L, std::move(set)); // Stack: co, cl, st, function userdata (ud) + lua_newtable(L); // Stack: co, cl, st, ud, ud metatable (mt) + lua_pushcfunction(L, &lua_deleteuserdata_aligned); // Stack: co, cl, st, ud, mt, gc function + rawsetfield(L, -2, "__gc"); // Stack: co, cl, st, ud, mt + lua_setmetatable(L, -2); // Stack: co, cl, st, ud + lua_pushcclosure(L, &detail::invoke_proxy_functor, 1); // Stack: co, cl, st, setter + detail::add_property_setter(L, name, -3); // Stack: co, cl, st + + return *this; + } + + //========================================================================================= + /** + * @brief Add or replace a namespace function by convertible to std::function (capturing lambdas). + */ + template != 0>> + Class addFunction(const char* name, Function function) + { + using FnType = decltype(function); + + using FirstArg = detail::function_argument_t<0, Function>; + static_assert(std::is_same_v>, T>); + + assert(name != nullptr); + assertStackState(); // Stack: const table (co), class table (cl), static table (st) + + if (name == std::string_view("__gc")) + { + throw_or_assert("__gc metamethod registration is forbidden"); + return *this; + } + + lua_newuserdata_aligned(L, std::move(function)); // Stack: co, cl, st, function userdata (ud) + lua_newtable(L); // Stack: co, cl, st, ud, ud metatable (mt) + lua_pushcfunction(L, &lua_deleteuserdata_aligned); // Stack: co, cl, st, ud, mt, gc function + rawsetfield(L, -2, "__gc"); // Stack: co, cl, st, ud, mt + lua_setmetatable(L, -2); // Stack: co, cl, st, ud + + lua_pushcclosure(L, &detail::invoke_proxy_functor, 1); // Stack: co, cl, st, function + + if constexpr (! std::is_const_v>>) + { + rawsetfield(L, -3, name); // Stack: co, cl, st + } + else + { + lua_pushvalue(L, -1); // Stack: co, cl, st, function, function + rawsetfield(L, -4, name); // Stack: co, cl, st, function + rawsetfield(L, -4, name); // Stack: co, cl, st + } + + return *this; + } + + //========================================================================================= + /** + * @brief Add or replace a member function. + */ + template + Class& addFunction(const char* name, ReturnType (U::*mf)(Params...)) + { + static_assert(std::is_base_of_v); + + using MemFn = decltype(mf); + + assert(name != nullptr); + assertStackState(); // Stack: const table (co), class table (cl), static table (st) + + if (name == std::string_view("__gc")) + { + throw_or_assert("__gc metamethod registration is forbidden"); + return *this; + } + + new (lua_newuserdata(L, sizeof(MemFn))) MemFn(mf); + lua_pushcclosure(L, &detail::invoke_member_function, 1); + rawsetfield(L, -3, name); // class table + + return *this; + } + + template + Class& addFunction(const char* name, ReturnType (U::*mf)(Params...) const) + { + static_assert(std::is_base_of_v); + + using MemFn = decltype(mf); + + assert(name != nullptr); + assertStackState(); // Stack: const table (co), class table (cl), static table (st) + + if (name == std::string_view("__gc")) + { + throw_or_assert("__gc metamethod registration is forbidden"); + return *this; + } + + new (lua_newuserdata(L, sizeof(MemFn))) MemFn(mf); + lua_pushcclosure(L, &detail::invoke_const_member_function, 1); + lua_pushvalue(L, -1); + rawsetfield(L, -5, name); // const table + rawsetfield(L, -3, name); // class table + + return *this; + } + + //========================================================================================= + /** + * @brief Add or replace a proxy function. + */ + template + Class& addFunction(const char* name, ReturnType (*proxyFn)(T* object, Params...)) + { + using FnType = decltype(proxyFn); + + assert(name != nullptr); + assertStackState(); // Stack: const table (co), class table (cl), static table (st) + + if (name == std::string_view("__gc")) + { + throw_or_assert("__gc metamethod registration is forbidden"); + return *this; + } + + lua_pushlightuserdata(L, reinterpret_cast(proxyFn)); // Stack: co, cl, st, function ptr + lua_pushcclosure(L, &detail::invoke_proxy_function, 1); // Stack: co, cl, st, function + rawsetfield(L, -3, name); // Stack: co, cl, st + + return *this; + } + + template + Class& addFunction(const char* name, ReturnType (*proxyFn)(const T* object, Params...)) + { + using FnType = decltype(proxyFn); + + assert(name != nullptr); + assertStackState(); // Stack: const table (co), class table (cl), static table (st) + + if (name == std::string_view("__gc")) + { + throw_or_assert("__gc metamethod registration is forbidden"); + return *this; + } + + lua_pushlightuserdata(L, reinterpret_cast(proxyFn)); // Stack: co, cl, st, function ptr + lua_pushcclosure(L, &detail::invoke_proxy_function, 1); // Stack: co, cl, st, function + lua_pushvalue(L, -1); // Stack: co, cl, st, function, function + rawsetfield(L, -4, name); // Stack: co, cl, st, function + rawsetfield(L, -4, name); // Stack: co, cl, st + + return *this; + } + + //========================================================================================= + /** + * @brief Add or replace a member lua_CFunction. + */ + template + Class& addFunction(const char* name, int (U::*mfp)(lua_State*)) + { + static_assert(std::is_base_of_v); + + using F = decltype(mfp); + + assert(name != nullptr); + assertStackState(); // Stack: const table (co), class table (cl), static table (st) + + if (name == std::string_view("__gc")) + { + throw_or_assert("__gc metamethod registration is forbidden"); + return *this; + } + + new (lua_newuserdata(L, sizeof(mfp))) F(mfp); // Stack: co, cl, st, function ptr + lua_pushcclosure(L, &detail::invoke_member_cfunction, 1); // Stack: co, cl, st, function + rawsetfield(L, -3, name); // Stack: co, cl, st + + return *this; + } + + //========================================================================================= + /** + * @brief Add or replace a const member lua_CFunction. + */ + template + Class& addFunction(const char* name, int (U::*mfp)(lua_State*) const) + { + static_assert(std::is_base_of_v); + + using F = decltype(mfp); + + assert(name != nullptr); + assertStackState(); // Stack: const table (co), class table (cl), static table (st) + + if (name == std::string_view("__gc")) + { + throw_or_assert("__gc metamethod registration is forbidden"); + return *this; + } + + new (lua_newuserdata(L, sizeof(mfp))) F(mfp); + lua_pushcclosure(L, &detail::invoke_const_member_cfunction, 1); + lua_pushvalue(L, -1); // Stack: co, cl, st, function, function + rawsetfield(L, -4, name); // Stack: co, cl, st, function + rawsetfield(L, -4, name); // Stack: co, cl, st + + return *this; + } + + //========================================================================================= + /** + * @brief Add or replace a free lua_CFunction that works as a member. + * + * This object is at top of the stack, then all other arguments. + */ + Class& addFunction(const char* name, lua_CFunction fp) + { + assert(name != nullptr); + assertStackState(); // Stack: const table (co), class table (cl), static table (st) + + if (name == std::string_view("__gc")) + { + throw_or_assert("__gc metamethod registration is forbidden"); + return *this; + } + + lua_pushcfunction(L, fp); // Stack: co, cl, st, function + rawsetfield(L, -3, name); // Stack: co, cl, st + + return *this; + } + + //========================================================================================= + /** + * @brief Add or replace a primary Constructor. + * + * The primary Constructor is invoked when calling the class type table like a function. + * + * The template parameter should be a function pointer type that matches the desired Constructor (since you can't take the + * address of a Constructor and pass it as an argument). + */ + template + Class& addConstructor() + { + assertStackState(); // Stack: const table (co), class table (cl), static table (st) + + lua_pushcclosure(L, &ctorContainerProxy, C>, 0); + rawsetfield(L, -2, "__call"); + + return *this; + } + + template + Class& addConstructor() + { + assertStackState(); // Stack: const table (co), class table (cl), static table (st) + + lua_pushcclosure(L, &ctorPlacementProxy, T>, 0); + rawsetfield(L, -2, "__call"); + + return *this; + } + + //========================================================================================= + /** + * @brief Add or replace a factory. + * + * The primary Constructor is invoked when calling the class type table like a function. + * + * The template parameter should be a function pointer type that matches the desired Constructor (since you can't take the + * address of a Constructor and pass it as an argument). + */ + template + Class addConstructor(Function function) + { + assertStackState(); // Stack: const table (co), class table (cl), static table (st) + + auto factory = [function = std::move(function)](lua_State* L) -> T* + { + std::error_code ec; + detail::UserdataValue* value = detail::UserdataValue::place(L, ec); + if (! value) + luaL_error(L, ec.message().c_str()); + + using FnTraits = detail::function_traits; + using FnArgs = detail::remove_first_type_t; + + T* obj = detail::factory::call(value->getObject(), function, detail::make_arguments_list(L)); + + value->commit(); + + return obj; + }; + + using FactoryFnType = decltype(factory); + + lua_newuserdata_aligned(L, std::move(factory)); // Stack: co, cl, st, function userdata (ud) + lua_newtable(L); // Stack: co, cl, st, ud, ud metatable (mt) + lua_pushcfunction(L, &lua_deleteuserdata_aligned); // Stack: co, cl, st, ud, mt, gc function + rawsetfield(L, -2, "__gc"); // Stack: co, cl, st, ud, mt + lua_setmetatable(L, -2); // Stack: co, cl, st, ud + + lua_pushcclosure(L, &detail::invoke_proxy_functor, 1); // Stack: co, cl, st, function + rawsetfield(L, -2, "__call"); // Stack: co, cl, st + + return *this; + } + }; + +private: + struct FromStack {}; + + //============================================================================================= + /** + * @brief Open the global namespace for registrations. + * + * @param L A Lua state. + */ + explicit Namespace(lua_State* L) + : Registrar(L) + { + lua_getglobal(L, "_G"); + + ++m_stackSize; + } + + //============================================================================================= + /** + * @brief Open the a namespace for registrations from a table on top of the stack. + * + * @param L A Lua state. + */ + Namespace(lua_State* L, FromStack) + : Registrar(L, 1) + { + assert(lua_istable(L, -1)); + + { + lua_pushvalue(L, -1); // Stack: ns, mt + + // ns.__metatable = ns + lua_setmetatable(L, -2); // Stack: ns, mt + + // ns.__index = index_metamethod + lua_pushcfunction(L, &detail::index_metamethod); + rawsetfield(L, -2, "__index"); // Stack: ns + + lua_newtable(L); // Stack: ns, mt, propget table (pg) + lua_rawsetp(L, -2, detail::getPropgetKey()); // ns [propgetKey] = pg. Stack: ns + + lua_newtable(L); // Stack: ns, mt, propset table (ps) + lua_rawsetp(L, -2, detail::getPropsetKey()); // ns [propsetKey] = ps. Stack: ns + } + + ++m_stackSize; + } + + //============================================================================================= + /** + * @brief Open a namespace for registrations. + * + * The namespace is created if it doesn't already exist. + * + * @param name The namespace name. + * @param parent The parent namespace object. + * + * @pre The parent namespace is at the top of the Lua stack. + */ + Namespace(const char* name, Namespace& parent) + : Registrar(parent) + { + assert(name != nullptr); + assert(lua_istable(L, -1)); // Stack: parent namespace (pns) + + rawgetfield(L, -1, name); // Stack: pns, namespace (ns) | nil + + if (lua_isnil(L, -1)) // Stack: pns, nil + { + lua_pop(L, 1); // Stack: pns + + lua_newtable(L); // Stack: pns, ns + lua_pushvalue(L, -1); // Stack: pns, ns, mt + + // ns.__metatable = ns + lua_setmetatable(L, -2); // Stack: pns, ns + + // ns.__index = index_metamethod + lua_pushcfunction(L, &detail::index_metamethod); + rawsetfield(L, -2, "__index"); // Stack: pns, ns + + // ns.__newindex = newindex_static_metamethod + lua_pushcfunction(L, &detail::newindex_static_metamethod); + rawsetfield(L, -2, "__newindex"); // Stack: pns, ns + + lua_newtable(L); // Stack: pns, ns, propget table (pg) + lua_rawsetp(L, -2, detail::getPropgetKey()); // ns [propgetKey] = pg. Stack: pns, ns + + lua_newtable(L); // Stack: pns, ns, propset table (ps) + lua_rawsetp(L, -2, detail::getPropsetKey()); // ns [propsetKey] = ps. Stack: pns, ns + + // pns [name] = ns + lua_pushvalue(L, -1); // Stack: pns, ns, ns + rawsetfield(L, -3, name); // Stack: pns, ns + } + + ++m_stackSize; + } + + //============================================================================================= + /** + * @brief Close the class and continue the namespace registrations. + * + * @param child A child class registration object. + */ + explicit Namespace(ClassBase& child) + : Registrar(child) + { + } + + using Registrar::operator=; + +public: + //============================================================================================= + /** + * @brief Retrieve the global namespace. + * + * It is recommended to put your namespace inside the global namespace, and then add your classes and functions to it, rather than + * adding many classes and functions directly to the global namespace. + * + * @param L A Lua state. + * + * @returns A namespace registration object. + */ + static Namespace getGlobalNamespace(lua_State* L) + { + return Namespace(L); + } + + /** + * @brief Retrieve the namespace on top of the stack. + * + * You should have a table on top of the stack before calling this function. It will then use the table there as destination for registrations. + * + * @param L A Lua state. + * + * @returns A namespace registration object. + */ + static Namespace getNamespaceFromStack(lua_State* L) + { + return Namespace(L, FromStack{}); + } + + //============================================================================================= + /** + * @brief Open a new or existing namespace for registrations. + * + * @param name The namespace name. + * + * @returns A namespace registration object. + */ + Namespace beginNamespace(const char* name) + { + assertIsActive(); + return Namespace(name, *this); + } + + //============================================================================================= + /** + * @brief Continue namespace registration in the parent. + * + * Do not use this on the global namespace. + * + * @returns A parent namespace registration object. + */ + Namespace endNamespace() + { + if (m_stackSize == 1) + { + throw_or_assert("endNamespace() called on global namespace"); + + return Namespace(*this); + } + + assert(m_stackSize > 1); + --m_stackSize; + lua_pop(L, 1); + return Namespace(*this); + } + + //============================================================================================= + /** + * @brief Add or replace a property. + * + * @param name The property name. + * @param value A value pointer. + * @param isWritable True for a read-write, false for read-only property. + * + * @returns This namespace registration object. + */ + template + Namespace& addProperty(const char* name, T* value, bool isWritable = true) + { + if (m_stackSize == 1) + { + throw_or_assert("addProperty() called on global namespace"); + + return *this; + } + + assert(name != nullptr); + assert(lua_istable(L, -1)); // Stack: namespace table (ns) + + lua_pushlightuserdata(L, value); // Stack: ns, pointer + lua_pushcclosure(L, &detail::property_getter::call, 1); // Stack: ns, getter + detail::add_property_getter(L, name, -2); // Stack: ns + + if (isWritable) + { + lua_pushlightuserdata(L, value); // Stack: ns, pointer + lua_pushcclosure(L, &detail::property_setter::call, 1); // Stack: ns, setter + } + else + { + lua_pushstring(L, name); // Stack: ns, ps, name + lua_pushcclosure(L, &detail::read_only_error, 1); // Stack: ns, function + } + + detail::add_property_setter(L, name, -2); // Stack: ns + + return *this; + } + + //============================================================================================= + /** + * @brief Add or replace a property. + * + * If the set function is omitted or null, the property is read-only. + * + * @param name The property name. + * @param get A pointer to a property getter function. + * @param set A pointer to a property setter function, optional. + * + * @returns This namespace registration object. + */ + template + Namespace& addProperty(const char* name, TG (*get)(), void (*set)(TS) = nullptr) + { + if (m_stackSize == 1) + { + throw_or_assert("addProperty() called on global namespace"); + + return *this; + } + + assert(name != nullptr); + assert(lua_istable(L, -1)); // Stack: namespace table (ns) + + lua_pushlightuserdata(L, reinterpret_cast(get)); // Stack: ns, function ptr + lua_pushcclosure(L, &detail::invoke_proxy_function, 1); // Stack: ns, getter + detail::add_property_getter(L, name, -2); + + if (set != nullptr) + { + lua_pushlightuserdata(L, reinterpret_cast(set)); // Stack: ns, function ptr + lua_pushcclosure(L, &detail::invoke_proxy_function, 1); + } + else + { + lua_pushstring(L, name); + lua_pushcclosure(L, &detail::read_only_error, 1); + } + + detail::add_property_setter(L, name, -2); + + return *this; + } + + //============================================================================================= + /** + * @brief Add or replace a readonly property. + * + * @param name The property name. + * @param get A pointer to a property getter function. + * + * @returns This namespace registration object. + */ + template + Namespace& addProperty(const char* name, Getter get) + { + using GetType = decltype(get); + + assert(name != nullptr); + assert(lua_istable(L, -1)); // Stack: namespace table (ns) + + lua_newuserdata_aligned(L, std::move(get)); // Stack: ns, function userdata (ud) + lua_newtable(L); // Stack: ns, ud, ud metatable (mt) + lua_pushcfunction(L, &lua_deleteuserdata_aligned); // Stack: ns, ud, mt, gc function + rawsetfield(L, -2, "__gc"); // Stack: ns, ud, mt + lua_setmetatable(L, -2); // Stack: ns, ud + lua_pushcclosure(L, &detail::invoke_proxy_functor, 1); // Stack: ns, ud, getter + detail::add_property_getter(L, name, -2); // Stack: ns, ud, getter + + lua_pushstring(L, name); // Stack: ns, name + lua_pushcclosure(L, &detail::read_only_error, 1); // Stack: ns, name, function + detail::add_property_setter(L, name, -2); // Stack: ns + + return *this; + } + + /** + * @brief Add or replace a mutable property. + * + * @param name The property name. + * @param get A pointer to a property getter function. + * @param set A pointer to a property setter function. + * + * @returns This namespace registration object. + */ + template + Namespace& addProperty(const char* name, Getter get, Setter set) + { + assert(name != nullptr); + assert(lua_istable(L, -1)); // Stack: namespace table (ns) + + addProperty(name, std::move(get)); + + using SetType = decltype(set); + + lua_newuserdata_aligned(L, std::move(set)); // Stack: ns, function userdata (ud) + lua_newtable(L); // Stack: ns, ud, ud metatable (mt) + lua_pushcfunction(L, &lua_deleteuserdata_aligned); // Stack: ns, ud, mt, gc function + rawsetfield(L, -2, "__gc"); // Stack: ns, ud, mt + lua_setmetatable(L, -2); // Stack: ns, ud + lua_pushcclosure(L, &detail::invoke_proxy_functor, 1); // Stack: ns, ud, getter + detail::add_property_setter(L, name, -2); // Stack: ns, ud, getter + + return *this; + } + + //============================================================================================= + /** + * @brief Add or replace a property. + * + * If the set function is omitted or null, the property is read-only. + * + * @param name The property name. + * @param get A pointer to a property getter function. + * @param set A pointer to a property setter function, optional. + * + * @returns This namespace registration object. + */ + Namespace& addProperty(const char* name, lua_CFunction get, lua_CFunction set = nullptr) + { + assert(name != nullptr); + assert(lua_istable(L, -1)); // Stack: namespace table (ns) + + lua_pushcfunction(L, get); // Stack: ns, getter + detail::add_property_getter(L, name, -2); // Stack: ns + + if (set != nullptr) + { + lua_pushcfunction(L, set); // Stack: ns, setter + detail::add_property_setter(L, name, -2); // Stack: ns + } + else + { + lua_pushstring(L, name); // Stack: ns, name + lua_pushcclosure(L, &detail::read_only_error, 1); // Stack: ns, name, function + detail::add_property_setter(L, name, -2); // Stack: ns + } + + return *this; + } + + //============================================================================================= + /** + * @brief Add or replace a namespace function by convertible to std::function. + */ + template + Namespace& addFunction(const char* name, Function function) + { + using FnType = decltype(function); + + assert(name != nullptr); + assert(lua_istable(L, -1)); // Stack: namespace table (ns) + + lua_newuserdata_aligned(L, std::move(function)); // Stack: ns, function userdata (ud) + lua_newtable(L); // Stack: ns, ud, ud metatable (mt) + lua_pushcfunction(L, &lua_deleteuserdata_aligned); // Stack: ns, ud, mt, gc function + rawsetfield(L, -2, "__gc"); // Stack: ns, ud, mt + lua_setmetatable(L, -2); // Stack: ns, ud + lua_pushcclosure(L, &detail::invoke_proxy_functor, 1); // Stack: ns, function + rawsetfield(L, -2, name); // Stack: ns + + return *this; + } + + //============================================================================================= + /** + * @brief Add or replace a free function. + */ + template + Namespace& addFunction(const char* name, ReturnType (*fp)(Params...)) + { + using FnType = decltype(fp); + + assert(name != nullptr); + assert(lua_istable(L, -1)); // Stack: namespace table (ns) + + lua_pushlightuserdata(L, reinterpret_cast(fp)); // Stack: ns, function ptr + lua_pushcclosure(L, &detail::invoke_proxy_function, 1); // Stack: ns, function + rawsetfield(L, -2, name); // Stack: ns + + return *this; + } + + //============================================================================================= + /** + * @brief Add or replace a lua_CFunction. + * + * @param name The function name. + * @param fp A C-function pointer. + * + * @returns This namespace registration object. + */ + Namespace& addFunction(const char* name, lua_CFunction fp) + { + assert(name != nullptr); + assert(lua_istable(L, -1)); // Stack: namespace table (ns) + + lua_pushcfunction(L, fp); // Stack: ns, function + rawsetfield(L, -2, name); // Stack: ns + + return *this; + } + + //============================================================================================= + /** + * @brief Open a new or existing class for registrations. + * + * @param name The class name. + * + * @returns A class registration object. + */ + template + Class beginClass(const char* name) + { + assertIsActive(); + return Class(name, *this); + } + + //============================================================================================= + /** + * @brief Derive a new class for registrations. + * + * Call deriveClass() only once. To continue registrations for the class later, use beginClass(). + * + * @param name The class name. + * + * @returns A class registration object. + */ + template + Class deriveClass(const char* name) + { + assertIsActive(); + return Class(name, *this, detail::getStaticRegistryKey()); + } +}; + +//================================================================================================= +/** + * @brief Retrieve the global namespace. + * + * It is recommended to put your namespace inside the global namespace, and then add your classes and functions to it, rather than adding + * many classes and functions directly to the global namespace. + * + * @param L A Lua state. + * + * @returns A namespace registration object. + */ +inline Namespace getGlobalNamespace(lua_State* L) +{ + return Namespace::getGlobalNamespace(L); +} + +//================================================================================================= +/** + * @brief Retrieve the namespace on top of the stack. + * + * You should have a table on top of the stack before calling this function. It will then use the table there as destination for registrations. + * + * @param L A Lua state. + * + * @returns A namespace registration object. + */ +inline Namespace getNamespaceFromStack(lua_State* L) +{ + return Namespace::getNamespaceFromStack(L); +} + +} // namespace luabridge + +// End File: Source/LuaBridge/detail/Namespace.h + +// Begin File: Source/LuaBridge/LuaBridge.h + +// https://github.com/kunitoki/LuaBridge3 +// Copyright 2020, Lucio Asnaghi +// Copyright 2020, Dmitry Tarakanov +// Copyright 2012, Vinnie Falco +// Copyright 2007, Nathan Reed +// SPDX-License-Identifier: MIT + +// All #include dependencies are listed here +// instead of in the individual header files. + +#define LUABRIDGE_MAJOR_VERSION 3 +#define LUABRIDGE_MINOR_VERSION 1 +#define LUABRIDGE_VERSION 301 + +#ifndef LUA_VERSION_NUM +#error "Lua headers must be included prior to LuaBridge ones" +#endif + + +// End File: Source/LuaBridge/LuaBridge.h + +// Begin File: Source/LuaBridge/List.h + +// https://github.com/kunitoki/LuaBridge3 +// Copyright 2020, Lucio Asnaghi +// Copyright 2020, Dmitry Tarakanov +// SPDX-License-Identifier: MIT + +namespace luabridge { + +//================================================================================================= +/** + * @brief Stack specialization for `std::array`. + */ +template +struct Stack> +{ + using Type = std::list; + + static bool push(lua_State* L, const Type& list, std::error_code& ec) + { + const int initialStackSize = lua_gettop(L); + + lua_createtable(L, static_cast(list.size()), 0); + + auto it = list.cbegin(); + for (lua_Integer tableIndex = 1; it != list.cend(); ++tableIndex, ++it) + { + lua_pushinteger(L, tableIndex); + + std::error_code errorCode; + if (! Stack::push(L, *it, errorCode)) + { + ec = errorCode; + lua_pop(L, lua_gettop(L) - initialStackSize); + return false; + } + + lua_settable(L, -3); + } + + return true; + } + + static Type get(lua_State* L, int index) + { + if (!lua_istable(L, index)) + luaL_error(L, "#%d argument must be a table", index); + + Type list; + + int absIndex = lua_absindex(L, index); + lua_pushnil(L); + + while (lua_next(L, absIndex) != 0) + { + list.emplace_back(Stack::get(L, -1)); + lua_pop(L, 1); + } + + return list; + } + + static bool isInstance(lua_State* L, int index) + { + return lua_istable(L, index); + } +}; + +} // namespace luabridge + +// End File: Source/LuaBridge/List.h + +// Begin File: Source/LuaBridge/Array.h + +// https://github.com/kunitoki/LuaBridge3 +// Copyright 2020, Lucio Asnaghi +// Copyright 2020, Dmitry Tarakanov +// SPDX-License-Identifier: MIT + +namespace luabridge { + +//================================================================================================= +/** + * @brief Stack specialization for `std::array`. + */ +template +struct Stack> +{ + using Type = std::array; + + static bool push(lua_State* L, const Type& array, std::error_code& ec) + { + const int initialStackSize = lua_gettop(L); + + lua_createtable(L, static_cast(Size), 0); + + for (std::size_t i = 0; i < Size; ++i) + { + lua_pushinteger(L, static_cast(i + 1)); + + std::error_code errorCode; + bool result = Stack::push(L, array[i], errorCode); + if (!result) + { + ec = errorCode; + lua_pop(L, lua_gettop(L) - initialStackSize); + return false; + } + + lua_settable(L, -3); + } + + return true; + } + + static Type get(lua_State* L, int index) + { + if (!lua_istable(L, index)) + luaL_error(L, "#%d argment must be a table", index); + + if (get_length(L, index) != Size) + luaL_error(L, "table size should be %d but is %d", Size, get_length(L, index)); + + Type array; + + int absIndex = lua_absindex(L, index); + lua_pushnil(L); + + int arrayIndex = 0; + while (lua_next(L, absIndex) != 0) + { + array[arrayIndex++] = Stack::get(L, -1); + lua_pop(L, 1); + } + + return array; + } +}; + +} // namespace luabridge + +// End File: Source/LuaBridge/Array.h + +// Begin File: Source/LuaBridge/Vector.h + +// https://github.com/kunitoki/LuaBridge3 +// Copyright 2020, Lucio Asnaghi +// Copyright 2018, Dmitry Tarakanov +// SPDX-License-Identifier: MIT + +namespace luabridge { + +//================================================================================================= +/** + * @brief Stack specialization for `std::vector`. + */ +template +struct Stack> +{ + using Type = std::vector; + + static bool push(lua_State* L, const Type& vector, std::error_code& ec) + { + const int initialStackSize = lua_gettop(L); + + lua_createtable(L, static_cast(vector.size()), 0); + + for (std::size_t i = 0; i < vector.size(); ++i) + { + lua_pushinteger(L, static_cast(i + 1)); + + std::error_code errorCode; + if (! Stack::push(L, vector[i], errorCode)) + { + ec = errorCode; + lua_pop(L, lua_gettop(L) - initialStackSize); + return false; + } + + lua_settable(L, -3); + } + + return true; + } + + static Type get(lua_State* L, int index) + { + if (!lua_istable(L, index)) + luaL_error(L, "#%d argument must be a table", index); + + Type vector; + vector.reserve(static_cast(get_length(L, index))); + + int absIndex = lua_absindex(L, index); + lua_pushnil(L); + + while (lua_next(L, absIndex) != 0) + { + vector.emplace_back(Stack::get(L, -1)); + lua_pop(L, 1); + } + + return vector; + } + + static bool isInstance(lua_State* L, int index) + { + return lua_istable(L, index); + } +}; + +} // namespace luabridge + +// End File: Source/LuaBridge/Vector.h + +// Begin File: Source/LuaBridge/Set.h + +// https://github.com/kunitoki/LuaBridge3 +// Copyright 2020, Lucio Asnaghi +// SPDX-License-Identifier: MIT + +namespace luabridge { + +//================================================================================================= +/** + * @brief Stack specialization for `std::set`. + */ +template +struct Stack> +{ + using Type = std::set; + + static bool push(lua_State* L, const Type& set, std::error_code& ec) + { + const int initialStackSize = lua_gettop(L); + + lua_createtable(L, 0, static_cast(set.size())); + + for (auto it = set.begin(); it != set.end(); ++it) + { + std::error_code errorCodeKey; + if (! Stack::push(L, it->first, errorCodeKey)) + { + ec = errorCodeKey; + lua_pop(L, lua_gettop(L) - initialStackSize); + return false; + } + + std::error_code errorCodeValue; + if (! Stack::push(L, it->second, errorCodeValue)) + { + ec = errorCodeValue; + lua_pop(L, lua_gettop(L) - initialStackSize); + return false; + } + + lua_settable(L, -3); + } + + return true; + } + + static Type get(lua_State* L, int index) + { + if (!lua_istable(L, index)) + luaL_error(L, "#%d argument must be a table", index); + + Type set; + + int absIndex = lua_absindex(L, index); + lua_pushnil(L); + + while (lua_next(L, absIndex) != 0) + { + set.emplace(Stack::get(L, -2), Stack::get(L, -1)); + lua_pop(L, 1); + } + + return set; + } + + static bool isInstance(lua_State* L, int index) + { + return lua_istable(L, index); + } +}; + +} // namespace luabridge + +// End File: Source/LuaBridge/Set.h + +// Begin File: Source/LuaBridge/RefCountedObject.h + +// https://github.com/kunitoki/LuaBridge3 +// Copyright 2020, Lucio Asnaghi +// Copyright 2012, Vinnie Falco +// Copyright 2004-11 by Raw Material Software Ltd. +// SPDX-License-Identifier: MIT + +//============================================================================== +/* + This is a derivative work used by permission from part of + JUCE, available at http://www.rawaterialsoftware.com + + License: The MIT License (http://www.opensource.org/licenses/mit-license.php) + + 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. + + This file incorporates work covered by the following copyright and + permission notice: + + This file is part of the JUCE library - "Jules' Utility Class Extensions" + Copyright 2004-11 by Raw Material Software Ltd. +*/ +//============================================================================== + +namespace luabridge { + +//============================================================================== +/** + Adds reference-counting to an object. + + To add reference-counting to a class, derive it from this class, and + use the RefCountedObjectPtr class to point to it. + + e.g. @code + class MyClass : public RefCountedObjectType + { + void foo(); + + // This is a neat way of declaring a typedef for a pointer class, + // rather than typing out the full templated name each time.. + typedef RefCountedObjectPtr Ptr; + }; + + MyClass::Ptr p = new MyClass(); + MyClass::Ptr p2 = p; + p = 0; + p2->foo(); + @endcode + + Once a new RefCountedObjectType has been assigned to a pointer, be + careful not to delete the object manually. +*/ +template +class RefCountedObjectType +{ +public: + //============================================================================== + /** Increments the object's reference count. + + This is done automatically by the smart pointer, but is public just + in case it's needed for nefarious purposes. + */ + inline void incReferenceCount() const { ++refCount; } + + /** Decreases the object's reference count. + + If the count gets to zero, the object will be deleted. + */ + inline void decReferenceCount() const + { + assert(getReferenceCount() > 0); + + if (--refCount == 0) + delete this; + } + + /** Returns the object's current reference count. + * @returns The reference count. + */ + inline int getReferenceCount() const { return static_cast(refCount); } + +protected: + //============================================================================== + /** Creates the reference-counted object (with an initial ref count of zero). */ + RefCountedObjectType() : refCount() {} + + /** Destructor. */ + virtual ~RefCountedObjectType() + { + // it's dangerous to delete an object that's still referenced by something else! + assert(getReferenceCount() == 0); + } + +private: + //============================================================================== + CounterType mutable refCount; +}; + +//============================================================================== + +/** Non thread-safe reference counted object. + + This creates a RefCountedObjectType that uses a non-atomic integer + as the counter. +*/ +typedef RefCountedObjectType RefCountedObject; + +//============================================================================== +/** + A smart-pointer class which points to a reference-counted object. + + The template parameter specifies the class of the object you want to point + to - the easiest way to make a class reference-countable is to simply make + it inherit from RefCountedObjectType, but if you need to, you could roll + your own reference-countable class by implementing a pair of methods called + incReferenceCount() and decReferenceCount(). + + When using this class, you'll probably want to create a typedef to + abbreviate the full templated name - e.g. + + @code + + typedef RefCountedObjectPtr MyClassPtr; + + @endcode +*/ +template +class RefCountedObjectPtr +{ +public: + /** The class being referenced by this pointer. */ + typedef ReferenceCountedObjectClass ReferencedType; + + //============================================================================== + /** Creates a pointer to a null object. */ + inline RefCountedObjectPtr() : referencedObject(0) {} + + /** Creates a pointer to an object. + This will increment the object's reference-count if it is non-null. + + @param refCountedObject A reference counted object to own. + */ + inline RefCountedObjectPtr(ReferenceCountedObjectClass* const refCountedObject) + : referencedObject(refCountedObject) + { + if (refCountedObject != 0) + refCountedObject->incReferenceCount(); + } + + /** Copies another pointer. + This will increment the object's reference-count (if it is non-null). + + @param other Another pointer. + */ + inline RefCountedObjectPtr(const RefCountedObjectPtr& other) + : referencedObject(other.referencedObject) + { + if (referencedObject != 0) + referencedObject->incReferenceCount(); + } + + /** + Takes-over the object from another pointer. + + @param other Another pointer. + */ + inline RefCountedObjectPtr(RefCountedObjectPtr&& other) + : referencedObject(other.referencedObject) + { + other.referencedObject = 0; + } + + /** Copies another pointer. + This will increment the object's reference-count (if it is non-null). + + @param other Another pointer. + */ + template + inline RefCountedObjectPtr(const RefCountedObjectPtr& other) + : referencedObject(static_cast(other.getObject())) + { + if (referencedObject != 0) + referencedObject->incReferenceCount(); + } + + /** Changes this pointer to point at a different object. + + The reference count of the old object is decremented, and it might be + deleted if it hits zero. The new object's count is incremented. + + @param other A pointer to assign from. + @returns This pointer. + */ + RefCountedObjectPtr& operator=(const RefCountedObjectPtr& other) + { + return operator=(other.referencedObject); + } + + /** Changes this pointer to point at a different object. + The reference count of the old object is decremented, and it might be + deleted if it hits zero. The new object's count is incremented. + + @param other A pointer to assign from. + @returns This pointer. + */ + template + RefCountedObjectPtr& operator=(const RefCountedObjectPtr& other) + { + return operator=(static_cast(other.getObject())); + } + + /** + Takes-over the object from another pointer. + + @param other A pointer to assign from. + @returns This pointer. + */ + RefCountedObjectPtr& operator=(RefCountedObjectPtr&& other) + { + std::swap(referencedObject, other.referencedObject); + return *this; + } + + /** Changes this pointer to point at a different object. + The reference count of the old object is decremented, and it might be + deleted if it hits zero. The new object's count is incremented. + + @param newObject A reference counted object to own. + @returns This pointer. + */ + RefCountedObjectPtr& operator=(ReferenceCountedObjectClass* const newObject) + { + if (referencedObject != newObject) + { + if (newObject != 0) + newObject->incReferenceCount(); + + ReferenceCountedObjectClass* const oldObject = referencedObject; + referencedObject = newObject; + + if (oldObject != 0) + oldObject->decReferenceCount(); + } + + return *this; + } + + /** Destructor. + This will decrement the object's reference-count, and may delete it if it + gets to zero. + */ + ~RefCountedObjectPtr() + { + if (referencedObject != 0) + referencedObject->decReferenceCount(); + } + + /** Returns the object that this pointer references. + The returned pointer may be null. + + @returns The pointee. + */ + operator ReferenceCountedObjectClass*() const { return referencedObject; } + + /** Returns the object that this pointer references. + The returned pointer may be null. + + @returns The pointee. + */ + ReferenceCountedObjectClass* operator->() const { return referencedObject; } + + /** Returns the object that this pointer references. + The returned pointer may be null. + + @returns The pointee. + */ + ReferenceCountedObjectClass* getObject() const { return referencedObject; } + +private: + //============================================================================== + ReferenceCountedObjectClass* referencedObject; +}; + +/** Compares two ReferenceCountedObjectPointers. */ +template +bool operator==(const RefCountedObjectPtr& object1, + ReferenceCountedObjectClass* const object2) +{ + return object1.getObject() == object2; +} + +/** Compares two ReferenceCountedObjectPointers. */ +template +bool operator==(const RefCountedObjectPtr& object1, + const RefCountedObjectPtr& object2) +{ + return object1.getObject() == object2.getObject(); +} + +/** Compares two ReferenceCountedObjectPointers. */ +template +bool operator==(ReferenceCountedObjectClass* object1, + RefCountedObjectPtr& object2) +{ + return object1 == object2.getObject(); +} + +/** Compares two ReferenceCountedObjectPointers. */ +template +bool operator!=(const RefCountedObjectPtr& object1, + const ReferenceCountedObjectClass* object2) +{ + return object1.getObject() != object2; +} + +/** Compares two ReferenceCountedObjectPointers. */ +template +bool operator!=(const RefCountedObjectPtr& object1, + RefCountedObjectPtr& object2) +{ + return object1.getObject() != object2.getObject(); +} + +/** Compares two ReferenceCountedObjectPointers. */ +template +bool operator!=(ReferenceCountedObjectClass* object1, + RefCountedObjectPtr& object2) +{ + return object1 != object2.getObject(); +} + +//============================================================================== + +template +struct ContainerTraits> +{ + using Type = T; + + static RefCountedObjectPtr construct(T* c) { return c; } + + static T* get(RefCountedObjectPtr const& c) { return c.getObject(); } +}; + +//============================================================================== + +} // namespace luabridge + +// End File: Source/LuaBridge/RefCountedObject.h + +// Begin File: Source/LuaBridge/detail/Dump.h + +// https://github.com/kunitoki/LuaBridge3 +// Copyright 2020, Lucio Asnaghi +// Copyright 2019, Dmitry Tarakanov +// Copyright 2012, Vinnie Falco +// Copyright 2007, Nathan Reed +// SPDX-License-Identifier: MIT + +namespace luabridge { +namespace debug { + +inline void putIndent(std::ostream& stream, unsigned level) +{ + for (unsigned i = 0; i < level; ++i) + { + stream << " "; + } +} + +inline void dumpTable(lua_State* L, int index, std::ostream& stream, unsigned level); + +inline void dumpValue(lua_State* L, int index, std::ostream& stream, unsigned level = 0) +{ + const int type = lua_type(L, index); + switch (type) + { + case LUA_TNIL: + stream << "nil"; + break; + + case LUA_TBOOLEAN: + stream << (lua_toboolean(L, index) ? "true" : "false"); + break; + + case LUA_TNUMBER: + stream << lua_tonumber(L, index); + break; + + case LUA_TSTRING: + stream << '"' << lua_tostring(L, index) << '"'; + break; + + case LUA_TFUNCTION: + if (lua_iscfunction(L, index)) + { + stream << "cfunction@" << lua_topointer(L, index); + } + else + { + stream << "function@" << lua_topointer(L, index); + } + break; + + case LUA_TTHREAD: + stream << "thread@" << lua_tothread(L, index); + break; + + case LUA_TLIGHTUSERDATA: + stream << "lightuserdata@" << lua_touserdata(L, index); + break; + + case LUA_TTABLE: + dumpTable(L, index, stream, level); + break; + + case LUA_TUSERDATA: + stream << "userdata@" << lua_touserdata(L, index); + break; + + default: + stream << lua_typename(L, type); + ; + break; + } +} + +inline void dumpTable(lua_State* L, int index, std::ostream& stream, unsigned level) +{ + stream << "table@" << lua_topointer(L, index); + + if (level > 0) + { + return; + } + + index = lua_absindex(L, index); + stream << " {"; + lua_pushnil(L); // Initial key + while (lua_next(L, index)) + { + stream << "\n"; + putIndent(stream, level + 1); + dumpValue(L, -2, stream, level + 1); // Key + stream << ": "; + dumpValue(L, -1, stream, level + 1); // Value + lua_pop(L, 1); // Value + } + putIndent(stream, level); + stream << "\n}"; +} + +inline void dumpState(lua_State* L, std::ostream& stream = std::cerr) +{ + int top = lua_gettop(L); + for (int i = 1; i <= top; ++i) + { + stream << "stack #" << i << ": "; + dumpValue(L, i, stream, 0); + stream << "\n"; + } +} + +} // namespace debug +} // namespace luabridge + +// End File: Source/LuaBridge/detail/Dump.h + +// clang-format on \ No newline at end of file diff --git a/sources/Component/IAControllable/IAControllableComponent.cpp b/sources/Component/IAControllable/IAControllableComponent.cpp index 11d40b2c..16c3b745 100644 --- a/sources/Component/IAControllable/IAControllableComponent.cpp +++ b/sources/Component/IAControllable/IAControllableComponent.cpp @@ -5,6 +5,7 @@ ** IAControllableComponent */ +#include "Map/MapInfo.hpp" #include "Component/IAControllable/IAControllableComponent.hpp" namespace BBM @@ -12,10 +13,17 @@ namespace BBM IAControllableComponent::IAControllableComponent(WAL::Entity &entity, std::string scriptPath) : Component(entity), _scriptPath(scriptPath), state(luaL_newstate()) { + luaL_openlibs(state); + luabridge::getGlobalNamespace(state) + .beginNamespace ("luaBBM") + .beginClass("MapInfo") + .addProperty("x", &MapInfo::x) + .addProperty("y", &MapInfo::y) + .addProperty("z", &MapInfo::z) + .addProperty("type", &MapInfo::type) + .endClass() + .endNamespace(); luaL_dofile(state, scriptPath.c_str()); - lua_getglobal(state, "Update"); - if (!lua_isfunction(state, -1)) - std::cout << "No update function in the script" << std::endl; } WAL::Component *IAControllableComponent::clone(WAL::Entity &entity) const diff --git a/sources/Component/IAControllable/IAControllableComponent.hpp b/sources/Component/IAControllable/IAControllableComponent.hpp index ab8b572e..9ff60a90 100644 --- a/sources/Component/IAControllable/IAControllableComponent.hpp +++ b/sources/Component/IAControllable/IAControllableComponent.hpp @@ -12,6 +12,7 @@ #include "Entity/Entity.hpp" #include "Models/Vector3.hpp" #include "lua.hpp" +#include "LuaBridge.hpp" namespace BBM { diff --git a/sources/Map/Map.hpp b/sources/Map/Map.hpp index f6b082a8..d0191ddc 100644 --- a/sources/Map/Map.hpp +++ b/sources/Map/Map.hpp @@ -27,7 +27,7 @@ namespace BBM class MapGenerator { - private: + public: //! @brief Enum of the block available. enum BlockType { NOTHING, @@ -40,10 +40,26 @@ namespace BBM SPAWNER, UNBREAKABLE }; - using MapElem = std::function scene)>; using MapBlock = std::map, BlockType>; + static void wallCollide(WAL::Entity &entity, const WAL::Entity &wall); + + + //! @param width Width of the map + //! @param height Height of the map + //! @brief Generate map of block to be loaded + static MapBlock createMap(int width, int height); + + //! @param width Width of the map + //! @param height Height of the map + //! @param map Map to load with block declared inside + //! @param scene Scene where the map is instanced + //! @brief Generate the map + static void loadMap(int width, int height, MapBlock map, std::shared_ptr scene); + + private: + //! @brief Generate random block type static BlockType getRandomBlockType(); @@ -155,21 +171,5 @@ namespace BBM static const std::string secondFloorHolePath; - public: - static void wallCollide(WAL::Entity &entity, const WAL::Entity &wall); - - - //! @param width Width of the map - //! @param height Height of the map - //! @brief Generate map of block to be loaded - static MapBlock createMap(int width, int height); - - //! @param width Width of the map - //! @param height Height of the map - //! @param map Map to load with block declared inside - //! @param scene Scene where the map is instanced - //! @brief Generate the map - static void loadMap(int width, int height, MapBlock map, std::shared_ptr scene); - }; } // namespace BBM \ No newline at end of file diff --git a/sources/Map/MapInfo.cpp b/sources/Map/MapInfo.cpp new file mode 100644 index 00000000..45723f3a --- /dev/null +++ b/sources/Map/MapInfo.cpp @@ -0,0 +1,21 @@ +// +// Created by Louis Auzuret 09/06/21 +// + +#include "MapInfo.hpp" + +namespace BBM +{ + MapInfo::MapInfo(Vector3f pos, MapGenerator::BlockType type) + : x(pos.x), y(pos.y), z(pos.z), type(type) + { } + + MapInfo &MapInfo::operator=(MapInfo &other) + { + this->x = other.x; + this->y = other.y; + this->z = other.z; + this->type = other.type; + return *this; + } +} \ No newline at end of file diff --git a/sources/Map/MapInfo.hpp b/sources/Map/MapInfo.hpp new file mode 100644 index 00000000..472b1542 --- /dev/null +++ b/sources/Map/MapInfo.hpp @@ -0,0 +1,37 @@ +// +// Created by Louis Auzuret on 09/06/21 +// + +#include "Map.hpp" + +namespace BBM +{ + class MapInfo + { + private: + public: + //! @brief Position on the x axis + float x; + + //! @brief Position on the y axis + float y; + + //! @brief Position on the z axis + float z; + + //! @brief Type of the block + MapGenerator::BlockType type; + + //! @brief Constructor + MapInfo(Vector3f pos, MapGenerator::BlockType type); + + //! @brief Default destructor + ~MapInfo() = default; + + //! @brief Default copy constructor + MapInfo(MapInfo &) = default; + + //! @brief Assignment operator + MapInfo &operator=(MapInfo &); + }; +} \ No newline at end of file diff --git a/sources/System/IAControllable/IAControllableSystem.cpp b/sources/System/IAControllable/IAControllableSystem.cpp index 55b32e07..87ed564e 100644 --- a/sources/System/IAControllable/IAControllableSystem.cpp +++ b/sources/System/IAControllable/IAControllableSystem.cpp @@ -2,6 +2,7 @@ // Created by Louis Auzuret on 06/07/21 // +#include "Map/MapInfo.hpp" #include "Component/Controllable/ControllableComponent.hpp" #include "Component/IAControllable/IAControllableComponent.hpp" #include "System/IAControllable/IAControllableSystem.hpp" @@ -43,19 +44,18 @@ namespace BBM { auto &ia = entity.get(); auto &controllable = entity.get(); + MapInfo info({1, 2, 3}, MapGenerator::NOTHING); - lua_getglobal(ia.state, "Update"); - if (!lua_isfunction(ia.state, -1)) + luabridge::LuaRef updateFunc = luabridge::getGlobal(ia.state, "Update"); + if (!updateFunc.isFunction()) + return; + luabridge::LuaResult res = updateFunc(info); + + if (res.hasFailed() || res.size() != 4) return; - //std::vector<> players; - //std::vector<> map; - //push parameters - int nbParams = 0; - int nbReturn = 4; - lua_pcall(ia.state, nbParams, nbReturn, 0); - controllable.bomb = getReturnBool(ia.state); - controllable.jump = getReturnBool(ia.state); - controllable.move.y = getReturnNumber(ia.state); - controllable.move.x = getReturnNumber(ia.state); + controllable.bomb = res[3]; + controllable.jump = res[2]; + controllable.move.y = res[1]; + controllable.move.x = res[0]; } } \ No newline at end of file