From 9b839aa32efe0287f5b28fd60285b10c6cf21d59 Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Wed, 7 Aug 2013 14:26:18 +0200 Subject: cLuaState has reference management, param checking and a fixed destructor. References are now managed as RAII objects, cLuaState::cRef. Destructor now calls correct function, either Close() or Detach(), based on the owned-ness of the lua_State *. --- source/LuaState.cpp | 187 +++++++++++++++++++++++++++++++++++++++++++++++++++- source/LuaState.h | 51 +++++++++++++- 2 files changed, 234 insertions(+), 4 deletions(-) diff --git a/source/LuaState.cpp b/source/LuaState.cpp index aa9d796c3..664ba8147 100644 --- a/source/LuaState.cpp +++ b/source/LuaState.cpp @@ -63,7 +63,14 @@ cLuaState::~cLuaState() { if (IsValid()) { - Close(); + if (m_IsOwned) + { + Close(); + } + else + { + Detach(); + } } } @@ -236,6 +243,35 @@ bool cLuaState::PushFunctionFromRegistry(int a_FnRef) +bool cLuaState::PushFunctionFromRefTable(cRef & a_TableRef, const char * a_FnName) +{ + ASSERT(IsValid()); + ASSERT(m_NumCurrentFunctionArgs == -1); // If not, there's already something pushed onto the stack + + lua_rawgeti(m_LuaState, LUA_REGISTRYINDEX, a_TableRef); // Get the table ref + if (!lua_istable(m_LuaState, -1)) + { + // Not a table, bail out + lua_pop(m_LuaState, 1); + return false; + } + lua_getfield(m_LuaState, -1, a_FnName); + if (lua_isnil(m_LuaState, -1) || !lua_isfunction(m_LuaState, -1)) + { + // Not a valid function, bail out + lua_pop(m_LuaState, 2); + return false; + } + lua_remove(m_LuaState, -2); // Remove the table ref from the stack + m_CurrentFunctionName = ""; + m_NumCurrentFunctionArgs = 0; + return true; +} + + + + + void cLuaState::PushStringVector(const AStringVector & a_Vector) { ASSERT(IsValid()); @@ -434,6 +470,125 @@ bool cLuaState::CallFunction(int a_NumResults) +bool cLuaState::CheckParamUserType(int a_StartParam, const char * a_UserType, int a_EndParam) +{ + ASSERT(IsValid()); + + if (a_EndParam < 0) + { + a_EndParam = a_StartParam; + } + + tolua_Error tolua_err; + for (int i = a_StartParam; i <= a_EndParam; i++) + { + if (tolua_isusertype(m_LuaState, i, a_UserType, 0, &tolua_err)) + { + continue; + } + // Not the correct parameter + lua_Debug entry; + VERIFY(lua_getstack(m_LuaState, 0, &entry)); + VERIFY(lua_getinfo (m_LuaState, "n", &entry)); + AString ErrMsg = Printf("#ferror in function '%s'.", (entry.name != NULL) ? entry.name : "?"); + tolua_error(m_LuaState, ErrMsg.c_str(), &tolua_err); + return false; + } // for i - Param + + // All params checked ok + return true; +} + + + + + +bool cLuaState::CheckParamTable(int a_StartParam, int a_EndParam) +{ + ASSERT(IsValid()); + + if (a_EndParam < 0) + { + a_EndParam = a_StartParam; + } + + tolua_Error tolua_err; + for (int i = a_StartParam; i <= a_EndParam; i++) + { + if (tolua_istable(m_LuaState, i, 0, &tolua_err)) + { + continue; + } + // Not the correct parameter + lua_Debug entry; + VERIFY(lua_getstack(m_LuaState, 0, &entry)); + VERIFY(lua_getinfo (m_LuaState, "n", &entry)); + AString ErrMsg = Printf("#ferror in function '%s'.", (entry.name != NULL) ? entry.name : "?"); + tolua_error(m_LuaState, ErrMsg.c_str(), &tolua_err); + return false; + } // for i - Param + + // All params checked ok + return true; +} + + + + + +bool cLuaState::CheckParamNumber(int a_StartParam, int a_EndParam) +{ + ASSERT(IsValid()); + + if (a_EndParam < 0) + { + a_EndParam = a_StartParam; + } + + tolua_Error tolua_err; + for (int i = a_StartParam; i <= a_EndParam; i++) + { + if (tolua_isnumber(m_LuaState, i, 0, &tolua_err)) + { + continue; + } + // Not the correct parameter + lua_Debug entry; + VERIFY(lua_getstack(m_LuaState, 0, &entry)); + VERIFY(lua_getinfo (m_LuaState, "n", &entry)); + AString ErrMsg = Printf("#ferror in function '%s'.", (entry.name != NULL) ? entry.name : "?"); + tolua_error(m_LuaState, ErrMsg.c_str(), &tolua_err); + return false; + } // for i - Param + + // All params checked ok + return true; +} + + + + + +bool cLuaState::CheckParamEnd(int a_Param) +{ + tolua_Error tolua_err; + if (tolua_isnoobj(m_LuaState, a_Param, &tolua_err)) + { + return true; + } + // Not the correct parameter + lua_Debug entry; + VERIFY(lua_getstack(m_LuaState, 0, &entry)); + VERIFY(lua_getinfo (m_LuaState, "n", &entry)); + AString ErrMsg = Printf("#ferror in function '%s': Too many arguments.", (entry.name != NULL) ? entry.name : "?"); + tolua_error(m_LuaState, ErrMsg.c_str(), &tolua_err); + return false; +} + + + + + bool cLuaState::ReportErrors(int a_Status) { return ReportErrors(m_LuaState, a_Status); @@ -459,3 +614,33 @@ bool cLuaState::ReportErrors(lua_State * a_LuaState, int a_Status) + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cLuaState::cRef: + +cLuaState::cRef::cRef(cLuaState & a_LuaState, int a_StackPos) : + m_LuaState(a_LuaState) +{ + ASSERT(m_LuaState.IsValid()); + + lua_pushvalue(m_LuaState, a_StackPos); // Push a copy of the value at a_StackPos onto the stack + m_Ref = luaL_ref(m_LuaState, LUA_REGISTRYINDEX); +} + + + + + +cLuaState::cRef::~cRef() +{ + ASSERT(m_LuaState.IsValid()); + + if (IsValid()) + { + luaL_unref(m_LuaState, LUA_REGISTRYINDEX, m_Ref); + } +} + + + + diff --git a/source/LuaState.h b/source/LuaState.h index 18c7c1354..6820f1777 100644 --- a/source/LuaState.h +++ b/source/LuaState.h @@ -13,6 +13,10 @@ Calling a Lua function is done by pushing the function, either by PushFunction() then pushing the arguments (PushString(), PushNumber(), PushUserData() etc.) and finally executing CallFunction(). cLuaState automatically keeps track of the number of arguments and the name of the function (for logging purposes), which makes the call less error-prone. + +Reference management is provided by the cLuaState::cRef class. This is used when you need to hold a reference to +any Lua object across several function calls; usually this is used for callbacks. The class is RAII-like, with +automatic resource management. */ @@ -20,12 +24,14 @@ function (for logging purposes), which makes the call less error-prone. #pragma once +extern "C" +{ + #include "lauxlib.h" +} -// fwd: lua.h -struct lua_State; class cWorld; class cPlayer; @@ -39,9 +45,31 @@ class cPickup; +/// Encapsulates a Lua state and provides some syntactic sugar for common operations class cLuaState { public: + + /// Used for storing references to object in the global registry + class cRef + { + public: + /// Creates a reference in the specified LuaState for object at the specified StackPos + cRef(cLuaState & a_LuaState, int a_StackPos); + ~cRef(); + + /// Returns true if the reference is valid + bool IsValid(void) const {return (m_Ref != LUA_REFNIL); } + + /// Allows to use this class wherever an int (i. e. ref) is to be used + operator int(void) { return m_Ref; } + + protected: + cLuaState & m_LuaState; + int m_Ref; + } ; + + /** Creates a new instance. The LuaState is not initialized. a_SubsystemName is used for reporting problems in the console, it is "plugin %s" for plugins, or "LuaScript" for the cLuaScript template @@ -90,6 +118,11 @@ public: */ bool PushFunctionFromRegistry(int a_FnRef); + /** Pushes a function that is stored in a table ref. + Returns true if successful, false on failure. Doesn't log failure. + */ + bool PushFunctionFromRefTable(cRef & a_TableRef, const char * a_FnName); + /// Pushes a string vector, as a table, onto the stack void PushStringVector(const AStringVector & a_Vector); @@ -123,7 +156,19 @@ public: Returns true if successful, logs a warning on failure. */ bool CallFunction(int a_NumReturnValues); - + + /// Returns true if the specified parameters on the stack are of the specified usertype; also logs warning if not + bool CheckParamUserType(int a_StartParam, const char * a_UserType, int a_EndParam = -1); + + /// Returns true if the specified parameters on the stack are a table; also logs warning if not + bool CheckParamTable(int a_StartParam, int a_EndParam = -1); + + /// Returns true if the specified parameters on the stack are a number; also logs warning if not + bool CheckParamNumber(int a_StartParam, int a_EndParam = -1); + + /// Returns true if the specified parameter on the stack is nil (indicating an end-of-parameters) + bool CheckParamEnd(int a_Param); + /// If the status is nonzero, prints the text on the top of Lua stack and returns true bool ReportErrors(int status); -- cgit v1.2.3