diff --git a/CMakeLists.txt b/CMakeLists.txt index a99ac474..afefeb04 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,7 +8,6 @@ include_directories(bomberman ${LUA_INCLUDE_DIR}) include_directories(bomberman lib/Ray/sources) include_directories(bomberman lib/wal/sources) include_directories(bomberman lib/LuaGate/sources) -include_directories(bomberman lib/LuaBridge) include_directories(bomberman sources) add_subdirectory(${PROJECT_SOURCE_DIR}/lib/wal) @@ -129,7 +128,7 @@ add_executable(bomberman ${SOURCES} ) -target_include_directories(bomberman PUBLIC sources ${LUA_INCLUDE_DIR} lib/LuaBridge) +target_include_directories(bomberman PUBLIC sources ${LUA_INCLUDE_DIR}) target_link_libraries(bomberman PUBLIC wal ray ${LUA_LIBRARIES} LuaGate) add_executable(unit_tests EXCLUDE_FROM_ALL diff --git a/ai_scripts/john.lua b/ai_scripts/john.lua index 63665371..0882c3d4 100644 --- a/ai_scripts/john.lua +++ b/ai_scripts/john.lua @@ -9,7 +9,7 @@ mapinfo.dist { } ------------ -local debug = true +local debug = false if not debug then log = function() end @@ -30,42 +30,52 @@ function PrintMap(map, maxX, maxZ) end end -function CreateMyMap(infos, maxX, maxZ) +function CreateMyMap(infos, maxX, maxY) local map = {} for i=0,maxX + 1 do map[i] = {} - for j=0,maxZ + 1 do + for j=0,maxY + 1 do map[i][j] = 0 end end for i, info in ipairs(infos) do - map[info.x][info.z] = info.type + map[info.x][info.y] = info.type end - PrintMap(map, maxX, maxZ) + PrintMap(map, maxX, maxY) return map end +function isInExplosionRange(mapinfo, x, y) + for i, danger in ipairs(mapinfo.danger) do + if danger.x == x and danger.y == y then + return true + end + end + return false +end + function Update(mapinfo) - --print(mapinfo.raw[0].x) - --print(mapinfo.raw[0].y) - --print(mapinfo.raw[0].z) local maxX = 0 - local maxZ = 0 + local maxY = 0 for i, info in ipairs(mapinfo.raw) do if info.x > maxX then maxX = info.x end - if info.z > maxZ then - maxZ = info.z + if info.y > maxY then + maxY = info.y end end - local myMap = CreateMyMap(mapinfo.raw, maxX, maxZ) - return 1, 1, false, false; - --if (isInExplosionRange()) then - -- return 0, , false, true - -- --play defensive RUN - --else - -- return 1, 1, false, false; - -- --play offensive - --end + local roundedPlayerPos = {x = math.floor(mapinfo.player.x+0.5), y = math.floor(mapinfo.player.y+0.5)} + local myMap = CreateMyMap(mapinfo.raw, maxX, maxY) + if (isInExplosionRange(mapinfo, roundedPlayerPos.x, roundedPlayerPos.y)) then + if (myMap[roundedPlayerPos.x + 1][roundedPlayerPos.y] ~= 0) then + return -1, 0, false, false + else + return 1, 0, false, false + end + --play defensive RUN + else + return 1, 0, true, true; + --play offensive + end end \ No newline at end of file diff --git a/lib/LuaBridge/LuaBridge.hpp b/lib/LuaBridge/LuaBridge.hpp deleted file mode 100644 index d8e84159..00000000 --- a/lib/LuaBridge/LuaBridge.hpp +++ /dev/null @@ -1,7789 +0,0 @@ -// 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/System/IAControllable/IAControllableSystem.cpp b/sources/System/IAControllable/IAControllableSystem.cpp index 1ef106c8..44dca2ca 100644 --- a/sources/System/IAControllable/IAControllableSystem.cpp +++ b/sources/System/IAControllable/IAControllableSystem.cpp @@ -49,9 +49,6 @@ namespace BBM state.push(player.x); state.setTable(); state.push("y"); - state.push(player.y); - state.setTable(); - state.push("z"); state.push(player.z); state.setTable(); state.setTable(); @@ -69,9 +66,6 @@ namespace BBM state.push(info.x); state.setTable(); state.push("y"); - state.push(info.y); - state.setTable(); - state.push("z"); state.push(info.z); state.setTable(); state.push("type"); @@ -82,11 +76,48 @@ namespace BBM state.setTable(); } + void IAControllableSystem::pushInfoDangerPos(LuaG::State &state, int &index, float xpos, float ypos) + { + state.push(index++); + state.newTable(); + state.push("x"); + state.push(xpos); + state.setTable(); + state.push("y"); + state.push(ypos); + state.setTable(); + state.setTable(); + } + + void IAControllableSystem::pushInfoDanger(LuaG::State &state) + { + int index = 0; + state.push("danger"); + state.newTable(); + for (auto &bomb : _bombs) { + Vector3f bombPos = std::get<0>(bomb); + int bombRadius = std::get<1>(bomb); + pushInfoDangerPos(state, index, bombPos.x, bombPos.z); + for (int i = 1; i < bombRadius; i++) { + Vector3f pos = bombPos - Vector3f(i, 0, 0); + pushInfoDangerPos(state, index, pos.x, pos.z); + pos = bombPos - Vector3f(-i, 0, 0); + pushInfoDangerPos(state, index, pos.x, pos.z); + pos = bombPos - Vector3f(0, 0, i); + pushInfoDangerPos(state, index, pos.x, pos.z); + pos = bombPos - Vector3f(0, 0, -i); + pushInfoDangerPos(state, index, pos.x, pos.z); + } + } + state.setTable(); + } + void IAControllableSystem::pushInfo(LuaG::State &state, MapInfo &player) { state.newTable(); pushInfoPlayer(state, player); pushInfoRaw(state); + pushInfoDanger(state); } void IAControllableSystem::onFixedUpdate(WAL::ViewEntity &entity) @@ -116,25 +147,4 @@ namespace BBM _map.clear(); _bombs.clear(); } - - bool IAControllableSystem::isInExplosionRange(float x, float y, float z) - { - Vector3f pos(x, y, z); - //pos = pos.round(); - //for (auto &bomb : _bombs) { - // Vector3f bombPos = std::get<0>(bomb); - // int bombRadius = std::get<1>(bomb); - // for (int i = 1; i < bombRadius; i++) { - // if (pos == bombPos - Vector3f(i, 0, 0)) - // return true; - // if (pos == bombPos - Vector3f(-i, 0, 0)) - // return true; - // if (pos == bombPos - Vector3f(0, 0, i)) - // return true; - // if (pos == bombPos - Vector3f(0, 0, -i)) - // return true; - // } - //} - return true; - } } \ No newline at end of file diff --git a/sources/System/IAControllable/IAControllableSystem.hpp b/sources/System/IAControllable/IAControllableSystem.hpp index 5bc1fa06..d2347512 100644 --- a/sources/System/IAControllable/IAControllableSystem.hpp +++ b/sources/System/IAControllable/IAControllableSystem.hpp @@ -31,13 +31,19 @@ namespace BBM //! @brief update the raw info of the map void UpdateMapInfos(WAL::ViewEntity &entity); - + + //! @brief push danger info position + void pushInfoDangerPos(LuaG::State &state, int &index, float xpos, float ypos); + //! @brief push player info void pushInfoPlayer(LuaG::State &state, MapInfo &player); //! @brief push raw map info void pushInfoRaw(LuaG::State &state); + //! @brief push danger map info + void pushInfoDanger(LuaG::State &state); + //! @brief push all the infos to the ai stack void pushInfo(LuaG::State &state, MapInfo &player); public: @@ -56,7 +62,5 @@ namespace BBM ~IAControllableSystem() override = default; //! @brief A keyboard system is assignable. IAControllableSystem &operator=(const IAControllableSystem &) = default; - - static bool isInExplosionRange(float x, float y, float z); }; }