From 89c9c6fe46ce4a273a15b7f882c3d0935a868145 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Tue, 16 Aug 2016 11:55:12 +0200 Subject: [PATCH 1/3] cLuaState: Added support for optional params and AStringMap values. --- src/Bindings/LuaState.cpp | 76 +++++++++++++++++++++++++++++++++++++++ src/Bindings/LuaState.h | 39 +++++++++++++++++--- 2 files changed, 111 insertions(+), 4 deletions(-) diff --git a/src/Bindings/LuaState.cpp b/src/Bindings/LuaState.cpp index fd29e8e34..b5832802d 100644 --- a/src/Bindings/LuaState.cpp +++ b/src/Bindings/LuaState.cpp @@ -341,6 +341,33 @@ void cLuaState::cStackTable::ForEachArrayElement(std::function a_ElementCallback) const +{ + #ifdef _DEBUG + auto stackTop = lua_gettop(m_LuaState); + #endif + lua_pushvalue(m_LuaState, m_StackPos); // Stk: + lua_pushnil(m_LuaState); // Stk:
nil + while (lua_next(m_LuaState, -2)) // Stk:
+ { + auto shouldAbort = a_ElementCallback(m_LuaState); + ASSERT(lua_gettop(m_LuaState) == stackTop + 3); // The element callback must not change the Lua stack below the value + lua_pop(m_LuaState, 1); // Stk:
+ if (shouldAbort) + { + // The callback wants to abort + lua_pop(m_LuaState, 2); // Stk: empty + return; + } + } + // Stk:
+ lua_pop(m_LuaState, 1); // Stk: empty +} + + + + + //////////////////////////////////////////////////////////////////////////////// // cLuaState: @@ -748,6 +775,24 @@ void cLuaState::Push(const AString & a_String) +void cLuaState::Push(const AStringMap & a_Dictionary) +{ + ASSERT(IsValid()); + + lua_createtable(m_LuaState, 0, static_cast(a_Dictionary.size())); + int newTable = lua_gettop(m_LuaState); + for (const auto & item: a_Dictionary) + { + Push(item.first); // key + Push(item.second); // value + lua_rawset(m_LuaState, newTable); + } +} + + + + + void cLuaState::Push(const AStringVector & a_Vector) { ASSERT(IsValid()); @@ -1113,6 +1158,37 @@ bool cLuaState::GetStackValue(int a_StackPos, AString & a_Value) +bool cLuaState::GetStackValue(int a_StackPos, AStringMap & a_Value) +{ + // Retrieve all values in a string => string dictionary table: + if (!lua_istable(m_LuaState, a_StackPos)) + { + return false; + } + cStackTable tbl(*this, a_StackPos); + bool isValid = true; + tbl.ForEachElement([&isValid, &a_Value](cLuaState & a_LuaState) + { + AString key, val; + if (a_LuaState.GetStackValues(-2, key, val)) + { + a_Value[key] = val; + } + else + { + isValid = false; + return true; + } + return false; + } + ); + return isValid; +} + + + + + bool cLuaState::GetStackValue(int a_StackPos, bool & a_ReturnedVal) { a_ReturnedVal = (tolua_toboolean(m_LuaState, a_StackPos, a_ReturnedVal ? 1 : 0) > 0); diff --git a/src/Bindings/LuaState.h b/src/Bindings/LuaState.h index 60ae840b0..303a59327 100644 --- a/src/Bindings/LuaState.h +++ b/src/Bindings/LuaState.h @@ -309,6 +309,24 @@ public: typedef UniquePtr cTableRefPtr; + /** Represents a parameter that is optional - calling a GetStackValue() with this object will not fail if the value on the Lua stack is nil. + Note that the GetStackValue() will still fail if the param is present but of a different type. + The class itself is just a marker so that the template magic will select the correct GetStackValue() overload. */ + template + class cOptionalParam + { + public: + explicit cOptionalParam(T & a_Dest): + m_Dest(a_Dest) + { + } + + T & GetDest(void) { return m_Dest; } + + protected: + T & m_Dest; + }; + /** A dummy class that's used only to delimit function args from return values for cLuaState::Call() */ class cRet { @@ -475,6 +493,7 @@ public: // Push a const value onto the stack (keep alpha-sorted): void Push(const AString & a_String); + void Push(const AStringMap & a_Dictionary); void Push(const AStringVector & a_Vector); void Push(const cCraftingGrid * a_Grid); void Push(const cCraftingRecipe * a_Recipe); @@ -508,6 +527,7 @@ public: // Returns whether value was changed // Enum values are checked for their allowed values and fail if the value is not assigned. bool GetStackValue(int a_StackPos, AString & a_Value); + bool GetStackValue(int a_StackPos, AStringMap & a_Value); bool GetStackValue(int a_StackPos, bool & a_Value); bool GetStackValue(int a_StackPos, cCallback & a_Callback); bool GetStackValue(int a_StackPos, cCallbackPtr & a_Callback); @@ -549,6 +569,17 @@ public: return true; } + /** Retrieves an optional value on the stack - doesn't fail if the stack contains nil instead of the value. */ + template + bool GetStackValue(int a_StackPos, cOptionalParam && a_ReturnedVal) + { + if (lua_isnoneornil(m_LuaState, a_StackPos)) + { + return true; + } + return GetStackValue(a_StackPos, a_ReturnedVal.GetDest()); + } + /** Pushes the named value in the table at the top of the stack. a_Name may be a path containing multiple table levels, such as "cChatColor.Blue". If the value is found, it is pushed on top of the stack and the returned cStackValue is valid. @@ -606,14 +637,14 @@ public: } /** Retrieves a list of values from the Lua stack, starting at the specified index. */ - template - inline bool GetStackValues(int a_StartStackPos, T & a_Ret, Args &&... args) + template + inline bool GetStackValues(int a_StartStackPos, Arg1 && a_Arg1, Args &&... args) { - if (!GetStackValue(a_StartStackPos, a_Ret)) + if (!GetStackValue(a_StartStackPos, std::forward(a_Arg1))) { return false; } - return GetStackValues(a_StartStackPos + 1, args...); + return GetStackValues(a_StartStackPos + 1, std::forward(args)...); } /** Returns true if the specified parameters on the stack are of the specified usertable type; also logs warning if not. Used for static functions */ From 9493488e48c49a54e9b1ec892107735de6736c13 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Tue, 16 Aug 2016 13:02:08 +0200 Subject: [PATCH 2/3] cLuaState: Added direct support for pushing a nil constant. --- src/Bindings/LuaJson.cpp | 5 ++--- src/Bindings/LuaState.cpp | 25 +++++++++++++------------ src/Bindings/LuaState.h | 12 +++++++++--- src/Bindings/ManualBindings.cpp | 6 ++---- src/Bindings/ManualBindings_Network.cpp | 7 +++---- 5 files changed, 29 insertions(+), 26 deletions(-) diff --git a/src/Bindings/LuaJson.cpp b/src/Bindings/LuaJson.cpp index 39a720319..7a0ddb961 100644 --- a/src/Bindings/LuaJson.cpp +++ b/src/Bindings/LuaJson.cpp @@ -82,7 +82,7 @@ void PushJsonValue(const Json::Value & a_Value, cLuaState & a_LuaState) { case Json::nullValue: { - a_LuaState.PushNil(); + a_LuaState.Push(cLuaState::Nil); break; } @@ -228,8 +228,7 @@ static int tolua_cJson_Parse(lua_State * a_LuaState) Json::Reader reader; if (!reader.parse(input, root, false)) { - L.PushNil(); - L.Push(Printf("Parsing Json failed: %s", reader.getFormattedErrorMessages().c_str())); + L.Push(cLuaState::Nil, Printf("Parsing Json failed: %s", reader.getFormattedErrorMessages().c_str())); return 2; } diff --git a/src/Bindings/LuaState.cpp b/src/Bindings/LuaState.cpp index b5832802d..2e1415519 100644 --- a/src/Bindings/LuaState.cpp +++ b/src/Bindings/LuaState.cpp @@ -42,6 +42,7 @@ extern "C" const cLuaState::cRet cLuaState::Return = {}; +const cLuaState::cNil cLuaState::Nil = {}; /** Each Lua state stores a pointer to its creating cLuaState in Lua globals, under this name. This way any cLuaState can reference the main cLuaState's TrackedCallbacks, mutex etc. */ @@ -751,18 +752,6 @@ bool cLuaState::PushFunction(const cRef & a_TableRef, const char * a_FnName) -void cLuaState::PushNil(void) -{ - ASSERT(IsValid()); - - lua_pushnil(m_LuaState); - m_NumCurrentFunctionArgs += 1; -} - - - - - void cLuaState::Push(const AString & a_String) { ASSERT(IsValid()); @@ -860,6 +849,18 @@ void cLuaState::Push(const cItems & a_Items) +void cLuaState::Push(const cNil & a_Nil) +{ + ASSERT(IsValid()); + + lua_pushnil(m_LuaState); + m_NumCurrentFunctionArgs += 1; +} + + + + + void cLuaState::Push(const cPlayer * a_Player) { ASSERT(IsValid()); diff --git a/src/Bindings/LuaState.h b/src/Bindings/LuaState.h index 303a59327..3c07ac5d2 100644 --- a/src/Bindings/LuaState.h +++ b/src/Bindings/LuaState.h @@ -327,14 +327,21 @@ public: T & m_Dest; }; + /** A dummy class that's used only to delimit function args from return values for cLuaState::Call() */ class cRet { } ; - static const cRet Return; // Use this constant to delimit function args from return values for cLuaState::Call() + /** A dummy class that's used only to push a constant nil as a function parameter in Call(). */ + class cNil + { + }; + static const cNil Nil; // Use this constant to give a function a nil parameter in Call() + + /** A RAII class for values pushed onto the Lua stack. Will pop the value off the stack in the destructor. */ class cStackValue @@ -489,8 +496,6 @@ public: Push(std::forward(a_Arg2), std::forward(a_Args)...); } - void PushNil(void); - // Push a const value onto the stack (keep alpha-sorted): void Push(const AString & a_String); void Push(const AStringMap & a_Dictionary); @@ -499,6 +504,7 @@ public: void Push(const cCraftingRecipe * a_Recipe); void Push(const char * a_Value); void Push(const cItems & a_Items); + void Push(const cNil & a_Nil); void Push(const cPlayer * a_Player); void Push(const cRef & a_Ref); void Push(const HTTPRequest * a_Request); diff --git a/src/Bindings/ManualBindings.cpp b/src/Bindings/ManualBindings.cpp index 8927b0b19..038c67995 100644 --- a/src/Bindings/ManualBindings.cpp +++ b/src/Bindings/ManualBindings.cpp @@ -2058,8 +2058,7 @@ static int tolua_cUrlParser_Parse(lua_State * a_LuaState) if (!res.first) { // Error, return nil and error msg: - L.PushNil(); - L.Push(res.second); + L.Push(cLuaState::Nil, res.second); return 2; } L.Push(scheme, username, password, host, port, path, query, fragment); @@ -2099,8 +2098,7 @@ static int tolua_cUrlParser_ParseAuthorityPart(lua_State * a_LuaState) if (!res.first) { // Error, return nil and error msg: - L.PushNil(); - L.Push(res.second); + L.Push(cLuaState::Nil, res.second); return 2; } L.Push(username, password, host, port); diff --git a/src/Bindings/ManualBindings_Network.cpp b/src/Bindings/ManualBindings_Network.cpp index 68eba5870..c8565d23d 100644 --- a/src/Bindings/ManualBindings_Network.cpp +++ b/src/Bindings/ManualBindings_Network.cpp @@ -649,8 +649,7 @@ static int tolua_cTCPLink_StartTLSClient(lua_State * L) AString res = Link->StartTLSClient(OwnCert, OwnPrivKey, OwnPrivKeyPassword); if (!res.empty()) { - S.PushNil(); - S.Push(Printf("Cannot start TLS on link to %s:%d: %s", Link->GetRemoteIP().c_str(), Link->GetRemotePort(), res.c_str())); + S.Push(cLuaState::Nil, Printf("Cannot start TLS on link to %s:%d: %s", Link->GetRemoteIP().c_str(), Link->GetRemotePort(), res.c_str())); return 2; } return 1; @@ -695,10 +694,10 @@ static int tolua_cTCPLink_StartTLSServer(lua_State * L) AString res = Link->StartTLSServer(OwnCert, OwnPrivKey, OwnPrivKeyPassword, StartTLSData); if (!res.empty()) { - S.PushNil(); - S.Push(Printf("Cannot start TLS on link to %s:%d: %s", Link->GetRemoteIP().c_str(), Link->GetRemotePort(), res.c_str())); + S.Push(cLuaState::Nil, Printf("Cannot start TLS on link to %s:%d: %s", Link->GetRemoteIP().c_str(), Link->GetRemotePort(), res.c_str())); return 2; } + S.Push(true); return 1; } From 11682d1386299d78bab39f77884797981950edee Mon Sep 17 00:00:00 2001 From: Mattes D Date: Tue, 16 Aug 2016 14:05:03 +0200 Subject: [PATCH 3/3] cLuaState: Moved function param counting to PushCallPop() template. The Push() functions can be used not only for function params, but also returns or temporaries, so it doesn't make sense to count the params there. --- src/Bindings/BindingsProcessor.lua | 1 - src/Bindings/LuaState.cpp | 27 --------------------------- src/Bindings/LuaState.h | 2 ++ 3 files changed, 2 insertions(+), 28 deletions(-) diff --git a/src/Bindings/BindingsProcessor.lua b/src/Bindings/BindingsProcessor.lua index f936d3a68..df479a634 100644 --- a/src/Bindings/BindingsProcessor.lua +++ b/src/Bindings/BindingsProcessor.lua @@ -234,7 +234,6 @@ local function OutputLuaStateHelpers(a_Package) if not(g_HasCustomPushImplementation[item.name]) then f:write("void cLuaState::Push(" .. item.name .. " * a_Value)\n{\n\tASSERT(IsValid());\n") f:write("\ttolua_pushusertype(m_LuaState, a_Value, \"" .. item.name .. "\");\n"); - f:write("\tm_NumCurrentFunctionArgs += 1;\n") f:write("}\n\n\n\n\n\n") end end diff --git a/src/Bindings/LuaState.cpp b/src/Bindings/LuaState.cpp index 2e1415519..ec6bdb48a 100644 --- a/src/Bindings/LuaState.cpp +++ b/src/Bindings/LuaState.cpp @@ -757,7 +757,6 @@ void cLuaState::Push(const AString & a_String) ASSERT(IsValid()); lua_pushlstring(m_LuaState, a_String.data(), a_String.size()); - m_NumCurrentFunctionArgs += 1; } @@ -794,7 +793,6 @@ void cLuaState::Push(const AStringVector & a_Vector) tolua_pushstring(m_LuaState, itr->c_str()); lua_rawseti(m_LuaState, newTable, index); } - m_NumCurrentFunctionArgs += 1; } @@ -806,7 +804,6 @@ void cLuaState::Push(const cCraftingGrid * a_Grid) ASSERT(IsValid()); tolua_pushusertype(m_LuaState, reinterpret_cast(const_cast(a_Grid)), "cCraftingGrid"); - m_NumCurrentFunctionArgs += 1; } @@ -818,7 +815,6 @@ void cLuaState::Push(const cCraftingRecipe * a_Recipe) ASSERT(IsValid()); tolua_pushusertype(m_LuaState, reinterpret_cast(const_cast(a_Recipe)), "cCraftingRecipe"); - m_NumCurrentFunctionArgs += 1; } @@ -830,7 +826,6 @@ void cLuaState::Push(const char * a_Value) ASSERT(IsValid()); tolua_pushstring(m_LuaState, a_Value); - m_NumCurrentFunctionArgs += 1; } @@ -842,7 +837,6 @@ void cLuaState::Push(const cItems & a_Items) ASSERT(IsValid()); tolua_pushusertype(m_LuaState, reinterpret_cast(const_cast(&a_Items)), "cItems"); - m_NumCurrentFunctionArgs += 1; } @@ -854,7 +848,6 @@ void cLuaState::Push(const cNil & a_Nil) ASSERT(IsValid()); lua_pushnil(m_LuaState); - m_NumCurrentFunctionArgs += 1; } @@ -866,7 +859,6 @@ void cLuaState::Push(const cPlayer * a_Player) ASSERT(IsValid()); tolua_pushusertype(m_LuaState, reinterpret_cast(const_cast(a_Player)), "cPlayer"); - m_NumCurrentFunctionArgs += 1; } @@ -878,7 +870,6 @@ void cLuaState::Push(const cLuaState::cRef & a_Ref) ASSERT(IsValid()); lua_rawgeti(m_LuaState, LUA_REGISTRYINDEX, static_cast(a_Ref)); - m_NumCurrentFunctionArgs += 1; } @@ -890,7 +881,6 @@ void cLuaState::Push(const HTTPRequest * a_Request) ASSERT(IsValid()); tolua_pushusertype(m_LuaState, reinterpret_cast(const_cast(a_Request)), "HTTPRequest"); - m_NumCurrentFunctionArgs += 1; } @@ -902,7 +892,6 @@ void cLuaState::Push(const HTTPTemplateRequest * a_Request) ASSERT(IsValid()); tolua_pushusertype(m_LuaState, reinterpret_cast(const_cast(a_Request)), "HTTPTemplateRequest"); - m_NumCurrentFunctionArgs += 1; } @@ -914,7 +903,6 @@ void cLuaState::Push(const Vector3d & a_Vector) ASSERT(IsValid()); tolua_pushusertype(m_LuaState, reinterpret_cast(const_cast(&a_Vector)), "Vector3"); - m_NumCurrentFunctionArgs += 1; } @@ -926,7 +914,6 @@ void cLuaState::Push(const Vector3d * a_Vector) ASSERT(IsValid()); tolua_pushusertype(m_LuaState, reinterpret_cast(const_cast(a_Vector)), "Vector3"); - m_NumCurrentFunctionArgs += 1; } @@ -938,7 +925,6 @@ void cLuaState::Push(const Vector3i & a_Vector) ASSERT(IsValid()); tolua_pushusertype(m_LuaState, reinterpret_cast(const_cast(&a_Vector)), "Vector3"); - m_NumCurrentFunctionArgs += 1; } @@ -950,7 +936,6 @@ void cLuaState::Push(const Vector3i * a_Vector) ASSERT(IsValid()); tolua_pushusertype(m_LuaState, reinterpret_cast(const_cast(a_Vector)), "Vector3"); - m_NumCurrentFunctionArgs += 1; } @@ -962,7 +947,6 @@ void cLuaState::Push(bool a_Value) ASSERT(IsValid()); tolua_pushboolean(m_LuaState, a_Value ? 1 : 0); - m_NumCurrentFunctionArgs += 1; } @@ -1027,8 +1011,6 @@ void cLuaState::Push(cEntity * a_Entity) } } // switch (EntityType) } - - m_NumCurrentFunctionArgs += 1; } @@ -1040,7 +1022,6 @@ void cLuaState::Push(cLuaServerHandle * a_ServerHandle) ASSERT(IsValid()); tolua_pushusertype(m_LuaState, a_ServerHandle, "cServerHandle"); - m_NumCurrentFunctionArgs += 1; } @@ -1052,7 +1033,6 @@ void cLuaState::Push(cLuaTCPLink * a_TCPLink) ASSERT(IsValid()); tolua_pushusertype(m_LuaState, a_TCPLink, "cTCPLink"); - m_NumCurrentFunctionArgs += 1; } @@ -1064,7 +1044,6 @@ void cLuaState::Push(cLuaUDPEndpoint * a_UDPEndpoint) ASSERT(IsValid()); tolua_pushusertype(m_LuaState, a_UDPEndpoint, "cUDPEndpoint"); - m_NumCurrentFunctionArgs += 1; } @@ -1076,7 +1055,6 @@ void cLuaState::Push(double a_Value) ASSERT(IsValid()); tolua_pushnumber(m_LuaState, a_Value); - m_NumCurrentFunctionArgs += 1; } @@ -1088,7 +1066,6 @@ void cLuaState::Push(int a_Value) ASSERT(IsValid()); tolua_pushnumber(m_LuaState, a_Value); - m_NumCurrentFunctionArgs += 1; } @@ -1100,7 +1077,6 @@ void cLuaState::Push(long a_Value) ASSERT(IsValid()); tolua_pushnumber(m_LuaState, static_cast(a_Value)); - m_NumCurrentFunctionArgs += 1; } @@ -1112,7 +1088,6 @@ void cLuaState::Push(UInt32 a_Value) ASSERT(IsValid()); tolua_pushnumber(m_LuaState, a_Value); - m_NumCurrentFunctionArgs += 1; } @@ -1124,7 +1099,6 @@ void cLuaState::Push(std::chrono::milliseconds a_Value) ASSERT(IsValid()); tolua_pushnumber(m_LuaState, static_cast(a_Value.count())); - m_NumCurrentFunctionArgs += 1; } @@ -1136,7 +1110,6 @@ void cLuaState::Pop(int a_NumValuesToPop) ASSERT(IsValid()); lua_pop(m_LuaState, a_NumValuesToPop); - m_NumCurrentFunctionArgs -= a_NumValuesToPop; } diff --git a/src/Bindings/LuaState.h b/src/Bindings/LuaState.h index 3c07ac5d2..cb68b9a98 100644 --- a/src/Bindings/LuaState.h +++ b/src/Bindings/LuaState.h @@ -634,6 +634,7 @@ public: template bool Call(const FnT & a_Function, Args &&... args) { + m_NumCurrentFunctionArgs = -1; if (!PushFunction(std::forward(a_Function))) { // Pushing the function failed @@ -795,6 +796,7 @@ protected: inline bool PushCallPop(T && a_Param, Args &&... args) { Push(std::forward(a_Param)); + m_NumCurrentFunctionArgs += 1; return PushCallPop(std::forward(args)...); }