// luaObject.hpp
//
// Copyright 2008 by Nigel Atkinson suprapilot+LuaCode@gmail.com
//
// This library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License (LGPL) as
// published by the Free Software Foundation, either version 3 of
// the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// A copy of the GNU General Public License is availible at:
// .
//
// Makes interfacing with Lua easier.
// Tested with Lua 5.1.3 and 5.1.2.
// MSVC 7.12 (2003) and GCC (g++) 4.1.2
//
// See http://www.atkinson.gen.nz/talking_with_lua.php for examples of use.
//
// 8th May 2008 Nigel
// Added LuaException class and changed all throws to use it. User code can use it too.
// It grabs any string, if any, from the top of the Lua stack.
// 4th August 2009 Nigel
// Added stream function to allow use of reference objects with std::cout etc.
// Changed exception class to have a 'what' method.
// Fixed problem where:
//
// LuaRef r = LuaRef( L, "something" );
//
// would result in the double cast method being called. This was fixed by adding 'const'
// to the copy constructor, and making LuaRef::push a const function.
//
// 8th Aug 2009
// Whoops, LuaVal really needs a copy constuctor now, that is sometimes holds a pointer.
// Also LuaException decends from from the standard runtime exception.
#ifndef __LUAOBJECT_H
#define __LUAOBJECT_H
#include
#include
#include
#include
#include
// Used when the object to set the LuaRef to, on creation, is on the top of the lua stack.
struct fromStack
{
lua_State *mL;
fromStack( lua_State *L ) : mL(L)
{;}
};
// Pops a number of objects off the Lua stack once out of scope.
// Used to ensure the stack is left as it was, even if exceptions occur.
class luaPop
{
lua_State *mL;
int mNum;
public:
luaPop( lua_State *L, int num=1 ): mL(L), mNum(num)
{;}
~luaPop()
{
lua_pop( mL, mNum );
}
};
class LuaRef;
std::ostream & operator<<( std::ostream &os, LuaRef &ref );
// Basic "ANY" class for representing Lua objects.
// Used in calling Lua functions as parameters.
class LuaVal
{
int mType;
double d;
std::string str;
lua_CFunction func;
LuaRef *obj;
public:
LuaVal() : mType( LUA_TNIL ), obj(NULL)
{;}
LuaVal( double n ) : d( n ), mType( LUA_TNUMBER ), obj(NULL)
{;}
LuaVal( std::string n ) : str( n ), mType( LUA_TSTRING ), obj(NULL)
{;}
LuaVal( const char *n ) : str( n ), mType( LUA_TSTRING ), obj(NULL)
{;}
LuaVal( lua_CFunction n ) : func( n ), mType( LUA_TFUNCTION ), obj(NULL)
{;}
LuaVal( LuaRef *o );
~LuaVal();
// Copy
LuaVal( const LuaVal &lv );
void push( lua_State *L );
};
// Exception class
class LuaException : public std::runtime_error
{
std::string luaError;
std::string file;
long line;
lua_State *mL;
// Do we we need a copy constuctor? I think the default supplied one works.
public:
LuaException( lua_State *L, const char *str, const char *filename, long fileline )
: std::runtime_error(str), file(filename), line(fileline)
{
if( lua_gettop( L ) != 0 )
if( lua_isstring( L, -1 ) )
luaError = lua_tostring( L, -1 );
mL = L;
}
std::string getLuaError() { return luaError; }
lua_State *getLuaState() { return mL; }
const char *what() const
{
std::stringstream ss;
ss << "*** " << std::runtime_error::what() << " ***" << std::endl;
ss << "*** " << luaError << " ***" << std::endl;
ss << "*** In file: " << file << " Line: " << line << " ***" << std::endl;
return ss.str().c_str();
}
~LuaException() throw () {}
};
// A Lua Object. Represents a Lua object within a Lua state.
// Each is referenced, so the Lua GC will not garbage collect this object while this instance is in scope.
// Once out of scope, the Lua object could be garbage collected, depending on any other references.
class LuaRef
{
int mRef; // Index to reference of object.
lua_State *mL; // and the vm
// Private internal class for returning as a proxy object. This is used when asked for a table element.
// This class can not be instantated outside of LuaRef. It lets you use syntax like the following:
//
// tbl["index"] = something;
//
// given that "tbl" is a LuaRef.
class tableElement
{
// Allow outside class access to members, and private constructor.
friend class LuaRef;
std::string mKey; // Element index
int mRef; // Reference to table
lua_State *mL;
// Private constructor.
tableElement( lua_State *L, int ref, const char *key ) : mRef( ref ), mL(L), mKey(key)
{ ; }
public:
// Put table element on top of Lua stack.
void push()
{
lua_rawgeti( mL, LUA_REGISTRYINDEX, mRef );
lua_getfield( mL, -1, mKey.c_str() );
}
// Return the 'type' of the table element.
int type()
{
int ret;
push();
ret = lua_type( mL, -1 );
lua_pop( mL, 2 ); // Remove index and table from stack.
return ret;
}
double operator = ( double f )
{
luaPop p(mL);
lua_rawgeti( mL, LUA_REGISTRYINDEX, mRef );
lua_pushnumber( mL, f );
lua_setfield( mL, -2, mKey.c_str() );
return f;
}
std::string & operator = ( std::string & str )
{
operator = ( str.c_str() ); // Just re-use the const char * code.
return str;
}
const char * operator = ( const char *str )
{
luaPop p(mL);
lua_rawgeti( mL, LUA_REGISTRYINDEX, mRef );
lua_pushstring( mL, str );
lua_setfield( mL, -2, mKey.c_str() );
return str;
}
lua_CFunction operator = ( lua_CFunction func )
{
luaPop p(mL);
lua_rawgeti( mL, LUA_REGISTRYINDEX, mRef );
lua_pushcfunction( mL, func );
lua_setfield( mL, -2, mKey.c_str() );
return func;
}
LuaRef & operator = ( LuaRef &obj )
{
luaPop p(mL);
lua_rawgeti( mL, LUA_REGISTRYINDEX, mRef );
obj.push();
lua_setfield( mL, -2, mKey.c_str() );
return obj;
}
operator double()
{
luaPop p1(mL,2); // Removes index and table from Lua stack once out of scope.
push();
return lua_tonumber( mL, -1 );
}
operator std::string()
{
push();
std::string str( lua_tostring( mL, -1 ) );
lua_pop( mL, 2 ); // Removes index and table from Lua stack
return str;
}
operator LuaRef()
{
luaPop p( mL, 2);
push();
fromStack fs( mL );
LuaRef ref(fs);
return ref;
}
operator LuaVal()
{
luaPop p( mL, 2);
push();
fromStack fs( mL );
LuaRef ref(fs);
LuaVal val(&ref);
return val;
}
};
public:
LuaRef( lua_State *L ) : mL( L ), mRef( LUA_NOREF ) // For "new" objects, that will be assigned a value later.
{;}
LuaRef( lua_State *L, const char *name ) : mL( L ) // Reference an existing object. Note that numbers and strings
{ // will be copied. Any changes will not change the original.
lua_getglobal( mL, name );
mRef = luaL_ref( mL, LUA_REGISTRYINDEX );
}
LuaRef( fromStack fs ) : mL( fs.mL ) // Reference an existing object that is on top of the Lua stack.
{ // Note same cavet as above.
mRef = luaL_ref( mL, LUA_REGISTRYINDEX ); // Removes from stack.
}
LuaRef( tableElement & te ) : mL( te.mL ) // Reference a table element.
{
lua_rawgeti( te.mL, LUA_REGISTRYINDEX, te.mRef );
lua_getfield( te.mL, -1, te.mKey.c_str() );
mRef = luaL_ref( mL, LUA_REGISTRYINDEX );
}
LuaRef( const LuaRef &obj ) : mL( obj.mL )
{
obj.push();
mRef = luaL_ref( mL, LUA_REGISTRYINDEX );
}
LuaRef( LuaRef *obj ) : mL( obj->mL )
{
obj->push();
mRef = luaL_ref( mL, LUA_REGISTRYINDEX );
}
~LuaRef() // We're gone. Release reference to object.
{
luaL_unref( mL, LUA_REGISTRYINDEX, mRef );
}
// Place object on top of Lua stack.
void push() const
{
lua_rawgeti( mL, LUA_REGISTRYINDEX, mRef );
}
// Return the 'type' of the object
int type() const
{
int ret;
push();
ret = lua_type( mL, -1 );
lua_pop( mL, 1 );
return ret;
}
// Create a new table.
void createTable()
{
lua_newtable( mL );
mRef = luaL_ref( mL, LUA_REGISTRYINDEX );
}
// Create a new table and associate a global variable with it.
void createTable( const char *name )
{
createTable();
store( name );
}
// Return a proxy to a table element givin an index.
tableElement operator [] ( const char *key )
{
push();
if( ! lua_istable( mL, -1 ) )
throw LuaException( mL, "LuaRef operator [] used on a non Lua table", __FILE__, __LINE__ );
lua_pop( mL, 1 );
return tableElement( mL, mRef, key );
}
// Return a proxy to a table element givin an index.
tableElement operator [] ( int key )
{
push();
if( ! lua_istable( mL, -1 ) )
throw LuaException( mL, "LuaRef operator [] used on a non Lua table", __FILE__, __LINE__ );
lua_pop( mL, 1 );
std::stringstream ss;
ss << key;
return tableElement( mL, mRef, ss.str().c_str() );
}
// Reference an object referenced by "obj"
LuaRef & operator = ( const LuaRef &obj )
{
obj.push();
mL = obj.mL;
mRef = luaL_ref( mL, LUA_REGISTRYINDEX );
return *this;
}
// Reference a number as a new object.
double operator = ( double f )
{
lua_pushnumber( mL, f );
mRef = luaL_ref( mL, LUA_REGISTRYINDEX );
return f;
}
// Reference a string as a new object.
std::string & operator = ( std::string & str )
{
operator = ( str.c_str() );
return str;
}
// Reference a string.
const char * operator = ( const char *str )
{
lua_pushstring( mL, str );
mRef = luaL_ref( mL, LUA_REGISTRYINDEX );
return str;
}
// Reference a function.
lua_CFunction operator = ( lua_CFunction func )
{
lua_pushcfunction( mL, func );
mRef = luaL_ref( mL, LUA_REGISTRYINDEX );
return func;
}
// Associate referenced object with a name, or global variable.
void store( const char *name )
{
push();
lua_setglobal( mL, name);
}
// Associate referenced object as a member of a table.
void store( const char *name, LuaRef & table )
{
if( table.type() != LUA_TTABLE )
throw LuaException( mL, "given object is not a table.", __FILE__, __LINE__ );
table.push();
push();
lua_setfield( mL, -2, name ); // Pops value
lua_pop( mL, 1 ); // Pops table
}
// Return as a number.
operator double()
{
double ret = 0.0;
luaPop p(mL);
push();
if( lua_isnumber( mL, -1 ) )
ret = lua_tonumber( mL, -1 );
else
throw LuaException( mL, "LuaRef referenced object is not a number.", __FILE__, __LINE__ );
return ret;
}
// Return as a string
operator std::string()
{
std::string ret;
luaPop p(mL);
push();
if( lua_isstring( mL, -1 ) )
ret = lua_tostring( mL, -1 );
else
throw LuaException( mL, "LuaRef referenced object is not a string.", __FILE__, __LINE__ );
return ret;
}
operator LuaVal()
{
LuaVal val(this);
return val;
}
// The next few overloads of operator () allow calling a referenced Lua object (provided it's a function),
// with the same syntax as calling a C/C++ function. Only the types LuaVal has conversions for can be used.
// Upto 4 parameters, but more can be added. Returns true on succesfull call. No results are returned.
LuaRef operator () ()
{
push();
if( lua_isfunction( mL, -1 ) )
{
if( lua_pcall( mL, 0, 1, 0 ) != 0 )
throw LuaException( mL, "Error running function in LuaRef operator ()", __FILE__, __LINE__ );
}
else
{
lua_pop( mL, 1 );
throw LuaException( mL, "LuaRef operator () called but does not reference a function", __FILE__, __LINE__ );
}
fromStack fs( mL );
LuaRef ref( fs );
return ref;
}
LuaRef operator () ( LuaVal p1 )
{
push();
if( lua_isfunction( mL, -1 ) )
{
p1.push(mL);
if( lua_pcall( mL, 1, 1, 0 ) != 0 )
throw LuaException( mL, "Error running function in LuaRef operator ()", __FILE__, __LINE__ );
}
else
{
lua_pop( mL, 1 );
throw LuaException( mL, "LuaRef operator () called but does not reference a function", __FILE__, __LINE__ );
}
fromStack fs( mL );
LuaRef ref( fs );
return ref;
}
LuaRef operator () ( LuaVal p1, LuaVal p2 )
{
push();
if( lua_isfunction( mL, -1 ) )
{
p1.push(mL);
p2.push(mL);
if( lua_pcall( mL, 2, 1, 0 ) != 0 )
throw LuaException( mL, "Error running function in LuaRef operator ()", __FILE__, __LINE__ );
}
else
{
lua_pop( mL, 1 );
throw LuaException( mL, "LuaRef operator () called but does not reference a function", __FILE__, __LINE__ );
}
fromStack fs( mL );
LuaRef ref( fs );
return ref;
}
LuaRef operator () ( LuaVal p1, LuaVal p2, LuaVal p3 )
{
push();
if( lua_isfunction( mL, -1 ) )
{
p1.push(mL);
p2.push(mL);
p3.push(mL);
if( lua_pcall( mL, 3, 1, 0 ) != 0 )
throw LuaException( mL,"Error running function in LuaRef operator ()" , __FILE__, __LINE__ );
}
else
{
lua_pop( mL, 1 );
throw LuaException( mL, "LuaRef operator () called but does not reference a function", __FILE__, __LINE__ );
}
fromStack fs( mL );
LuaRef ref( fs );
return ref;
}
LuaRef operator () ( LuaVal p1, LuaVal p2, LuaVal p3, LuaVal p4 )
{
push();
if( lua_isfunction( mL, -1 ) )
{
p1.push(mL);
p2.push(mL);
p3.push(mL);
p4.push(mL);
if( lua_pcall( mL, 4, 1, 0 ) != 0 )
throw LuaException( mL, "Error running function in LuaRef operator ()", __FILE__, __LINE__ );
}
else
{
lua_pop( mL, 1 );
throw LuaException( mL,"LuaRef operator () called but does not reference a function" , __FILE__, __LINE__ );
}
fromStack fs( mL );
LuaRef ref( fs );
return ref;
}
};
#endif // __LUAOBJECT_H