1
0

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 *.
This commit is contained in:
madmaxoft 2013-08-07 14:26:18 +02:00
parent c55fabb5ad
commit 9b839aa32e
2 changed files with 234 additions and 4 deletions

View File

@ -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 = "<table_callback>";
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);
}
}

View File

@ -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);