So this might be a little frivolous, but I wanted to look at GetNestedFunction to see if it cleaned up after itself on the Lua stack. It does, but I wanted to make the usage semantics a bit more obvious, i.e. yes, you do have to pop from the stack afterwards.
This is the refactoring I've done:
// Given a name of the form "table.function", retrieves a function
// from a set of nested tables. (There can be 0 or more "table"s in the name.)
// Pushes the named function to the stack on success, and nil on failure.
bool GetNestedFunction (lua_State * L, const char * sName, const bool bRaiseError)
{
// L: ...
vector<string> v;
StringToVector (sName, v, ".", true);
// get the method from the end, leaving only table names in the vector
string sMethod = v.back ();
v.pop_back ();
// run down the vector to get to the table containing the method.
// Start with the globals table, _G.
lua_pushvalue(L, LUA_GLOBALSINDEX); // L: ... _G
string sTable = "_G";
vector<string>::const_iterator iter = v.begin ();
vector<string>::const_iterator end = v.end ();
for (; iter != end; ++iter)
{
// L: ... parent
if (!lua_istable (L, -1))
break; // it's not a table, so we can't get the method we wanted
// pull the child table out, and remove the parent
lua_getfield (L, -1, iter->c_str ()); // L: ... parent, child
lua_remove (L, -2); // L: ... child
sTable = *iter;
}
// L: ... table
// make sure the last thing was a table
if (lua_istable (L, -1))
{
// get the method and remove the table
lua_getfield (L, -1, sMethod.c_str ()); // L: ... table, function
lua_remove (L, -2); // L: ... function
// make sure we have a function
if (lua_isfunction (L, -1))
return true; // L: ... function
else
sTable = sMethod; // for the error message at the bottom
}
// ... unknown
if (bRaiseError)
::UMessageBox (TFormat ("Cannot find the function '%s' - item '%s' is %s",
sMethod,
sTable.c_str (),
luaL_typename (L, -1)),
MB_ICONEXCLAMATION);
lua_pop (L, 1); // L: ...
lua_pushnil (L); // L: ... nil
return false; // L: ... nil
}
Mostly, I tightened up the semantics such that 'nil' is always at the top of the stack if it can't resolve the method. I also replaced the lua_pushstring/lua_gettable pairs with lua_getfield because it's more concise (and I couldn't resist).
Given the nil/function stack result, this function could easily return void, and callers could act based on the value at the top of the stack. The error message could be pushed to the stack after the nil, which the calling code could display as it wishes. I didn't do these because I don't want to touch the calling code yet.
I'm not putting this into an active refactoring branch, I just thought I'd have some fun experimenting with it. Ideally, this function wouldn't deal with showing errors to the user directly, because it's orthogonal to the function's job. I'd prefer usage like this (in Lua pseudocode):
local func, err = get_nested_function("foo.bar")
if func == nil then
error(err)
end
Anyways, I just thought I'd throw it out there. |