// 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 #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 #if defined(LUAU_FASTMATH_BEGIN) #define LUABRIDGE_ON_LUAU 1 #elif defined(LUA_JROOT) #define LUABRIDGE_ON_LUAJIT 1 #elif defined(LUA_VERSION_NUM) #define LUABRIDGE_ON_LUA 1 #else #error "Lua headers must be included prior to LuaBridge ones" #endif #if !defined(LUABRIDGE_SAFE_STACK_CHECKS) #define LUABRIDGE_SAFE_STACK_CHECKS 1 #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 { /** * @brief Helper for unused vars. */ template constexpr void unused(Args&&...) { } // These functions and defines are for Luau. #if LUABRIDGE_ON_LUAU inline int luaL_ref(lua_State* L, int idx) { assert(idx == LUA_REGISTRYINDEX); const int ref = lua_ref(L, -1); lua_pop(L, 1); return ref; } inline void luaL_unref(lua_State* L, int idx, int ref) { unused(idx); lua_unref(L, ref); } template inline void* lua_newuserdata_x(lua_State* L, size_t sz) { return lua_newuserdatadtor(L, sz, [](void* x) { T* object = static_cast(x); object->~T(); }); } inline void lua_pushcfunction_x(lua_State *L, lua_CFunction fn) { lua_pushcfunction(L, fn, ""); } inline void lua_pushcclosure_x(lua_State* L, lua_CFunction fn, int n) { lua_pushcclosure(L, fn, "", n); } #else using ::luaL_ref; using ::luaL_unref; template inline void* lua_newuserdata_x(lua_State* L, size_t sz) { return lua_newuserdata(L, sz); } inline void lua_pushcfunction_x(lua_State *L, lua_CFunction fn) { lua_pushcfunction(L, fn); } inline void lua_pushcclosure_x(lua_State* L, lua_CFunction fn, int n) { lua_pushcclosure(L, fn, n); } #endif // LUABRIDGE_ON_LUAU // These are for Lua versions prior to 5.3.0. #if LUA_VERSION_NUM < 503 inline lua_Number to_numberx(lua_State* L, int idx, int* isnum) { lua_Number n = lua_tonumber(L, idx); if (isnum) *isnum = (n != 0 || lua_isnumber(L, idx)); return n; } inline lua_Integer to_integerx(lua_State* L, int idx, int* isnum) { int ok = 0; lua_Number n = to_numberx(L, idx, &ok); if (ok) { const auto int_n = static_cast(n); if (n == static_cast(int_n)) { if (isnum) *isnum = 1; return int_n; } } if (isnum) *isnum = 0; return 0; } #endif // LUA_VERSION_NUM < 503 // These are for Lua versions prior to 5.2.0. #if LUA_VERSION_NUM < 502 using lua_Unsigned = std::make_unsigned_t; #if ! LUABRIDGE_ON_LUAU inline int lua_absindex(lua_State* L, int idx) { if (idx > LUA_REGISTRYINDEX && idx < 0) return lua_gettop(L) + idx + 1; else return idx; } #endif inline void lua_rawgetp(lua_State* L, int idx, const void* p) { idx = lua_absindex(L, idx); luaL_checkstack(L, 1, "not enough stack slots"); lua_pushlightuserdata(L, const_cast(p)); lua_rawget(L, idx); } inline void lua_rawsetp(lua_State* L, int idx, const void* 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); case LUA_OPLT: return lua_lessthan(L, idx1, idx2); case LUA_OPLE: return lua_equal(L, idx1, idx2) || lua_lessthan(L, idx1, idx2); default: return 0; } } inline int get_length(lua_State* L, int idx) { return static_cast(lua_objlen(L, idx)); } #else // LUA_VERSION_NUM >= 502 inline int get_length(lua_State* L, int idx) { lua_len(L, idx); const int len = static_cast(luaL_checknumber(L, -1)); lua_pop(L, 1); return len; } #endif // LUA_VERSION_NUM < 502 #ifndef LUA_OK #define LUABRIDGE_LUA_OK 0 #else #define LUABRIDGE_LUA_OK LUA_OK #endif /** * @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 unused(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) { static_assert(std::is_unsigned_v); lua_pushinteger(L, static_cast(value)); } /** * @brief Helper to convert to integer. */ inline lua_Number tonumber(lua_State* L, int idx, int* isnum) { #if ! LUABRIDGE_ON_LUAU && LUA_VERSION_NUM > 502 return lua_tonumberx(L, idx, isnum); #else return to_numberx(L, idx, isnum); #endif } /** * @brief Helper to convert to integer. */ inline lua_Integer tointeger(lua_State* L, int idx, int* isnum) { #if ! LUABRIDGE_ON_LUAU && LUA_VERSION_NUM > 502 return lua_tointegerx(L, idx, isnum); #else return to_integerx(L, idx, isnum); #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 { const auto address = reinterpret_cast(ptr); const auto offset = address % alignof(T); const 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]] constexpr size_t maximum_space_needed_to_align() noexcept { return sizeof(T) + alignof(T) - 1; } /** * @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 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) { #if LUABRIDGE_ON_LUAU void* pointer = lua_newuserdatadtor(L, maximum_space_needed_to_align(), [](void* x) { T* aligned = align(x); aligned->~T(); }); #else void* pointer = lua_newuserdata_x(L, maximum_space_needed_to_align()); lua_newtable(L); lua_pushcfunction_x(L, &lua_deleteuserdata_aligned); rawsetfield(L, -2, "__gc"); lua_setmetatable(L, -2); #endif T* aligned = align(pointer); new (aligned) T(std::forward(args)...); return pointer; } /** * @brief Checks if the value on the stack is a number type and can fit into the corresponding c++ integral type.. */ template constexpr bool is_integral_representable_by(T value) { constexpr bool same_signedness = (std::is_unsigned_v && std::is_unsigned_v) || (!std::is_unsigned_v && !std::is_unsigned_v); if constexpr (sizeof(T) == sizeof(U)) { if constexpr (same_signedness) return true; if constexpr (std::is_unsigned_v) return value <= static_cast(std::numeric_limits::max()); return value >= static_cast(std::numeric_limits::min()) && static_cast(value) <= std::numeric_limits::max(); } if constexpr (sizeof(T) < sizeof(U)) { return static_cast(value) >= std::numeric_limits::min() && static_cast(value) <= std::numeric_limits::max(); } if constexpr (std::is_unsigned_v) return value <= static_cast(std::numeric_limits::max()); return value >= static_cast(std::numeric_limits::min()) && value <= static_cast(std::numeric_limits::max()); } template bool is_integral_representable_by(lua_State* L, int index) { int isValid = 0; const auto value = tointeger(L, index, &isValid); return isValid ? is_integral_representable_by(value) : false; } /** * @brief Checks if the value on the stack is a number type and can fit into the corresponding c++ numerical type.. */ template constexpr bool is_floating_point_representable_by(T value) { if constexpr (sizeof(T) == sizeof(U)) return true; if constexpr (sizeof(T) < sizeof(U)) return static_cast(value) >= -std::numeric_limits::max() && static_cast(value) <= std::numeric_limits::max(); return value >= static_cast(-std::numeric_limits::max()) && value <= static_cast(std::numeric_limits::max()); } template bool is_floating_point_representable_by(lua_State* L, int index) { int isValid = 0; const auto value = tonumber(L, index, &isValid); return isValid ? is_floating_point_representable_by(value) : false; } } // 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 { //================================================================================================= namespace detail { static inline constexpr char error_lua_stack_overflow[] = "stack overflow"; } // namespace detail //================================================================================================= /** * @brief LuaBridge error codes. */ enum class ErrorCode { ClassNotRegistered = 1, LuaStackOverflow, LuaFunctionCallFailed, IntegerDoesntFitIntoLuaInteger, FloatingPointDoesntFitIntoLuaNumber, }; //================================================================================================= 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::LuaStackOverflow: return "The lua stack has overflow"; case ErrorCode::LuaFunctionCallFailed: return "The lua function invocation raised an error"; case ErrorCode::IntegerDoesntFitIntoLuaInteger: return "The native integer can't fit inside a lua integer"; case ErrorCode::FloatingPointDoesntFitIntoLuaNumber: return "The native floating point can't fit inside a lua number"; 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; #if LUABRIDGE_ON_LUAU auto callbacks = lua_callbacks(L); callbacks->panic = +[](lua_State* L, int) { panicHandlerCallback(L); }; #else lua_atpanic(L, panicHandlerCallback); #endif } //============================================================================================= /** * @brief Retrieve the lua_State associated with the exception. * * @return A Lua state. */ lua_State* state() const { return m_L; } 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 nullptr; } 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: UserdataValue(const UserdataValue&) = delete; UserdataValue operator=(const UserdataValue&) = delete; ~UserdataValue() { if (getPointer() != nullptr) { getObject()->~T(); } } /** * @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_x>(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 : Userdata() { } 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: UserdataPtr(const UserdataPtr&) = delete; UserdataPtr operator=(const UserdataPtr&) = delete; /** * @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_x(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; } }; //============================================================================ /** * @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(const UserdataShared&) = delete; UserdataShared& operator=(const UserdataShared&) = delete; ~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: 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_x>(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_x>(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_x>(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_x>(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 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; [[nodiscard]] static bool push(lua_State* L, const T& value, std::error_code& ec) { return detail::StackHelper::value>::push(L, value, ec); } [[nodiscard]] static bool push(lua_State* L, T&& value, std::error_code& ec) { return detail::StackHelper::value>::push(L, std::move(value), ec); } [[nodiscard]] static ReturnType get(lua_State* L, int index) { return Getter::get(L, index); } [[nodiscard]] 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 implementation for userdata. */ 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 { [[nodiscard]] static bool push(lua_State*, std::error_code&) { return true; } }; //================================================================================================= /** * @brief Specialization for nullptr_t. */ template <> struct Stack { [[nodiscard]] static bool push(lua_State* L, std::nullptr_t, std::error_code& ec) { #if LUABRIDGE_SAFE_STACK_CHECKS if (! lua_checkstack(L, 1)) { ec = makeErrorCode(ErrorCode::LuaStackOverflow); return false; } #endif lua_pushnil(L); return true; } [[nodiscard]] static std::nullptr_t get(lua_State*, int) { return nullptr; } [[nodiscard]] static bool isInstance(lua_State* L, int index) { return lua_isnil(L, index); } }; //================================================================================================= /** * @brief Receive the lua_State* as an argument. */ template <> struct Stack { [[nodiscard]] static lua_State* get(lua_State* L, int) { return L; } }; //================================================================================================= /** * @brief Stack specialization for a lua_CFunction. */ template <> struct Stack { [[nodiscard]] static bool push(lua_State* L, lua_CFunction f, std::error_code& ec) { #if LUABRIDGE_SAFE_STACK_CHECKS if (! lua_checkstack(L, 1)) { ec = makeErrorCode(ErrorCode::LuaStackOverflow); return false; } #endif lua_pushcfunction_x(L, f); return true; } [[nodiscard]] static lua_CFunction get(lua_State* L, int index) { return lua_tocfunction(L, index); } [[nodiscard]] static bool isInstance(lua_State* L, int index) { return lua_iscfunction(L, index); } }; //================================================================================================= /** * @brief Stack specialization for `bool`. */ template <> struct Stack { [[nodiscard]] static bool push(lua_State* L, bool value, std::error_code& ec) { #if LUABRIDGE_SAFE_STACK_CHECKS if (! lua_checkstack(L, 1)) { ec = makeErrorCode(ErrorCode::LuaStackOverflow); return false; } #endif lua_pushboolean(L, value ? 1 : 0); return true; } [[nodiscard]] static bool get(lua_State* L, int index) { return lua_toboolean(L, index) ? true : false; } [[nodiscard]] static bool isInstance(lua_State* L, int index) { return lua_isboolean(L, index); } }; //================================================================================================= /** * @brief Stack specialization for `std::byte`. */ template <> struct Stack { static_assert(sizeof(std::byte) < sizeof(lua_Integer)); [[nodiscard]] static bool push(lua_State* L, std::byte value, std::error_code& ec) { #if LUABRIDGE_SAFE_STACK_CHECKS if (! lua_checkstack(L, 1)) { ec = makeErrorCode(ErrorCode::LuaStackOverflow); return false; } #endif pushunsigned(L, std::to_integer>(value)); return true; } [[nodiscard]] static std::byte get(lua_State* L, int index) { return static_cast(luaL_checkinteger(L, index)); } [[nodiscard]] static bool isInstance(lua_State* L, int index) { if (lua_type(L, index) == LUA_TNUMBER) return is_integral_representable_by(L, index); return false; } }; //================================================================================================= /** * @brief Stack specialization for `char`. */ template <> struct Stack { [[nodiscard]] static bool push(lua_State* L, char value, std::error_code& ec) { #if LUABRIDGE_SAFE_STACK_CHECKS if (! lua_checkstack(L, 1)) { ec = makeErrorCode(ErrorCode::LuaStackOverflow); return false; } #endif lua_pushlstring(L, &value, 1); return true; } [[nodiscard]] static char get(lua_State* L, int index) { return luaL_checkstring(L, index)[0]; } [[nodiscard]] static bool isInstance(lua_State* L, int index) { if (lua_type(L, index) == LUA_TSTRING) { std::size_t len; luaL_checklstring(L, index, &len); return len == 1; } return false; } }; //================================================================================================= /** * @brief Stack specialization for `int8_t`. */ template <> struct Stack { static_assert(sizeof(int8_t) < sizeof(lua_Integer)); [[nodiscard]] static bool push(lua_State* L, int8_t value, std::error_code& ec) { #if LUABRIDGE_SAFE_STACK_CHECKS if (! lua_checkstack(L, 1)) { ec = makeErrorCode(ErrorCode::LuaStackOverflow); return false; } #endif lua_pushinteger(L, static_cast(value)); return true; } [[nodiscard]] static int8_t get(lua_State* L, int index) { return static_cast(luaL_checkinteger(L, index)); } [[nodiscard]] static bool isInstance(lua_State* L, int index) { if (lua_type(L, index) == LUA_TNUMBER) return is_integral_representable_by(L, index); return false; } }; //================================================================================================= /** * @brief Stack specialization for `unsigned char`. */ template <> struct Stack { static_assert(sizeof(unsigned char) < sizeof(lua_Integer)); [[nodiscard]] static bool push(lua_State* L, unsigned char value, std::error_code& ec) { #if LUABRIDGE_SAFE_STACK_CHECKS if (! lua_checkstack(L, 1)) { ec = makeErrorCode(ErrorCode::LuaStackOverflow); return false; } #endif pushunsigned(L, value); return true; } [[nodiscard]] static unsigned char get(lua_State* L, int index) { return static_cast(luaL_checkinteger(L, index)); } [[nodiscard]] static bool isInstance(lua_State* L, int index) { if (lua_type(L, index) == LUA_TNUMBER) return is_integral_representable_by(L, index); return false; } }; //================================================================================================= /** * @brief Stack specialization for `short`. */ template <> struct Stack { static_assert(sizeof(short) < sizeof(lua_Integer)); [[nodiscard]] static bool push(lua_State* L, short value, std::error_code& ec) { #if LUABRIDGE_SAFE_STACK_CHECKS if (! lua_checkstack(L, 1)) { ec = makeErrorCode(ErrorCode::LuaStackOverflow); return false; } #endif lua_pushinteger(L, static_cast(value)); return true; } [[nodiscard]] static short get(lua_State* L, int index) { return static_cast(luaL_checkinteger(L, index)); } [[nodiscard]] static bool isInstance(lua_State* L, int index) { if (lua_type(L, index) == LUA_TNUMBER) return is_integral_representable_by(L, index); return false; } }; //================================================================================================= /** * @brief Stack specialization for `unsigned short`. */ template <> struct Stack { static_assert(sizeof(unsigned short) < sizeof(lua_Integer)); [[nodiscard]] static bool push(lua_State* L, unsigned short value, std::error_code& ec) { #if LUABRIDGE_SAFE_STACK_CHECKS if (! lua_checkstack(L, 1)) { ec = makeErrorCode(ErrorCode::LuaStackOverflow); return false; } #endif pushunsigned(L, value); return true; } [[nodiscard]] static unsigned short get(lua_State* L, int index) { return static_cast(luaL_checkinteger(L, index)); } [[nodiscard]] static bool isInstance(lua_State* L, int index) { if (lua_type(L, index) == LUA_TNUMBER) return is_integral_representable_by(L, index); return false; } }; //================================================================================================= /** * @brief Stack specialization for `int`. */ template <> struct Stack { [[nodiscard]] static bool push(lua_State* L, int value, std::error_code& ec) { #if LUABRIDGE_SAFE_STACK_CHECKS if (! lua_checkstack(L, 1)) { ec = makeErrorCode(ErrorCode::LuaStackOverflow); return false; } #endif if (! is_integral_representable_by(value)) { ec = makeErrorCode(ErrorCode::IntegerDoesntFitIntoLuaInteger); return false; } lua_pushinteger(L, static_cast(value)); return true; } [[nodiscard]] static int get(lua_State* L, int index) { return static_cast(luaL_checkinteger(L, index)); } [[nodiscard]] static bool isInstance(lua_State* L, int index) { if (lua_type(L, index) == LUA_TNUMBER) return is_integral_representable_by(L, index); return false; } }; //================================================================================================= /** * @brief Stack specialization for `unsigned int`. */ template <> struct Stack { [[nodiscard]] static bool push(lua_State* L, unsigned int value, std::error_code& ec) { #if LUABRIDGE_SAFE_STACK_CHECKS if (! lua_checkstack(L, 1)) { ec = makeErrorCode(ErrorCode::LuaStackOverflow); return false; } #endif if (! is_integral_representable_by(value)) { ec = makeErrorCode(ErrorCode::IntegerDoesntFitIntoLuaInteger); return false; } pushunsigned(L, value); return true; } [[nodiscard]] static uint32_t get(lua_State* L, int index) { return static_cast(luaL_checkinteger(L, index)); } [[nodiscard]] static bool isInstance(lua_State* L, int index) { if (lua_type(L, index) == LUA_TNUMBER) return is_integral_representable_by(L, index); return false; } }; //================================================================================================= /** * @brief Stack specialization for `long`. */ template <> struct Stack { [[nodiscard]] static bool push(lua_State* L, long value, std::error_code& ec) { #if LUABRIDGE_SAFE_STACK_CHECKS if (! lua_checkstack(L, 1)) { ec = makeErrorCode(ErrorCode::LuaStackOverflow); return false; } #endif if (! is_integral_representable_by(value)) { ec = makeErrorCode(ErrorCode::IntegerDoesntFitIntoLuaInteger); return false; } lua_pushinteger(L, static_cast(value)); return true; } [[nodiscard]] static long get(lua_State* L, int index) { return static_cast(luaL_checkinteger(L, index)); } [[nodiscard]] static bool isInstance(lua_State* L, int index) { if (lua_type(L, index) == LUA_TNUMBER) return is_integral_representable_by(L, index); return false; } }; //================================================================================================= /** * @brief Stack specialization for `unsigned long`. */ template <> struct Stack { [[nodiscard]] static bool push(lua_State* L, unsigned long value, std::error_code& ec) { #if LUABRIDGE_SAFE_STACK_CHECKS if (! lua_checkstack(L, 1)) { ec = makeErrorCode(ErrorCode::LuaStackOverflow); return false; } #endif if (! is_integral_representable_by(value)) { ec = makeErrorCode(ErrorCode::IntegerDoesntFitIntoLuaInteger); return false; } pushunsigned(L, value); return true; } [[nodiscard]] static unsigned long get(lua_State* L, int index) { return static_cast(luaL_checkinteger(L, index)); } [[nodiscard]] static bool isInstance(lua_State* L, int index) { if (lua_type(L, index) == LUA_TNUMBER) return is_integral_representable_by(L, index); return false; } }; //================================================================================================= /** * @brief Stack specialization for `long long`. */ template <> struct Stack { [[nodiscard]] static bool push(lua_State* L, long long value, std::error_code& ec) { #if LUABRIDGE_SAFE_STACK_CHECKS if (! lua_checkstack(L, 1)) { ec = makeErrorCode(ErrorCode::LuaStackOverflow); return false; } #endif if (! is_integral_representable_by(value)) { ec = makeErrorCode(ErrorCode::IntegerDoesntFitIntoLuaInteger); return false; } lua_pushinteger(L, static_cast(value)); return true; } [[nodiscard]] static long long get(lua_State* L, int index) { return static_cast(luaL_checkinteger(L, index)); } [[nodiscard]] static bool isInstance(lua_State* L, int index) { if (lua_type(L, index) == LUA_TNUMBER) return is_integral_representable_by(L, index); return false; } }; //================================================================================================= /** * @brief Stack specialization for `unsigned long long`. */ template <> struct Stack { [[nodiscard]] static bool push(lua_State* L, unsigned long long value, std::error_code& ec) { #if LUABRIDGE_SAFE_STACK_CHECKS if (! lua_checkstack(L, 1)) { ec = makeErrorCode(ErrorCode::LuaStackOverflow); return false; } #endif if (! is_integral_representable_by(value)) { ec = makeErrorCode(ErrorCode::IntegerDoesntFitIntoLuaInteger); return false; } pushunsigned(L, value); return true; } [[nodiscard]] static unsigned long long get(lua_State* L, int index) { return static_cast(luaL_checkinteger(L, index)); } [[nodiscard]] static bool isInstance(lua_State* L, int index) { if (lua_type(L, index) == LUA_TNUMBER) return is_integral_representable_by(L, index); return false; } }; //================================================================================================= /** * @brief Stack specialization for `float`. */ template <> struct Stack { [[nodiscard]] static bool push(lua_State* L, float value, std::error_code& ec) { #if LUABRIDGE_SAFE_STACK_CHECKS if (! lua_checkstack(L, 1)) { ec = makeErrorCode(ErrorCode::LuaStackOverflow); return false; } #endif if (! is_floating_point_representable_by(value)) { ec = makeErrorCode(ErrorCode::FloatingPointDoesntFitIntoLuaNumber); return false; } lua_pushnumber(L, static_cast(value)); return true; } [[nodiscard]] static float get(lua_State* L, int index) { return static_cast(luaL_checknumber(L, index)); } [[nodiscard]] static bool isInstance(lua_State* L, int index) { if (lua_type(L, index) == LUA_TNUMBER) return is_floating_point_representable_by(L, index); return false; } }; //================================================================================================= /** * @brief Stack specialization for `double`. */ template <> struct Stack { [[nodiscard]] static bool push(lua_State* L, double value, std::error_code& ec) { #if LUABRIDGE_SAFE_STACK_CHECKS if (! lua_checkstack(L, 1)) { ec = makeErrorCode(ErrorCode::LuaStackOverflow); return false; } #endif if (! is_floating_point_representable_by(value)) { ec = makeErrorCode(ErrorCode::FloatingPointDoesntFitIntoLuaNumber); return false; } lua_pushnumber(L, static_cast(value)); return true; } [[nodiscard]] static double get(lua_State* L, int index) { return static_cast(luaL_checknumber(L, index)); } [[nodiscard]] static bool isInstance(lua_State* L, int index) { if (lua_type(L, index) == LUA_TNUMBER) return is_floating_point_representable_by(L, index); return false; } }; //================================================================================================= /** * @brief Stack specialization for `long double`. */ template <> struct Stack { [[nodiscard]] static bool push(lua_State* L, long double value, std::error_code& ec) { #if LUABRIDGE_SAFE_STACK_CHECKS if (! lua_checkstack(L, 1)) { ec = makeErrorCode(ErrorCode::LuaStackOverflow); return false; } #endif if (! is_floating_point_representable_by(value)) { ec = makeErrorCode(ErrorCode::FloatingPointDoesntFitIntoLuaNumber); return false; } lua_pushnumber(L, static_cast(value)); return true; } [[nodiscard]] static long double get(lua_State* L, int index) { return static_cast(luaL_checknumber(L, index)); } [[nodiscard]] static bool isInstance(lua_State* L, int index) { if (lua_type(L, index) == LUA_TNUMBER) return is_floating_point_representable_by(L, index); return false; } }; //================================================================================================= /** * @brief Stack specialization for `const char*`. */ template <> struct Stack { [[nodiscard]] static bool push(lua_State* L, const char* str, std::error_code& ec) { #if LUABRIDGE_SAFE_STACK_CHECKS if (! lua_checkstack(L, 1)) { ec = makeErrorCode(ErrorCode::LuaStackOverflow); return false; } #endif if (str != nullptr) lua_pushstring(L, str); else lua_pushlstring(L, "", 0); return true; } [[nodiscard]] static const char* get(lua_State* L, int index) { return luaL_checkstring(L, index); } [[nodiscard]] static bool isInstance(lua_State* L, int index) { return lua_type(L, index) == LUA_TSTRING; } }; //================================================================================================= /** * @brief Stack specialization for `std::string_view`. */ template <> struct Stack { [[nodiscard]] static bool push(lua_State* L, std::string_view str, std::error_code& ec) { #if LUABRIDGE_SAFE_STACK_CHECKS if (! lua_checkstack(L, 1)) { ec = makeErrorCode(ErrorCode::LuaStackOverflow); return false; } #endif lua_pushlstring(L, str.data(), str.size()); return true; } [[nodiscard]] static std::string_view get(lua_State* L, int index) { return luaL_checkstring(L, index); } [[nodiscard]] static bool isInstance(lua_State* L, int index) { return lua_type(L, index) == LUA_TSTRING; } }; //================================================================================================= /** * @brief Stack specialization for `std::string`. */ template <> struct Stack { [[nodiscard]] static bool push(lua_State* L, const std::string& str, std::error_code& ec) { #if LUABRIDGE_SAFE_STACK_CHECKS if (! lua_checkstack(L, 1)) { ec = makeErrorCode(ErrorCode::LuaStackOverflow); return false; } #endif lua_pushlstring(L, str.data(), str.size()); return true; } [[nodiscard]] 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; } [[nodiscard]] static bool isInstance(lua_State* L, int index) { return lua_type(L, index) == LUA_TSTRING; } }; //================================================================================================= /** * @brief Stack specialization for `std::optional`. */ template struct Stack> { using Type = std::optional; [[nodiscard]] static bool push(lua_State* L, const Type& value, std::error_code& ec) { if (value) return Stack::push(L, *value, ec); #if LUABRIDGE_SAFE_STACK_CHECKS if (! lua_checkstack(L, 1)) { ec = makeErrorCode(ErrorCode::LuaStackOverflow); return false; } #endif lua_pushnil(L); return true; } [[nodiscard]] static Type get(lua_State* L, int index) { if (lua_type(L, index) == LUA_TNIL) return std::nullopt; return Stack::get(L, index); } [[nodiscard]] static bool isInstance(lua_State* L, int index) { return lua_isnil(L, index) || Stack::isInstance(L, index); } }; //================================================================================================= /** * @brief Stack specialization for `std::tuple`. */ template struct Stack> { [[nodiscard]] static bool push(lua_State* L, const std::tuple& t, std::error_code& ec) { #if LUABRIDGE_SAFE_STACK_CHECKS if (! lua_checkstack(L, 3)) { ec = makeErrorCode(ErrorCode::LuaStackOverflow); return false; } #endif lua_createtable(L, static_cast(Size), 0); return push_element(L, t, ec); } [[nodiscard]] 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) != static_cast(Size)) luaL_error(L, "table size should be %d but is %d", static_cast(Size), get_length(L, index)); std::tuple value; int absIndex = lua_absindex(L, index); lua_pushnil(L); pop_element(L, absIndex, value); return value; } [[nodiscard]] static bool isInstance(lua_State* L, int index) { return lua_type(L, index) == LUA_TTABLE && get_length(L, index) == static_cast(Size); } 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); } }; //================================================================================================= /** * @brief Stack specialization for `T[N]`. */ template struct Stack { static_assert(N > 0, "Unsupported zero sized array"); [[nodiscard]] static bool push(lua_State* L, const T (&value)[N], std::error_code& ec) { if constexpr (std::is_same_v) { #if LUABRIDGE_SAFE_STACK_CHECKS if (! lua_checkstack(L, 1)) { ec = makeErrorCode(ErrorCode::LuaStackOverflow); return false; } #endif lua_pushlstring(L, value, N - 1); return true; } #if LUABRIDGE_SAFE_STACK_CHECKS if (! lua_checkstack(L, 2)) { ec = makeErrorCode(ErrorCode::LuaStackOverflow); return false; } #endif const int initialStackSize = lua_gettop(L); lua_createtable(L, static_cast(N), 0); for (std::size_t i = 0; i < N; ++i) { lua_pushinteger(L, static_cast(i + 1)); std::error_code push_ec; bool result = Stack::push(L, value[i], push_ec); if (! result) { ec = push_ec; lua_pop(L, lua_gettop(L) - initialStackSize); return false; } lua_settable(L, -3); } return true; } [[nodiscard]] static bool isInstance(lua_State* L, int index) { return lua_type(L, index) == LUA_TTABLE && get_length(L, index) == static_cast(N); } }; 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 auto 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>> { using Helper = detail::StackOpSelector::value>; using ReturnType = typename Helper::ReturnType; [[nodiscard]] static bool push(lua_State* L, T& value, std::error_code& ec) { return Helper::push(L, value, ec); } [[nodiscard]] static ReturnType get(lua_State* L, int index) { return Helper::get(L, index); } [[nodiscard]] 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; [[nodiscard]] static bool push(lua_State* L, const T& value, std::error_code& ec) { return Helper::push(L, value, ec); } [[nodiscard]] static auto get(lua_State* L, int index) { return Helper::get(L, index); } [[nodiscard]] 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; [[nodiscard]] static bool push(lua_State* L, T* value, std::error_code& ec) { return Helper::push(L, value, ec); } [[nodiscard]] static ReturnType get(lua_State* L, int index) { return Helper::get(L, index); } [[nodiscard]] 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; [[nodiscard]] static bool push(lua_State* L, const T* value, std::error_code& ec) { return Helper::push(L, value, ec); } [[nodiscard]] static ReturnType get(lua_State* L, int index) { return Helper::get(L, index); } [[nodiscard]] static bool isInstance(lua_State* L, int index) { return Helper::template isInstance(L, index); } }; //================================================================================================= /** * @brief Push an object onto the Lua stack. */ template [[nodiscard]] bool push(lua_State* L, const T& t, std::error_code& ec) { return Stack::push(L, t, ec); } //================================================================================================= /** * @brief Get an object from the Lua stack. */ template [[nodiscard]] T get(lua_State* L, int index) { return Stack::get(L, index); } //================================================================================================= /** * @brief Check whether an object on the Lua stack is of type T. */ template [[nodiscard]] bool isInstance(lua_State* L, int index) { return Stack::isInstance(L, index); } //================================================================================================= /** * @brief Stack restorer. */ class StackRestore final { public: StackRestore(lua_State* L) : m_L(L) , m_stackTop(lua_gettop(L)) { } ~StackRestore() { lua_settop(m_L, m_stackTop); } private: lua_State* const m_L = nullptr; int m_stackTop = 0; }; } // namespace luabridge // End File: Source/LuaBridge/detail/Stack.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; [[nodiscard]] static bool push(lua_State* L, const Type& vector, std::error_code& ec) { #if LUABRIDGE_SAFE_STACK_CHECKS if (! lua_checkstack(L, 3)) { ec = makeErrorCode(ErrorCode::LuaStackOverflow); return false; } #endif 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; } [[nodiscard]] 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; } [[nodiscard]] 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/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; [[nodiscard]] static bool push(lua_State* L, const Type& map, std::error_code& ec) { #if LUABRIDGE_SAFE_STACK_CHECKS if (! lua_checkstack(L, 3)) { ec = makeErrorCode(ErrorCode::LuaStackOverflow); return false; } #endif 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; } [[nodiscard]] 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; } [[nodiscard]] 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/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; [[nodiscard]] static bool push(lua_State* L, const Type& set, std::error_code& ec) { #if LUABRIDGE_SAFE_STACK_CHECKS if (! lua_checkstack(L, 3)) { ec = makeErrorCode(ErrorCode::LuaStackOverflow); return false; } #endif 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; } [[nodiscard]] 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; } [[nodiscard]] 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/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; [[nodiscard]] static bool push(lua_State* L, const Type& map, std::error_code& ec) { #if LUABRIDGE_SAFE_STACK_CHECKS if (! lua_checkstack(L, 3)) { ec = makeErrorCode(ErrorCode::LuaStackOverflow); return false; } #endif 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; } [[nodiscard]] 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; } [[nodiscard]] 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/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) luaL_error(L, "%s", ec.message().c_str()); #if LUABRIDGE_HAS_EXCEPTIONS } catch (const std::exception& e) { luaL_error(L, "%s", e.what()); } catch (...) { luaL_error(L, "Error while calling function"); } #endif return 1; } 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) luaL_error(L, "%s", ec.message().c_str()); #if LUABRIDGE_HAS_EXCEPTIONS } catch (const std::exception& e) { luaL_error(L, "%s", e.what()); } catch (...) { luaL_error(L, "Error while calling method"); } #endif return 1; } }; 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)); #if LUABRIDGE_HAS_EXCEPTIONS } catch (const std::exception& e) { luaL_error(L, "%s", e.what()); } catch (...) { luaL_error(L, "Error while calling function"); } #endif return 0; } 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)); #if LUABRIDGE_HAS_EXCEPTIONS } catch (const std::exception& e) { luaL_error(L, "%s", e.what()); } catch (...) { luaL_error(L, "Error while calling method"); } #endif return 0; } }; //================================================================================================= /** * @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) { #if LUABRIDGE_SAFE_STACK_CHECKS luaL_checkstack(L, 3, detail::error_lua_stack_overflow); #endif 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) { #if LUABRIDGE_SAFE_STACK_CHECKS luaL_checkstack(L, 3, detail::error_lua_stack_overflow); #endif 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: - 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: - 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 } return 0; } //================================================================================================= /** * @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"; luaL_error(L, "%s", s.c_str()); return 0; } //================================================================================================= /** * @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, "%s", 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, "%s", 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, "%s", ec.message().c_str()); #if LUABRIDGE_HAS_EXCEPTIONS } catch (const std::exception& e) { luaL_error(L, "%s", 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) { #if LUABRIDGE_SAFE_STACK_CHECKS luaL_checkstack(L, 2, detail::error_lua_stack_overflow); #endif 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, "%s", 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) { #if LUABRIDGE_SAFE_STACK_CHECKS luaL_checkstack(L, 2, detail::error_lua_stack_overflow); #endif 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 { [[nodiscard]] static bool push(lua_State* L, const LuaNil&, std::error_code& ec) { #if LUABRIDGE_SAFE_STACK_CHECKS if (! lua_checkstack(L, 1)) { ec = makeErrorCode(ErrorCode::LuaStackOverflow); return false; } #endif lua_pushnil(L); return true; } [[nodiscard]] 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 { #if LUABRIDGE_SAFE_STACK_CHECKS if (! lua_checkstack(m_L, 2)) return {}; #endif 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(); if constexpr (std::is_enum_v) { using U = std::underlying_type_t; return static_cast(Stack::get(m_L, -1)); } else { 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(); if constexpr (std::is_enum_v) { using U = std::underlying_type_t; return Stack::isInstance(m_L, -1); } else { 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==(const 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!=(const 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<(const 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<=(const 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>(const 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>=(const 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(const 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. */ #if 0 template void append(const 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); } #endif //============================================================================================= /** * @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_keyRef(luaL_ref(L, LUA_REGISTRYINDEX)) { #if LUABRIDGE_SAFE_STACK_CHECKS luaL_checkstack(m_L, 1, detail::error_lua_stack_overflow); #endif 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) { #if LUABRIDGE_SAFE_STACK_CHECKS if (! lua_checkstack(m_L, 1)) return; #endif 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=(const T& v) { #if LUABRIDGE_SAFE_STACK_CHECKS if (! lua_checkstack(m_L, 2)) return *this; #endif 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(const T& v) { #if LUABRIDGE_SAFE_STACK_CHECKS if (! lua_checkstack(m_L, 2)) return *this; #endif 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 { #if LUABRIDGE_SAFE_STACK_CHECKS if (! lua_checkstack(m_L, 3)) return; #endif 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[](const 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(const T& key) const { return LuaRef(*this).rawget(key); } private: int m_tableRef = LUA_NOREF; int m_keyRef = LUA_NOREF; }; 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) { #if LUABRIDGE_SAFE_STACK_CHECKS if (! lua_checkstack(m_L, 1)) return; #endif 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) { } //============================================================================================= /** * @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, const T& v) : LuaRefBase(L) { 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 Move a reference to an existing Lua value. * * @param other An existing reference. */ LuaRef(LuaRef&& other) : LuaRefBase(other.m_L) , m_ref(std::exchange(other.m_ref, LUA_NOREF)) { } //============================================================================================= /** * @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) { return LuaRef(L, index, 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) { #if LUABRIDGE_SAFE_STACK_CHECKS if (! lua_checkstack(L, 1)) return { L }; #endif 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) { #if LUABRIDGE_SAFE_STACK_CHECKS if (! lua_checkstack(L, 1)) return { L }; #endif 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 Move assign another LuaRef to this LuaRef. * * @param rhs A reference to assign from. * * @returns This reference. */ LuaRef& operator=(LuaRef&& rhs) { if (m_ref != LUA_NOREF) luaL_unref(m_L, LUA_REGISTRYINDEX, m_ref); m_L = rhs.m_L; m_ref = std::exchange(rhs.m_ref, LUA_NOREF); 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=(const 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 { #if LUABRIDGE_SAFE_STACK_CHECKS if (! lua_checkstack(m_L, 1)) return; #endif lua_rawgeti(m_L, LUA_REGISTRYINDEX, m_ref); } //============================================================================================= /** * @brief Pop the top of Lua stack and assign the ref to m_ref */ void pop() { if (m_ref != LUA_NOREF) 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[](const 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(const 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 { [[nodiscard]] static bool push(lua_State* L, const LuaRef& v, std::error_code&) { return v.push(L), true; } [[nodiscard]] static LuaRef get(lua_State* L, int index) { return LuaRef::fromStack(L, index); } }; //================================================================================================= /** * @brief Stack specialization for `TableItem`. */ template <> struct Stack { [[nodiscard]] 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 (). */ [[nodiscard]] 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 (). */ [[nodiscard]] inline LuaRef getGlobal(lua_State* L, const char* name) { return LuaRef::getGlobal(L, name); } //================================================================================================= /** * @brief C++ like cast syntax. */ template [[nodiscard]] 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() { #if LUABRIDGE_SAFE_STACK_CHECKS if (! lua_checkstack(m_L, 2)) { m_key = LuaNil(); m_value = LuaNil(); return; } #endif 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) { m_stackSize = rhs.m_stackSize; m_skipStackPops = rhs.m_skipStackPops; 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 /** * @brief Error reporting. * * 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; luaL_error(L, "%s", s.c_str()); return 0; } #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_x(L, &detail::index_metamethod); rawsetfield(L, -2, "__index"); lua_pushcfunction_x(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_x(L, &tostringMetaMethod, 1); rawsetfield(L, -2, "__tostring"); #endif lua_pushcfunction_x(L, &detail::index_metamethod); rawsetfield(L, -2, "__index"); lua_pushcfunction_x(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, "%s", 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, "%s", 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) #if !defined(LUABRIDGE_ON_LUAU) lua_pushcfunction_x(L, &detail::gc_metamethod); // Stack: ns, co, function rawsetfield(L, -2, "__gc"); // co ["__gc"] = function. Stack: ns, co #endif ++m_stackSize; createClassTable(name); // Stack: ns, co, class table (cl) #if !defined(LUABRIDGE_ON_LUAU) lua_pushcfunction_x(L, &detail::gc_metamethod); // Stack: ns, co, cl, function rawsetfield(L, -2, "__gc"); // cl ["__gc"] = function. Stack: ns, co, cl #endif ++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) #if !defined(LUABRIDGE_ON_LUAU) lua_pushcfunction_x(L, &detail::gc_metamethod); // Stack: ns, co, function rawsetfield(L, -2, "__gc"); // co ["__gc"] = function. Stack: ns, co #endif ++m_stackSize; createClassTable(name); // Stack: ns, co, class table (cl) #if !defined(LUABRIDGE_ON_LUAU) lua_pushcfunction_x(L, &detail::gc_metamethod); // Stack: ns, co, cl, function rawsetfield(L, -2, "__gc"); // cl ["__gc"] = function. Stack: ns, co, cl #endif ++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 { lua_pop(L, 1); 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_x(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_x(L, &detail::property_setter::call, 1); // Stack: co, cl, st, ps, setter } else { lua_pushstring(L, name); // Stack: co, cl, st, name lua_pushcclosure_x(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_x(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_x(L, &detail::invoke_proxy_function, 1); // Stack: co, cl, st, setter } else { lua_pushstring(L, name); // Stack: co, cl, st, ps, name lua_pushcclosure_x(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_x(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_pushcclosure_x(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_x(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_x(L, sizeof(MemberPtrType))) MemberPtrType(mp); // Stack: co, cl, st, field ptr lua_pushcclosure_x(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_x(L, sizeof(MemberPtrType))) MemberPtrType(mp); // Stack: co, cl, st, field ptr lua_pushcclosure_x(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_x(L, sizeof(GetType))) GetType(get); // Stack: co, cl, st, funcion ptr lua_pushcclosure_x(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_x(L, sizeof(SetType))) SetType(set); // Stack: co, cl, st, function ptr lua_pushcclosure_x(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_x(L, sizeof(GetType))) GetType(get); // Stack: co, cl, st, funcion ptr lua_pushcclosure_x(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_x(L, sizeof(SetType))) SetType(set); // Stack: co, cl, st, function ptr lua_pushcclosure_x(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_x(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_x(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_x(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_x(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_pushcclosure_x(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_pushcclosure_x(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_pushcclosure_x(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_x(L, sizeof(MemFn))) MemFn(mf); lua_pushcclosure_x(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_x(L, sizeof(MemFn))) MemFn(mf); lua_pushcclosure_x(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_x(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_x(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_x(L, sizeof(mfp))) F(mfp); // Stack: co, cl, st, function ptr lua_pushcclosure_x(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_x(L, sizeof(mfp))) F(mfp); lua_pushcclosure_x(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_x(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_x(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_x(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, "%s", 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_pushcclosure_x(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_x(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_x(L, &detail::index_metamethod); rawsetfield(L, -2, "__index"); // Stack: pns, ns // ns.__newindex = newindex_static_metamethod lua_pushcfunction_x(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 variable. * * @param name The property name. * @param value A value pointer. * * @returns This namespace registration object. */ template Namespace& addVariable(const char* name, const T& value) { if (m_stackSize == 1) { throw_or_assert("addVariable() called on global namespace"); return *this; } assert(name != nullptr); assert(lua_istable(L, -1)); // Stack: namespace table (ns) std::error_code ec; if constexpr (std::is_enum_v) { using U = std::underlying_type_t; if (! Stack::push(L, static_cast(value), ec)) luaL_error(L, "%s", ec.message().c_str()); } else { if (! Stack::push(L, value, ec)) luaL_error(L, "%s", ec.message().c_str()); } rawsetfield(L, -2, name); // Stack: ns return *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_x(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_x(L, &detail::property_setter::call, 1); // Stack: ns, setter } else { lua_pushstring(L, name); // Stack: ns, ps, name lua_pushcclosure_x(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_x(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_x(L, &detail::invoke_proxy_function, 1); } else { lua_pushstring(L, name); lua_pushcclosure_x(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_pushcclosure_x(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_x(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_pushcclosure_x(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_x(L, get); // Stack: ns, getter detail::add_property_getter(L, name, -2); // Stack: ns if (set != nullptr) { lua_pushcfunction_x(L, set); // Stack: ns, setter detail::add_property_setter(L, name, -2); // Stack: ns } else { lua_pushstring(L, name); // Stack: ns, name lua_pushcclosure_x(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_pushcclosure_x(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_x(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_x(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 // 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; [[nodiscard]] static bool push(lua_State* L, const Type& list, std::error_code& ec) { #if LUABRIDGE_SAFE_STACK_CHECKS if (! lua_checkstack(L, 3)) { ec = makeErrorCode(ErrorCode::LuaStackOverflow); return false; } #endif 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; } [[nodiscard]] 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; } [[nodiscard]] 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; [[nodiscard]] static bool push(lua_State* L, const Type& array, std::error_code& ec) { #if LUABRIDGE_SAFE_STACK_CHECKS if (! lua_checkstack(L, 3)) { ec = makeErrorCode(ErrorCode::LuaStackOverflow); return false; } #endif 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; } [[nodiscard]] 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", static_cast(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; } [[nodiscard]] static bool isInstance(lua_State* L, int index) { return lua_istable(L, index) && get_length(L, index) == Size; } }; } // namespace luabridge // End File: Source/LuaBridge/Array.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 = 0) { 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