#pragma once #include "debug.h" // try to detect if we are building against LuaJIT or MoonJIT #if defined(LUA_JITLIBNAME) #include "luajit.h" #if (defined(__x86_64__) || defined(_M_X64) || defined(__LP64__)) #define LUAJIT_FLAVOR() 64 #else // 64 bits #define LUAJIT_FLAVOR() 32 #endif // 64 bits #else // LUA_JITLIBNAME #define LUAJIT_FLAVOR() 0 #define LUA_JITLIBNAME "jit" #endif // LUA_JITLIBNAME #ifndef LUA_OK #define LUA_OK 0 // doesn't exist in Lua 5.1 #endif // LUA_OK #ifndef LUA_ERRGCMM #define LUA_ERRGCMM 666 // doesn't exist in Lua 5.1 and Lua 5.4, we don't care about the actual value #endif // LUA_ERRGCMM #ifndef LUA_LOADED_TABLE #define LUA_LOADED_TABLE "_LOADED" // doesn't exist before Lua 5.3 #endif // LUA_LOADED_TABLE // code is now preferring Lua 5.4 API // ################################################################################################# // a strong-typed wrapper over lua types to see them easier in a debugger enum class LuaType { NONE = LUA_TNONE, NIL = LUA_TNIL, BOOLEAN = LUA_TBOOLEAN, LIGHTUSERDATA = LUA_TLIGHTUSERDATA, NUMBER = LUA_TNUMBER, STRING = LUA_TSTRING, TABLE = LUA_TTABLE, FUNCTION = LUA_TFUNCTION, USERDATA = LUA_TUSERDATA, THREAD = LUA_TTHREAD, CDATA = 10 // LuaJIT CDATA }; // ################################################################################################# // add some Lua 5.3-style API when building for Lua 5.1 #if LUA_VERSION_NUM == 501 inline size_t lua_rawlen(lua_State* L_, int idx_) { return lua_objlen(L_, idx_); } void luaL_requiref(lua_State* L_, const char* modname_, lua_CFunction openf_, int glb_); // implementation copied from Lua 5.2 sources int luaL_getsubtable(lua_State* L_, int idx_, const char* fname_); #endif // LUA_VERSION_NUM == 501 // ################################################################################################# // wrap Lua 5.3 calls under Lua 5.1 API when it is simpler that way #if LUA_VERSION_NUM == 503 inline int luaL_optint(lua_State* L_, int n_, lua_Integer d_) { return static_cast(luaL_optinteger(L_, n_, d_)); } #endif // LUA_VERSION_NUM == 503 // ################################################################################################# #if LUA_VERSION_NUM < 504 void* lua_newuserdatauv(lua_State* L_, size_t sz_, int nuvalue_); int lua_getiuservalue(lua_State* L_, int idx_, int n_); int lua_setiuservalue(lua_State* L_, int idx_, int n_); #define LUA_GNAME "_G" #endif // LUA_VERSION_NUM < 504 // ################################################################################################# // wrap Lua 5.4 calls under Lua 5.1 API when it is simpler that way #if LUA_VERSION_NUM == 504 inline int luaL_optint(lua_State* L_, int n_, lua_Integer d_) { return static_cast(luaL_optinteger(L_, n_, d_)); } #endif // LUA_VERSION_NUM == 504 // ################################################################################################# // a strong-typed wrapper over lua error codes to see them easier in a debugger enum class LuaError { OK = LUA_OK, YIELD = LUA_YIELD, ERRRUN = LUA_ERRRUN, ERRSYNTAX = LUA_ERRSYNTAX, ERRMEM = LUA_ERRMEM, ERRGCMM = LUA_ERRGCMM, ERRERR = LUA_ERRERR, ERRFILE = LUA_ERRFILE }; inline constexpr LuaError ToLuaError(int const rc_) { assert(rc_ == LUA_OK || rc_ == LUA_YIELD || rc_ == LUA_ERRRUN || rc_ == LUA_ERRSYNTAX || rc_ == LUA_ERRMEM || rc_ == LUA_ERRGCMM || rc_ == LUA_ERRERR || rc_ == LUA_ERRFILE); return static_cast(rc_); } // ################################################################################################# // break lexical order for that one because it's needed below inline LuaType luaG_type(lua_State* const L_, int const idx_) { return static_cast(lua_type(L_, idx_)); } // ################################################################################################# // ################################################################################################# // All the compatibility wrappers we expose start with luaG_ // ################################################################################################# // ################################################################################################# // use this in place of lua_absindex to save a function call inline int luaG_absindex(lua_State* L_, int idx_) { return (((idx_) >= 0 || (idx_) <= LUA_REGISTRYINDEX) ? (idx_) : lua_gettop(L_) + (idx_) + 1); } // ################################################################################################# template concept RequiresOldLuaDump = requires(LUA_DUMP f_) { { f_(nullptr, nullptr, nullptr) } -> std::same_as; }; template static inline int WrapLuaDump(LUA_DUMP f_, lua_State* const L_, lua_Writer const writer_, void* const data_, [[maybe_unused]] int const strip_) { return f_(L_, writer_, data_); } // ------------------------------------------------------------------------------------------------- template concept RequiresNewLuaDump = requires(LUA_DUMP f_) { { f_(nullptr, nullptr, nullptr, 0) } -> std::same_as; }; template static inline int WrapLuaDump(LUA_DUMP f_, lua_State* const L_, lua_Writer const writer_, void* const data_, int const strip_) { return f_(L_, writer_, data_, strip_); } // ------------------------------------------------------------------------------------------------- static inline int luaG_dump(lua_State* const L_, lua_Writer const writer_, void* const data_, int const strip_) { return WrapLuaDump(lua_dump, L_, writer_, data_, strip_); } // ################################################################################################# int luaG_getalluservalues(lua_State* L_, int idx_); // ################################################################################################# template concept RequiresOldLuaGetfield = requires(LUA_GETFIELD f_) { { f_(nullptr, 0, nullptr) } -> std::same_as; }; template static inline int WrapLuaGetField(LUA_GETFIELD f_, lua_State* const L_, int const idx_, char const* const name_) { f_(L_, idx_, name_); return lua_type(L_, -1); } // ------------------------------------------------------------------------------------------------- template concept RequiresNewLuaGetfield = requires(LUA_GETFIELD f_) { { f_(nullptr, 0, nullptr) } -> std::same_as; }; template static inline int WrapLuaGetField(LUA_GETFIELD f_, lua_State* const L_, int const idx_, char const* const name_) { return f_(L_, idx_, name_); } // ------------------------------------------------------------------------------------------------- static inline LuaType luaG_getfield(lua_State* const L_, int const idx_, std::string_view const& name_) { return static_cast(WrapLuaGetField(lua_getfield, L_, idx_, name_.data())); } // ################################################################################################# LuaType luaG_getmodule(lua_State* L_, std::string_view const& name_); // ################################################################################################# inline void luaG_registerlibfuncs(lua_State* L_, luaL_Reg const* funcs_) { // fake externs to make clang happy... extern void luaL_register(lua_State*, char const*, luaL_Reg const*); // Lua 5.1 extern void luaL_setfuncs(lua_State* const L_, luaL_Reg const funcs_[], int nup_); // Lua 5.2+ if constexpr (LUA_VERSION_NUM == 501) { luaL_register(L_, nullptr, funcs_); } else { luaL_setfuncs(L_, funcs_, 0); } } // ################################################################################################# template static inline void luaG_newlib(lua_State* const L_, luaL_Reg const (&funcs_)[N]) { lua_createtable(L_, 0, N - 1); luaG_registerlibfuncs(L_, funcs_); } // ################################################################################################# template [[nodiscard]] T* luaG_newuserdatauv(lua_State* L_, int nuvalue_) { return static_cast(lua_newuserdatauv(L_, sizeof(T), nuvalue_)); } // ################################################################################################# inline void luaG_pushglobaltable(lua_State* const L_) { #ifdef LUA_GLOBALSINDEX // All flavors of Lua 5.1 ::lua_pushvalue(L_, LUA_GLOBALSINDEX); #else // LUA_GLOBALSINDEX ::lua_rawgeti(L_, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS); #endif // LUA_GLOBALSINDEX } // ################################################################################################# inline void luaG_setfield(lua_State* const L_, int const idx_, char const* k_) = delete; inline void luaG_setfield(lua_State* const L_, int const idx_, std::string_view const& k_) { lua_setfield(L_, idx_, k_.data()); } // ################################################################################################# inline void luaG_setmetatable(lua_State* const L_, std::string_view const& tname_) { // fake externs to make clang happy... extern void luaL_setmetatable(lua_State* const L_, char const* const tname_); // Lua 5.2+ if constexpr (LUA_VERSION_NUM == 501) { luaL_setmetatable(L_, tname_.data()); } else { luaL_getmetatable(L_, tname_.data()); lua_setmetatable(L_, -2); } } // ################################################################################################# // a small helper to extract a full userdata pointer from the stack in a safe way template [[nodiscard]] T* luaG_tofulluserdata(lua_State* const L_, int const index_) { LUA_ASSERT(L_, lua_isnil(L_, index_) || lua_type(L_, index_) == LUA_TUSERDATA); return static_cast(lua_touserdata(L_, index_)); } // ------------------------------------------------------------------------------------------------- template [[nodiscard]] auto luaG_tolightuserdata(lua_State* const L_, int const index_) { LUA_ASSERT(L_, lua_isnil(L_, index_) || lua_islightuserdata(L_, index_)); if constexpr (std::is_pointer_v) { return static_cast(lua_touserdata(L_, index_)); } else { return static_cast(lua_touserdata(L_, index_)); } } // ------------------------------------------------------------------------------------------------- [[nodiscard]] inline std::string_view luaG_typename(lua_State* const L_, LuaType const t_) { return lua_typename(L_, static_cast(t_)); } // ------------------------------------------------------------------------------------------------- [[nodiscard]] inline std::string_view luaG_typename(lua_State* const L_, int const idx_) { return luaG_typename(L_, luaG_type(L_, idx_)); } // ################################################################################################# // must keep as a macro as long as we do constant string concatenations #define STRINGVIEW_FMT "%.*s" // a replacement of lua_tolstring [[nodiscard]] inline std::string_view luaG_tostring(lua_State* const L_, int const idx_) { size_t _len{ 0 }; char const* _str{ lua_tolstring(L_, idx_, &_len) }; return std::string_view{ _str, _len }; } [[nodiscard]] inline std::string_view luaG_checkstring(lua_State* const L_, int const idx_) { size_t _len{ 0 }; char const* _str{ luaL_checklstring(L_, idx_, &_len) }; return std::string_view{ _str, _len }; } [[nodiscard]] inline std::string_view luaG_optstring(lua_State* const L_, int const idx_, std::string_view const& default_) { if (lua_isnoneornil(L_, idx_)) { return default_; } size_t _len{ 0 }; char const* _str{ luaL_optlstring(L_, idx_, default_.data(), &_len) }; return std::string_view{ _str, _len }; } template [[nodiscard]] inline std::string_view luaG_pushstring(lua_State* const L_, std::string_view const& str_, EXTRA&&... extra_) { if constexpr (sizeof...(EXTRA) == 0) { if constexpr (LUA_VERSION_NUM == 501) { // lua_pushlstring doesn't return a value in Lua 5.1 lua_pushlstring(L_, str_.data(), str_.size()); return luaG_tostring(L_, -1); } else { return std::string_view{ lua_pushlstring(L_, str_.data(), str_.size()), str_.size() }; } } else { static_assert((... && std::is_trivial_v>)); return std::string_view{ lua_pushfstring(L_, str_.data(), std::forward(extra_)...) }; } }