/* * LuaStack.h, part of VCMI engine * * Authors: listed in file AUTHORS in main folder * * License: GNU General Public License v2.0 or later * Full text of license available in license.txt file, in main folder * */ #pragma once #include "api/Registry.h" #include "../../lib/GameConstants.h" VCMI_LIB_NAMESPACE_BEGIN class JsonNode; class int3; namespace scripting { namespace detail { template struct IsRegularClass { static constexpr auto value = std::is_class_v && !std::is_base_of_v; }; template struct IsIdClass { static constexpr auto value = std::is_class_v && std::is_base_of_v; }; } class LuaStack { public: LuaStack(lua_State * L_); void balance(); void clear(); void pushByIndex(lua_Integer index); void pushNil(); void pushInteger(lua_Integer value); void push(bool value); void push(const char * value); void push(const std::string & value); void push(const JsonNode & value); template void push(const std::optional & value) { if(value.has_value()) push(value.value()); else pushNil(); } template && !std::is_same_v, int> = 0> void push(const T value) { pushInteger(static_cast(value)); } template, int> = 0> void push(const T value) { pushInteger(static_cast(value)); } void push(const int3 & value); template::value, int> = 0> void push(const T & value) { pushInteger(static_cast(value.getNum())); } template::value, int> = 0> void push(T * value) { using UData = T *; static auto KEY = api::TypeRegistry::get()->getKey(); if(!value) { pushNil(); return; } void * raw = lua_newuserdata(L, sizeof(UData)); if(!raw) { pushNil(); return; } auto * ptr = static_cast(raw); *ptr = value; luaL_getmetatable(L, KEY); lua_setmetatable(L, -2); } template::value, int> = 0> void push(std::shared_ptr value) { using UData = std::shared_ptr; static auto KEY = api::TypeRegistry::get()->getKey(); if(!value) { pushNil(); return; } void * raw = lua_newuserdata(L, sizeof(UData)); if(!raw) { pushNil(); return; } new(raw) UData(value); luaL_getmetatable(L, KEY); lua_setmetatable(L, -2); } template::value, int> = 0> void push(std::unique_ptr && value) { using UData = std::unique_ptr; static auto KEY = api::TypeRegistry::get()->getKey(); if(!value) { pushNil(); return; } void * raw = lua_newuserdata(L, sizeof(UData)); if(!raw) { pushNil(); return; } new(raw) UData(std::move(value)); luaL_getmetatable(L, KEY); lua_setmetatable(L, -2); } bool tryGetInteger(int position, lua_Integer & value); bool tryGet(int position, bool & value); template && !std::is_same_v, int> = 0> bool tryGet(int position, T & value) { lua_Integer temp; if(tryGetInteger(position, temp)) { value = static_cast(temp); return true; } else { return false; } } template::value, int> = 0> bool tryGet(int position, T & value) { lua_Integer temp; if(tryGetInteger(position, temp)) { value = T(temp); return true; } else { return false; } } template, int> = 0> bool tryGet(int position, T & value) { lua_Integer temp; if(tryGetInteger(position, temp)) { value = static_cast(temp); return true; } else { return false; } } bool tryGet(int position, int3 & value); bool tryGet(int position, double & value); bool tryGet(int position, std::string & value); template::value && std::is_const_v, int> = 0> STRONG_INLINE bool tryGet(int position, T * & value) { using NCValue = typename std::remove_const_t; using UData = NCValue *; using CUData = T *; return tryGetCUData(position, value); } template::value && !std::is_const_v, int> = 0> STRONG_INLINE bool tryGet(int position, T * & value) { return tryGetUData(position, value); } template::value && std::is_const_v, int> = 0> STRONG_INLINE bool tryGet(int position, std::shared_ptr & value) { using NCValue = typename std::remove_const_t; using UData = std::shared_ptr; using CUData = std::shared_ptr; return tryGetCUData, UData, CUData>(position, value); } template::value && !std::is_const_v, int> = 0> STRONG_INLINE bool tryGet(int position, std::shared_ptr & value) { return tryGetUData>(position, value); } template bool tryGetUData(int position, U & value) { static auto KEY = api::TypeRegistry::get()->getKey(); void * raw = lua_touserdata(L, position); if(!raw) return false; if(lua_getmetatable(L, position) == 0) return false; lua_getfield(L, LUA_REGISTRYINDEX, KEY); if(lua_rawequal(L, -1, -2) == 1) { value = *(static_cast(raw)); lua_pop(L, 2); return true; } lua_pop(L, 2); return false; } template bool tryGetCUData(int position, T & value) { static auto KEY = api::TypeRegistry::get()->getKey(); static auto C_KEY = api::TypeRegistry::get()->getKey(); void * raw = lua_touserdata(L, position); if(!raw) return false; if(lua_getmetatable(L, position) == 0) return false; //top is metatable lua_getfield(L, LUA_REGISTRYINDEX, KEY); if(lua_rawequal(L, -1, -2) == 1) { value = *(static_cast(raw)); lua_pop(L, 2); return true; } lua_pop(L, 1); //top is metatable lua_getfield(L, LUA_REGISTRYINDEX, C_KEY); if(lua_rawequal(L, -1, -2) == 1) { value = *(static_cast(raw)); lua_pop(L, 2); return true; } lua_pop(L, 2); return false; } bool tryGet(int position, JsonNode & value); int retNil(); int retVoid(); STRONG_INLINE int retPushed() { return lua_gettop(L); } inline bool isFunction(int position) { return lua_isfunction(L, position); } inline bool isNumber(int position) { return lua_isnumber(L, position); } static int quickRetBool(lua_State * L, bool value) { lua_settop(L, 0); lua_pushboolean(L, value); return 1; } template static int quickRetInt(lua_State * L, const T & value) { lua_settop(L, 0); lua_pushinteger(L, static_cast(value)); return 1; } template static int quickRetInt(lua_State * L, const std::bitset & value) { lua_settop(L, 0); lua_pushinteger(L, static_cast(value.to_ulong())); return 1; } static int quickRetStr(lua_State * L, const std::string & value) { lua_settop(L, 0); lua_pushlstring(L, value.c_str(), value.size()); return 1; } private: lua_State * L; int initialTop; }; } VCMI_LIB_NAMESPACE_END