This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// copy lua5.1.dll to output path | |
// link lua5.1.lib | |
extern "C" | |
{ | |
#include "lua.h" | |
#include "lauxlib.h" | |
#include "lualib.h" | |
} | |
typedef boost::shared_ptr<lua_State> LuaStatePtr; | |
{ | |
LuaStatePtr L( luaL_newstate(), lua_close ); // lua_close is called automatically when the lua_State is not reference in any place. | |
int width = 0, height = 0; | |
Foo( L.get(), "dimension.lua", &width, &height ); | |
} | |
void Foo( lua_State* L, const char *filename, int *w, int *h ) | |
{ | |
if( luaL_loadfile( L, filename ) || | |
lua_pcall( L, 0, 0, 0 ) ) | |
{ | |
throw Exception( StringFormat::As( | |
_T("cannot run config file : %s"), CA2CT( lua_tostring(L,-1) ))); | |
} | |
lua_getglobal(L, "width"); // the 'width' value is pushed to the stack | |
lua_getglobal(L, "height"); // then 'height' is pused | |
if( !lua_isnumber(L,-2)) | |
{ | |
throw Exception( _T("'width' should be a number")); | |
} | |
if( !lua_isnumber(L,-1)) | |
{ | |
throw Exception( _T("'height' should be a number")); | |
} | |
*w = lua_tointeger( L, -2 ); // stack -2 is where the 'width' was pused | |
*h = lua_tointeger( L, -1 ); // stack -1 is where the 'height' was pushed | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- dimension.lua | |
width = 200 | |
height = 300 |
To further assist the embedding and interacting with C/C++, there is Luabind. You can make method and class binded to Lua script. And it also provides utility class that hide the details of stack.
Refer below code snippet to include Luabind in the C++ code.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// luabind 0.9.1 | |
// copy luabind.dll ( or luabindd.dll ) to output path | |
// link luabind.lib ( or luabindd.lib ) | |
#pragma warning( push ) | |
// need preprocessor : LUABIND_DYNAMIC_LINK | |
#pragma warning(disable:4251) | |
#include "luabind\luabind.hpp" | |
#include "luabind\object.hpp" | |
#pragma warning( pop ) | |
void foo() | |
{ | |
LuaStatePtr LPtr( luaL_newstate(), lua_close ); | |
lua_State* L = LPtr.get(); | |
luabind::open(L); | |
... | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
config = | |
{ | |
a = 123.4, -- real number | |
b = { r = 0.3, g = 0.1, b = 0 }, -- table | |
c = true, -- boolean | |
d = 'Hello World!' -- string | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
void foo() | |
{ | |
using namespace luabind; | |
try | |
{ | |
LuaStatePtr LPtr( luaL_newstate(), lua_close ); | |
lua_State* L = LPtr.get(); | |
luabind::open(L); | |
if( luaL_loadfile( L, "test.lua" ) || | |
lua_pcall( L, 0, 0, 0 ) ) | |
{ | |
throw Exception( StringFormat::As( | |
_T("cannot run config file : %s"), CA2CT( lua_tostring(L,-1) ))); | |
} | |
object G( globals(L) ); | |
object config( G[ "config" ] ); | |
double a = object_cast<double>( config["a"] ); // a <- 123.4 | |
object b( config[ "b" ] ); | |
double br = object_cast<double>( b["r"] ); // b <- 0.3 | |
bool c = object_cast<bool>( config["c"] ); // c <- true | |
std::string d = object_cast<std::string>( config["d"] ); // d <- "Hello World!" | |
} | |
catch( ... ) | |
{ | |
} | |
} |
But what if you want to access configuration like SAX does ? There should be a way to iterate through configuration items. Lua can iterate through items in the table, and we can make the script calls you with predefined interface. Here is code snippet that can do it.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using namespace std; | |
using namespace luabind; | |
class Visitor // this is the interface class that will be binded to Lua using LuaBind. | |
{ | |
public: | |
virtual ~Visitor() {} | |
virtual void Begin( const string& arg ) = 0; // when an iteration starts | |
virtual bool BeginTable( const string& name ) = 0; // when a table starts | |
virtual void VisitItem( const string& key, const string& value ) = 0; // string | |
virtual void VisitItem( const string& key, double value ) = 0; // real number | |
virtual void VisitItem( const string& key, bool value ) = 0; // boolean | |
virtual void EndTable() = 0; // when the table ends | |
virtual void End() = 0; // when the iteration ends | |
}; | |
class ConfigWriter : public Visitor | |
{ | |
public: | |
ConfigWriter() { ... } | |
~ConfigWriter() { ... } | |
void Begin( const string& arg ) { ... } | |
... | |
}; | |
typedef boost::shared_ptr<Visitor> VisitorPtr; // Lua will hold the created instance using shared_ptr which can be destroyed when it goes out of scope. | |
VisitorPtr CreateVisitor( const string& arg ) | |
{ | |
if( arg == "ConfigWriter" ) | |
return VisitorPtr( new ConfigWriter() ); | |
else | |
... | |
} | |
void Config::Open( cont _TCHAR* filename ) | |
{ | |
m_LuaState = LuaStatePtr( luaL_newstate(), lua_close ); | |
lua_State* L = m_LuaState.get(); | |
luaopen_base(L); | |
luaL_openlibs(L); | |
luabind::open( L ); | |
// need typedef to help luabind to figure out right overloaded method. | |
typedef void(Visitor::*VisitItem1)( const string&, const string& ); | |
typedef void(Visitor::*VisitItem2)( const string&, double ); | |
typedef void(Visitor::*VisitItem3)( const string&, bool ); | |
// Here goes a beautiful code snippet that binds Lua with C++ thanks to LuaBind | |
module(L) [ | |
class_<Visitor>("Visitor") | |
.def("Begin", &Visitor::Begin) | |
.def("BeginTable", &Visitor::BeginTable) | |
.def("VisitItem", (VisitItem1)&Visitor::VisitItem) | |
.def("VisitItem", (VisitItem2)&Visitor::VisitItem) | |
.def("VisitItem", (VisitItem3)&Visitor::VisitItem) | |
.def("EndTable", &Visitor::EndTable) | |
.def("End", &Visitor::End) | |
, | |
def("CreateVisitor", CreateVisitor) | |
]; | |
} | |
int pcall_handler(lua_State* L) | |
{ | |
return 1; | |
} | |
void dostring(lua_State* state, char const* str) | |
{ | |
lua_pushcclosure(state, &pcall_handler, 0); | |
if (luaL_loadbuffer(state, str, std::strlen(str), str)) | |
{ | |
string err(lua_tostring(state, -1)); | |
lua_pop(state, 2); | |
throw err; | |
} | |
if (lua_pcall(state, 0, 0, -2)) | |
{ | |
std::string err(lua_tostring(state, -1)); | |
lua_pop(state, 2); | |
throw err; | |
} | |
lua_pop(state, 1); | |
} | |
void Config::Save( const _TCHAR* filename ) | |
{ | |
stringstream script; | |
script << | |
"function IterateTable( visitor, table, tablename ) \n" | |
" if visitor:BeginTable( tablename ) == false then \n " // call ConfigWriter::BeginTable( tablename ) | |
" return \n" | |
" end \n" | |
" for k,v in pairs(table) do \n" | |
" local typev = type(v) \n" | |
" if typev == 'table' then \n" | |
" IterateTable( visitor, v, k ) \n" // recursive call to itself | |
" elseif typev == 'string' or typev == 'boolean' or typev == 'number' then \n" | |
" visitor:VisitItem( k, v ) \n" // call ConfigWriter::VisitItem( k, v ) | |
" end \n" | |
" end \n" | |
" visitor:EndTable() \n" // call ConfigWriter::EndTable() | |
"end \n" | |
"visitor = CreateVisitor( 'ConfigWriter' ) \n" // this create a C++ class ConfigWriter | |
"visitor:Begin('" << string(filename) << "') \n" // call ConfigWriter::Begin( filename ) | |
" IterateTable( visitor, Config, 'Config') \n" // iterate through 'Config' | |
"visitor:End() \n" // call ConfigWriter::End() | |
"visitor = nil \n" | |
"collectgarbage() \n" // ensure ConfigWriter's desturctor call. | |
; | |
dostring( m_LuaState.get(), script.str().c_str() ); | |
} |