// https://github.com/kunitoki/LuaBridge3 // Copyright 2020, kunitoki // Copyright 2019, Dmitry Tarakanov // Copyright 2012, Vinnie Falco // Copyright 2007, Nathan Reed // SPDX-License-Identifier: MIT #pragma once #include "LuaHelpers.h" #include "Errors.h" #include "Expected.h" #include "Result.h" #include "Userdata.h" #include "Converter.h" #include #include #include #include #include #include #include #include #include #include #include #if LUABRIDGE_HAS_CXX17_FILESYSTEM #include #endif namespace luabridge { //================================================================================================= /** * @brief Stack restorer. */ class StackRestore final { public: StackRestore(lua_State* L) : m_L(L) , m_stackTop(lua_gettop(L)) { } ~StackRestore() { if (m_doRestoreStack) lua_settop(m_L, m_stackTop); } void reset() { m_doRestoreStack = false; } private: lua_State* const m_L = nullptr; int m_stackTop = 0; bool m_doRestoreStack = true; }; //================================================================================================= /** * @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 Result push(lua_State*) { return {}; } }; //================================================================================================= /** * @brief Specialization for nullptr_t. */ template <> struct Stack { [[nodiscard]] static Result push(lua_State* L, std::nullptr_t) { #if LUABRIDGE_SAFE_STACK_CHECKS if (! lua_checkstack(L, 1)) return makeErrorCode(ErrorCode::LuaStackOverflow); #endif lua_pushnil(L); return {}; } [[nodiscard]] static TypeResult get(lua_State* L, int index) { if (! lua_isnil(L, index)) return makeErrorCode(ErrorCode::InvalidTypeCast); 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 TypeResult get(lua_State* L, int) { return L; } }; //================================================================================================= /** * @brief Stack specialization for a lua_CFunction. */ template <> struct Stack { [[nodiscard]] static Result push(lua_State* L, lua_CFunction f) { #if LUABRIDGE_SAFE_STACK_CHECKS if (! lua_checkstack(L, 1)) return makeErrorCode(ErrorCode::LuaStackOverflow); #endif lua_pushcfunction_x(L, f, ""); return {}; } [[nodiscard]] static TypeResult get(lua_State* L, int index) { if (! lua_iscfunction(L, index)) return makeErrorCode(ErrorCode::InvalidTypeCast); return lua_tocfunction(L, index); } [[nodiscard]] static bool isInstance(lua_State* L, int index) { return lua_iscfunction(L, index) != 0; } }; //================================================================================================= /** * @brief Stack specialization for `bool`. */ template <> struct Stack { [[nodiscard]] static Result push(lua_State* L, bool value) { #if LUABRIDGE_SAFE_STACK_CHECKS if (! lua_checkstack(L, 1)) return makeErrorCode(ErrorCode::LuaStackOverflow); #endif lua_pushboolean(L, value ? 1 : 0); return {}; } [[nodiscard]] static TypeResult get(lua_State* L, int index) { #if LUABRIDGE_STRICT_STACK_CONVERSIONS if (lua_type(L, index) != LUA_TBOOLEAN) return makeErrorCode(ErrorCode::InvalidTypeCast); #endif 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 Result push(lua_State* L, std::byte value) { #if LUABRIDGE_SAFE_STACK_CHECKS if (! lua_checkstack(L, 1)) return makeErrorCode(ErrorCode::LuaStackOverflow); #endif pushunsigned(L, std::to_integer>(value)); return {}; } [[nodiscard]] static TypeResult get(lua_State* L, int index) { if (lua_type(L, index) != LUA_TNUMBER) return makeErrorCode(ErrorCode::InvalidTypeCast); if (! is_integral_representable_by(L, index)) return makeErrorCode(ErrorCode::IntegerDoesntFitIntoLuaInteger); return static_cast(lua_tointeger(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 Result push(lua_State* L, char value) { #if LUABRIDGE_SAFE_STACK_CHECKS if (! lua_checkstack(L, 1)) return makeErrorCode(ErrorCode::LuaStackOverflow); #endif lua_pushlstring(L, &value, 1); return {}; } [[nodiscard]] static TypeResult get(lua_State* L, int index) { if (lua_type(L, index) != LUA_TSTRING) return makeErrorCode(ErrorCode::InvalidTypeCast); std::size_t length = 0; const char* str = lua_tolstring(L, index, &length); if (str == nullptr || length != 1) return makeErrorCode(ErrorCode::InvalidTypeCast); return str[0]; } [[nodiscard]] static bool isInstance(lua_State* L, int index) { if (lua_type(L, index) == LUA_TSTRING) { std::size_t len = 0; lua_tolstring(L, index, &len); return len == 1; } return false; } }; //================================================================================================= /** * @brief Stack specialization for `int8_t`. */ #if !defined(__BORLANDC__) template <> struct Stack { static_assert(sizeof(int8_t) < sizeof(lua_Integer)); [[nodiscard]] static Result push(lua_State* L, int8_t value) { #if LUABRIDGE_SAFE_STACK_CHECKS if (! lua_checkstack(L, 1)) return makeErrorCode(ErrorCode::LuaStackOverflow); #endif lua_pushinteger(L, static_cast(value)); return {}; } [[nodiscard]] static TypeResult get(lua_State* L, int index) { if (lua_type(L, index) != LUA_TNUMBER) return makeErrorCode(ErrorCode::InvalidTypeCast); if (! is_integral_representable_by(L, index)) return makeErrorCode(ErrorCode::IntegerDoesntFitIntoLuaInteger); return static_cast(lua_tointeger(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; } }; #endif //================================================================================================= /** * @brief Stack specialization for `unsigned char`. */ template <> struct Stack { static_assert(sizeof(unsigned char) < sizeof(lua_Integer)); [[nodiscard]] static Result push(lua_State* L, unsigned char value) { #if LUABRIDGE_SAFE_STACK_CHECKS if (! lua_checkstack(L, 1)) return makeErrorCode(ErrorCode::LuaStackOverflow); #endif pushunsigned(L, value); return {}; } [[nodiscard]] static TypeResult get(lua_State* L, int index) { if (lua_type(L, index) != LUA_TNUMBER) return makeErrorCode(ErrorCode::InvalidTypeCast); if (! is_integral_representable_by(L, index)) return makeErrorCode(ErrorCode::IntegerDoesntFitIntoLuaInteger); return static_cast(lua_tointeger(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 Result push(lua_State* L, short value) { #if LUABRIDGE_SAFE_STACK_CHECKS if (! lua_checkstack(L, 1)) return makeErrorCode(ErrorCode::LuaStackOverflow); #endif lua_pushinteger(L, static_cast(value)); return {}; } [[nodiscard]] static TypeResult get(lua_State* L, int index) { if (lua_type(L, index) != LUA_TNUMBER) return makeErrorCode(ErrorCode::InvalidTypeCast); if (! is_integral_representable_by(L, index)) return makeErrorCode(ErrorCode::IntegerDoesntFitIntoLuaInteger); return static_cast(lua_tointeger(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 Result push(lua_State* L, unsigned short value) { #if LUABRIDGE_SAFE_STACK_CHECKS if (! lua_checkstack(L, 1)) return makeErrorCode(ErrorCode::LuaStackOverflow); #endif pushunsigned(L, value); return {}; } [[nodiscard]] static TypeResult get(lua_State* L, int index) { if (lua_type(L, index) != LUA_TNUMBER) return makeErrorCode(ErrorCode::InvalidTypeCast); if (! is_integral_representable_by(L, index)) return makeErrorCode(ErrorCode::IntegerDoesntFitIntoLuaInteger); return static_cast(lua_tointeger(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 Result push(lua_State* L, int value) { #if LUABRIDGE_SAFE_STACK_CHECKS if (! lua_checkstack(L, 1)) return makeErrorCode(ErrorCode::LuaStackOverflow); #endif if (! is_integral_representable_by(value)) return makeErrorCode(ErrorCode::IntegerDoesntFitIntoLuaInteger); lua_pushinteger(L, static_cast(value)); return {}; } [[nodiscard]] static TypeResult get(lua_State* L, int index) { if (lua_type(L, index) != LUA_TNUMBER) return makeErrorCode(ErrorCode::InvalidTypeCast); if (! is_integral_representable_by(L, index)) return makeErrorCode(ErrorCode::IntegerDoesntFitIntoLuaInteger); return static_cast(lua_tointeger(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 Result push(lua_State* L, unsigned int value) { #if LUABRIDGE_SAFE_STACK_CHECKS if (! lua_checkstack(L, 1)) return makeErrorCode(ErrorCode::LuaStackOverflow); #endif if (! is_integral_representable_by(value)) return makeErrorCode(ErrorCode::IntegerDoesntFitIntoLuaInteger); pushunsigned(L, value); return {}; } [[nodiscard]] static TypeResult get(lua_State* L, int index) { if (lua_type(L, index) != LUA_TNUMBER) return makeErrorCode(ErrorCode::InvalidTypeCast); if (! is_integral_representable_by(L, index)) return makeErrorCode(ErrorCode::IntegerDoesntFitIntoLuaInteger); return static_cast(lua_tointeger(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 Result push(lua_State* L, long value) { #if LUABRIDGE_SAFE_STACK_CHECKS if (! lua_checkstack(L, 1)) return makeErrorCode(ErrorCode::LuaStackOverflow); #endif if (! is_integral_representable_by(value)) return makeErrorCode(ErrorCode::IntegerDoesntFitIntoLuaInteger); lua_pushinteger(L, static_cast(value)); return {}; } [[nodiscard]] static TypeResult get(lua_State* L, int index) { if (lua_type(L, index) != LUA_TNUMBER) return makeErrorCode(ErrorCode::InvalidTypeCast); if (! is_integral_representable_by(L, index)) return makeErrorCode(ErrorCode::IntegerDoesntFitIntoLuaInteger); return static_cast(lua_tointeger(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 Result push(lua_State* L, unsigned long value) { #if LUABRIDGE_SAFE_STACK_CHECKS if (! lua_checkstack(L, 1)) return makeErrorCode(ErrorCode::LuaStackOverflow); #endif if (! is_integral_representable_by(value)) return makeErrorCode(ErrorCode::IntegerDoesntFitIntoLuaInteger); pushunsigned(L, value); return {}; } [[nodiscard]] static TypeResult get(lua_State* L, int index) { if (lua_type(L, index) != LUA_TNUMBER) return makeErrorCode(ErrorCode::InvalidTypeCast); if (! is_integral_representable_by(L, index)) return makeErrorCode(ErrorCode::IntegerDoesntFitIntoLuaInteger); return static_cast(lua_tointeger(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 Result push(lua_State* L, long long value) { #if LUABRIDGE_SAFE_STACK_CHECKS if (! lua_checkstack(L, 1)) return makeErrorCode(ErrorCode::LuaStackOverflow); #endif if (! is_integral_representable_by(value)) return makeErrorCode(ErrorCode::IntegerDoesntFitIntoLuaInteger); lua_pushinteger(L, static_cast(value)); return {}; } [[nodiscard]] static TypeResult get(lua_State* L, int index) { if (lua_type(L, index) != LUA_TNUMBER) return makeErrorCode(ErrorCode::InvalidTypeCast); if (! is_integral_representable_by(L, index)) return makeErrorCode(ErrorCode::IntegerDoesntFitIntoLuaInteger); return static_cast(lua_tointeger(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 Result push(lua_State* L, unsigned long long value) { #if LUABRIDGE_SAFE_STACK_CHECKS if (! lua_checkstack(L, 1)) return makeErrorCode(ErrorCode::LuaStackOverflow); #endif if (! is_integral_representable_by(value)) return makeErrorCode(ErrorCode::IntegerDoesntFitIntoLuaInteger); pushunsigned(L, value); return {}; } [[nodiscard]] static TypeResult get(lua_State* L, int index) { if (lua_type(L, index) != LUA_TNUMBER) return makeErrorCode(ErrorCode::InvalidTypeCast); if (! is_integral_representable_by(L, index)) return makeErrorCode(ErrorCode::IntegerDoesntFitIntoLuaInteger); return static_cast(lua_tointeger(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; } }; #if 0 // defined(__SIZEOF_INT128__) //================================================================================================= /** * @brief Stack specialization for `__int128_t`. */ template <> struct Stack<__int128_t> { [[nodiscard]] static Result push(lua_State* L, __int128_t value) { #if LUABRIDGE_SAFE_STACK_CHECKS if (! lua_checkstack(L, 1)) return makeErrorCode(ErrorCode::LuaStackOverflow); #endif if (! is_integral_representable_by(value)) return makeErrorCode(ErrorCode::IntegerDoesntFitIntoLuaInteger); lua_pushinteger(L, static_cast(value)); return {}; } [[nodiscard]] static TypeResult<__int128_t> get(lua_State* L, int index) { if (lua_type(L, index) != LUA_TNUMBER) return makeErrorCode(ErrorCode::InvalidTypeCast); if (! is_integral_representable_by<__int128_t>(L, index)) return makeErrorCode(ErrorCode::IntegerDoesntFitIntoLuaInteger); return static_cast<__int128_t>(lua_tointeger(L, index)); } [[nodiscard]] static bool isInstance(lua_State* L, int index) { if (lua_type(L, index) == LUA_TNUMBER) return is_integral_representable_by<__int128_t>(L, index); return false; } }; //================================================================================================= /** * @brief Stack specialization for `__uint128_t`. */ template <> struct Stack<__uint128_t> { [[nodiscard]] static Result push(lua_State* L, __uint128_t value) { #if LUABRIDGE_SAFE_STACK_CHECKS if (! lua_checkstack(L, 1)) return makeErrorCode(ErrorCode::LuaStackOverflow); #endif if (! is_integral_representable_by(value)) return makeErrorCode(ErrorCode::IntegerDoesntFitIntoLuaInteger); lua_pushinteger(L, static_cast(value)); return {}; } [[nodiscard]] static TypeResult<__uint128_t> get(lua_State* L, int index) { if (lua_type(L, index) != LUA_TNUMBER) return makeErrorCode(ErrorCode::InvalidTypeCast); if (! is_integral_representable_by<__uint128_t>(L, index)) return makeErrorCode(ErrorCode::IntegerDoesntFitIntoLuaInteger); return static_cast<__uint128_t>(lua_tointeger(L, index)); } [[nodiscard]] static bool isInstance(lua_State* L, int index) { if (lua_type(L, index) == LUA_TNUMBER) return is_integral_representable_by<__uint128_t>(L, index); return false; } }; #endif //================================================================================================= /** * @brief Stack specialization for `float`. */ template <> struct Stack { [[nodiscard]] static Result push(lua_State* L, float value) { #if LUABRIDGE_SAFE_STACK_CHECKS if (! lua_checkstack(L, 1)) return makeErrorCode(ErrorCode::LuaStackOverflow); #endif if (! is_floating_point_representable_by(value)) return makeErrorCode(ErrorCode::FloatingPointDoesntFitIntoLuaNumber); lua_pushnumber(L, static_cast(value)); return {}; } [[nodiscard]] static TypeResult get(lua_State* L, int index) { if (lua_type(L, index) != LUA_TNUMBER) return makeErrorCode(ErrorCode::InvalidTypeCast); if (! is_floating_point_representable_by(L, index)) return makeErrorCode(ErrorCode::FloatingPointDoesntFitIntoLuaNumber); return static_cast(lua_tonumber(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 Result push(lua_State* L, double value) { #if LUABRIDGE_SAFE_STACK_CHECKS if (! lua_checkstack(L, 1)) return makeErrorCode(ErrorCode::LuaStackOverflow); #endif if (! is_floating_point_representable_by(value)) return makeErrorCode(ErrorCode::FloatingPointDoesntFitIntoLuaNumber); lua_pushnumber(L, static_cast(value)); return {}; } [[nodiscard]] static TypeResult get(lua_State* L, int index) { if (lua_type(L, index) != LUA_TNUMBER) return makeErrorCode(ErrorCode::InvalidTypeCast); if (! is_floating_point_representable_by(L, index)) return makeErrorCode(ErrorCode::FloatingPointDoesntFitIntoLuaNumber); return static_cast(lua_tonumber(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 Result push(lua_State* L, long double value) { #if LUABRIDGE_SAFE_STACK_CHECKS if (! lua_checkstack(L, 1)) return makeErrorCode(ErrorCode::LuaStackOverflow); #endif if (! is_floating_point_representable_by(value)) return makeErrorCode(ErrorCode::FloatingPointDoesntFitIntoLuaNumber); lua_pushnumber(L, static_cast(value)); return {}; } [[nodiscard]] static TypeResult get(lua_State* L, int index) { if (lua_type(L, index) != LUA_TNUMBER) return makeErrorCode(ErrorCode::InvalidTypeCast); if (! is_floating_point_representable_by(L, index)) return makeErrorCode(ErrorCode::FloatingPointDoesntFitIntoLuaNumber); return static_cast(lua_tonumber(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 Result push(lua_State* L, const char* str) { #if LUABRIDGE_SAFE_STACK_CHECKS if (! lua_checkstack(L, 1)) return makeErrorCode(ErrorCode::LuaStackOverflow); #endif if (str != nullptr) lua_pushstring(L, str); else lua_pushnil(L); return {}; } [[nodiscard]] static TypeResult get(lua_State* L, int index) { const bool isNil = lua_isnil(L, index); if (!isNil && lua_type(L, index) != LUA_TSTRING) return makeErrorCode(ErrorCode::InvalidTypeCast); if (isNil) return nullptr; std::size_t length = 0; const char* str = lua_tolstring(L, index, &length); if (str == nullptr) return makeErrorCode(ErrorCode::InvalidTypeCast); return str; } [[nodiscard]] static bool isInstance(lua_State* L, int index) { return lua_isnil(L, index) || lua_type(L, index) == LUA_TSTRING; } }; //================================================================================================= /** * @brief Stack specialization for `std::string_view`. */ template <> struct Stack { [[nodiscard]] static Result push(lua_State* L, std::string_view str) { #if LUABRIDGE_SAFE_STACK_CHECKS if (! lua_checkstack(L, 1)) return makeErrorCode(ErrorCode::LuaStackOverflow); #endif lua_pushlstring(L, str.data(), str.size()); return {}; } [[nodiscard]] static TypeResult get(lua_State* L, int index) { if (lua_type(L, index) != LUA_TSTRING) return makeErrorCode(ErrorCode::InvalidTypeCast); std::size_t length = 0; const char* str = lua_tolstring(L, index, &length); if (str == nullptr) return makeErrorCode(ErrorCode::InvalidTypeCast); return std::string_view{ str, length }; } [[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 Result push(lua_State* L, const std::string& str) { #if LUABRIDGE_SAFE_STACK_CHECKS if (! lua_checkstack(L, 1)) return makeErrorCode(ErrorCode::LuaStackOverflow); #endif lua_pushlstring(L, str.data(), str.size()); return {}; } [[nodiscard]] static TypeResult get(lua_State* L, int index) { std::size_t length = 0; const char* str = nullptr; if (lua_type(L, index) == LUA_TSTRING) { str = lua_tolstring(L, index, &length); } #if !LUABRIDGE_STRICT_STACK_CONVERSIONS else { #if LUABRIDGE_SAFE_STACK_CHECKS if (! lua_checkstack(L, 1)) return makeErrorCode(ErrorCode::LuaStackOverflow); #endif // 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); str = lua_tolstring(L, -1, &length); lua_pop(L, 1); } #endif if (str == nullptr) return makeErrorCode(ErrorCode::InvalidTypeCast); return std::string{ str, length }; } [[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 Result push(lua_State* L, const Type& value) { if (value) { StackRestore stackRestore(L); auto result = Stack::push(L, *value); if (! result) return result; stackRestore.reset(); return {}; } #if LUABRIDGE_SAFE_STACK_CHECKS if (! lua_checkstack(L, 1)) return makeErrorCode(ErrorCode::LuaStackOverflow); #endif lua_pushnil(L); return {}; } [[nodiscard]] static TypeResult get(lua_State* L, int index) { const auto type = lua_type(L, index); if (type == LUA_TNIL || type == LUA_TNONE) return std::nullopt; auto result = Stack::get(L, index); if (! result) return result.error(); return *result; } [[nodiscard]] static bool isInstance(lua_State* L, int index) { const auto type = lua_type(L, index); return (type == LUA_TNIL || type == LUA_TNONE) || Stack::isInstance(L, index); } }; //================================================================================================= /** * @brief Stack specialization for `luabridge::Expected`. */ template struct Stack> { using Type = Expected; [[nodiscard]] static Result push(lua_State* L, const Type& value) { if (value.hasValue()) { StackRestore stackRestore(L); auto result = Stack::push(L, *value); if (! result) return result; stackRestore.reset(); return {}; } #if LUABRIDGE_SAFE_STACK_CHECKS if (! lua_checkstack(L, 1)) return makeErrorCode(ErrorCode::LuaStackOverflow); #endif lua_pushnil(L); return {}; } [[nodiscard]] static TypeResult get(lua_State* L, int index) { const auto type = lua_type(L, index); if (type == LUA_TNIL || type == LUA_TNONE) return makeErrorCode(ErrorCode::InvalidTypeCast); auto result = Stack::get(L, index); if (! result) return result.error(); return Type(*result); } [[nodiscard]] static bool isInstance(lua_State* L, int index) { const auto type = lua_type(L, index); return (type != LUA_TNIL && type != LUA_TNONE) && Stack::isInstance(L, index); } }; //================================================================================================= /** * @brief Stack specialization for `std::pair`. */ template struct Stack> { [[nodiscard]] static Result push(lua_State* L, const std::pair& t) { #if LUABRIDGE_SAFE_STACK_CHECKS if (! lua_checkstack(L, 3)) return makeErrorCode(ErrorCode::LuaStackOverflow); #endif StackRestore stackRestore(L); lua_createtable(L, 2, 0); auto result1 = push_element<0>(L, t); if (! result1) return result1; auto result2 = push_element<1>(L, t); if (! result2) return result2; stackRestore.reset(); return {}; } [[nodiscard]] static TypeResult> get(lua_State* L, int index) { const StackRestore stackRestore(L); if (!lua_istable(L, index)) return makeErrorCode(ErrorCode::InvalidTypeCast); if (get_length(L, index) != 2) return makeErrorCode(ErrorCode::InvalidTableSizeInCast); std::pair value; int absIndex = lua_absindex(L, index); lua_pushnil(L); auto result1 = pop_element<0>(L, absIndex, value); if (! result1) return result1.error(); auto result2 = pop_element<1>(L, absIndex, value); if (! result2) return result2.error(); return value; } [[nodiscard]] static bool isInstance(lua_State* L, int index) { return lua_type(L, index) == LUA_TTABLE && get_length(L, index) == 2; } private: template static Result push_element(lua_State* L, const std::pair& p) { static_assert(Index < 2); using T = std::tuple_element_t>; lua_pushinteger(L, static_cast(Index + 1)); auto result = Stack::push(L, std::get(p)); if (! result) return result; lua_settable(L, -3); return {}; } template static Result pop_element(lua_State* L, int absIndex, std::pair& p) { static_assert(Index < 2); using T = std::tuple_element_t>; if (lua_next(L, absIndex) == 0) return makeErrorCode(ErrorCode::InvalidTypeCast); auto result = Stack::get(L, -1); if (! result) return makeErrorCode(ErrorCode::InvalidTypeCast); std::get(p) = std::move(*result); lua_pop(L, 1); return {}; } }; //================================================================================================= /** * @brief Stack specialization for `std::tuple`. */ template struct Stack> { [[nodiscard]] static Result push(lua_State* L, const std::tuple& t) { #if LUABRIDGE_SAFE_STACK_CHECKS if (! lua_checkstack(L, 3)) return makeErrorCode(ErrorCode::LuaStackOverflow); #endif StackRestore stackRestore(L); lua_createtable(L, static_cast(Size), 0); auto result = push_element(L, t); if (! result) return result; stackRestore.reset(); return {}; } [[nodiscard]] static TypeResult> get(lua_State* L, int index) { const StackRestore stackRestore(L); if (!lua_istable(L, index)) return makeErrorCode(ErrorCode::InvalidTypeCast); if (get_length(L, index) != static_cast(Size)) return makeErrorCode(ErrorCode::InvalidTableSizeInCast); std::tuple value; int absIndex = lua_absindex(L, index); lua_pushnil(L); auto result = pop_element(L, absIndex, value); if (! result) return result.error(); 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::enable_if_t { return {}; } template static auto push_element(lua_State* L, const std::tuple& t) -> std::enable_if_t { using T = std::tuple_element_t>; lua_pushinteger(L, static_cast(Index + 1)); auto result = Stack::push(L, std::get(t)); if (! result) return result; lua_settable(L, -3); return push_element(L, t); } template static auto pop_element(lua_State*, int, std::tuple&) -> std::enable_if_t { return {}; } 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 makeErrorCode(ErrorCode::InvalidTypeCast); auto result = Stack::get(L, -1); if (! result) return makeErrorCode(ErrorCode::InvalidTypeCast); std::get(t) = std::move(*result); lua_pop(L, 1); return pop_element(L, absIndex, t); } }; //================================================================================================= /** * @brief Stack specialization for `T[N]`. */ template struct Stack { static_assert(N > 0, "Unsupported zero sized array"); [[nodiscard]] static Result push(lua_State* L, const T (&value)[N]) { if constexpr (std::is_same_v) { #if LUABRIDGE_SAFE_STACK_CHECKS if (! lua_checkstack(L, 1)) return makeErrorCode(ErrorCode::LuaStackOverflow); #endif lua_pushlstring(L, value, N - 1); return {}; } #if LUABRIDGE_SAFE_STACK_CHECKS if (! lua_checkstack(L, 2)) return makeErrorCode(ErrorCode::LuaStackOverflow); #endif StackRestore stackRestore(L); lua_createtable(L, static_cast(N), 0); for (std::size_t i = 0; i < N; ++i) { lua_pushinteger(L, static_cast(i + 1)); auto result = Stack::push(L, value[i]); if (! result) return result; lua_settable(L, -3); } stackRestore.reset(); return {}; } [[nodiscard]] static bool isInstance(lua_State* L, int index) { return lua_type(L, index) == LUA_TTABLE && get_length(L, index) == static_cast(N); } }; //================================================================================================= /** * @brief Specialization for `void*` and `const void*` type. */ template <> struct Stack { [[nodiscard]] static Result push(lua_State* L, void* ptr) { #if LUABRIDGE_SAFE_STACK_CHECKS if (! lua_checkstack(L, 1)) return makeErrorCode(ErrorCode::LuaStackOverflow); #endif lua_pushlightuserdata(L, ptr); return {}; } [[nodiscard]] static TypeResult get(lua_State* L, int index) { if (lua_isnil(L, index)) return nullptr; if (lua_islightuserdata(L, index)) return lua_touserdata(L, index); return makeErrorCode(ErrorCode::InvalidTypeCast); } [[nodiscard]] static bool isInstance(lua_State* L, int index) { return lua_islightuserdata(L, index) || lua_isnil(L, index); } }; template <> struct Stack { [[nodiscard]] static Result push(lua_State* L, const void* ptr) { #if LUABRIDGE_SAFE_STACK_CHECKS if (! lua_checkstack(L, 1)) return makeErrorCode(ErrorCode::LuaStackOverflow); #endif lua_pushlightuserdata(L, const_cast(ptr)); return {}; } [[nodiscard]] static TypeResult get(lua_State* L, int index) { if (lua_isnil(L, index)) return nullptr; if (lua_islightuserdata(L, index)) return lua_touserdata(L, index); return makeErrorCode(ErrorCode::InvalidTypeCast); } [[nodiscard]] static bool isInstance(lua_State* L, int index) { return lua_islightuserdata(L, index) || lua_isnil(L, index); } }; #if LUABRIDGE_HAS_CXX17_FILESYSTEM //================================================================================================= /** * @brief Stack specialization for `std::filesystem::path`. */ template <> struct Stack { [[nodiscard]] static Result push(lua_State* L, const std::filesystem::path& path) { #if LUABRIDGE_SAFE_STACK_CHECKS if (! lua_checkstack(L, 1)) return makeErrorCode(ErrorCode::LuaStackOverflow); #endif lua_pushlstring(L, path.string().c_str(), path.string().size()); return {}; } [[nodiscard]] static TypeResult get(lua_State* L, int index) { if (lua_type(L, index) != LUA_TSTRING) return makeErrorCode(ErrorCode::InvalidTypeCast); std::size_t len = 0; const char* str = lua_tolstring(L, index, &len); return std::filesystem::path(std::string(str, len)); } [[nodiscard]] static bool isInstance(lua_State* L, int index) { return lua_type(L, index) == LUA_TSTRING; } }; #endif // LUABRIDGE_HAS_CXX17_FILESYSTEM //================================================================================================= namespace detail { template struct StackOpSelector { using ReturnType = TypeResult; static Result push(lua_State* L, T* value) { return value ? Stack::push(L, *value) : Stack::push(L, nullptr); } 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 = TypeResult; static Result push(lua_State* L, const T* value) { return value ? Stack::push(L, *value) : Stack::push(L, nullptr); } 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 = TypeResult; static Result push(lua_State* L, T& value) { return Stack::push(L, value); } 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 = TypeResult; static Result push(lua_State* L, const T& value) { return Stack::push(L, value); } 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 Result push(lua_State* L, T* value) { return Helper::push(L, value); } [[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::isInstance(L, index); } }; template struct Stack { using Helper = detail::StackOpSelector::value>; using ReturnType = typename Helper::ReturnType; [[nodiscard]] static Result push(lua_State* L, const T* value) { return Helper::push(L, value); } [[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::isInstance(L, index); } }; template struct Stack && !std::is_const_v>> { using Helper = detail::StackOpSelector::value>; using ReturnType = typename Helper::ReturnType; [[nodiscard]] static Result push(lua_State* L, T& value) { return Helper::push(L, value); } [[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::isInstance(L, index); } }; template struct Stack && !StackConversion::enabled>> { using Helper = detail::StackOpSelector::value>; using ReturnType = typename Helper::ReturnType; [[nodiscard]] static Result push(lua_State* L, const T& value) { return Helper::push(L, value); } [[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::isInstance(L, index); } }; template struct Stack::enabled && !std::is_array_v>> { using ReturnType = TypeResult>; [[nodiscard]] static Result push(lua_State* L, const T& value) { return Stack::push(L, value); } [[nodiscard]] static ReturnType get(lua_State* L, int index) { if (detail::Userdata::isInstance(L, index)) { auto result = detail::Userdata::get(L, index, true); if (!result) return result.error(); if (T* ptr = *result) return detail::ConverterConstRef(*ptr); return detail::getNilBadArgError(L, index); } auto converted = detail::tryConvertFromRegisteredConverter(L, index); if (!converted) return converted.error(); return detail::ConverterConstRef(std::move(*converted)); } [[nodiscard]] static bool isInstance(lua_State* L, int index) { return Stack::isInstance(L, index); } }; template struct Stack::enabled>> { using IsUserdata = void; using ReturnType = TypeResult; [[nodiscard]] static Result push(lua_State* L, const T& value) { return detail::StackHelper::value>::push(L, value); } [[nodiscard]] static Result push(lua_State* L, T&& value) { return detail::StackHelper::value>::push(L, std::move(value)); } [[nodiscard]] static ReturnType get(lua_State* L, int index) { if (detail::Userdata::isInstance(L, index)) { auto result = detail::Userdata::get(L, index, true); if (result) { if (T* ptr = *result) return *ptr; } return result.error(); } return detail::tryConvertFromRegisteredConverter(L, index); } [[nodiscard]] static bool isInstance(lua_State* L, int index) { return detail::Userdata::isInstance(L, index); } }; //================================================================================================= /** * @brief Push an object onto the Lua stack. */ template [[nodiscard]] Result push(lua_State* L, const T& t) { return Stack::push(L, t); } //================================================================================================= /** * @brief Get an object from the Lua stack. */ template [[nodiscard]] TypeResult 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); } } // namespace luabridge