Calling Lua From Cpp |
|
Luna (see SimplerCppBinding) is good for calling C++ functions from Lua.
But if you wish to call Lua functions from C++, then you'll need
to stash your Lua function in a sub-table of either the registry
or the table of globals, and then use lua_pcall
to invoke it from your C++ code.
This example uses Lunar (see CppBindingWithLunar)
which is a revised version of Luna that makes this a bit easier.
You can find lunar.h
on the CppBindingWithLunar page.
extern "C" { #include "lua.h" #include "lauxlib.h" #include "lualib.h" } #include "lunar.h" class Account { lua_Number m_balance; public: Account(double balance=0) : m_balance(balance) { } void deposit(double amount) { m_balance += amount; } void withdraw(double amount) { m_balance -= amount; } double balance(void) { return m_balance; } ~Account() { printf("deleted Account (%p)\n", this); } // Lua interface Account(lua_State *L) : m_balance(luaL_checknumber(L, 1)) { } int deposit (lua_State *L) { deposit (luaL_checknumber(L, 1)); return 0; } int withdraw(lua_State *L) { withdraw(luaL_checknumber(L, 1)); return 0; } int balance (lua_State *L) { lua_pushnumber(L, balance()); return 1; } static const char className[]; static Lunar<Account>::RegType methods[]; }; const char Account::className[] = "Account"; #define method(class, name) {#name, &class::name} Lunar<Account>::RegType Account::methods[] = { method(Account, deposit), method(Account, withdraw), method(Account, balance), {0,0} }; static int report (lua_State *L, int status) { if (status) { const char *msg = lua_tostring(L, -1); if (msg == NULL) msg = "(error with no message)"; fprintf(stderr, "ERROR: %s\n", msg); lua_pop(L, 1); } return status; } static int application (lua_State *L) { lua_settop(L, 0); lua_pushliteral(L, "_TRACEBACK"); lua_gettable(L, LUA_GLOBALSINDEX); // get traceback function int tb = lua_gettop(L); Account a; Account *b = new Account(30); int A = Lunar<Account>::push(L, &a); int B = Lunar<Account>::push(L, b, true); lua_pushliteral(L, "a"); lua_pushvalue(L, A); lua_settable(L, LUA_GLOBALSINDEX); lua_pushliteral(L, "b"); lua_pushvalue(L, B); lua_settable(L, LUA_GLOBALSINDEX); lua_pushvalue(L, A); lua_pushnumber(L, 100.00); report(L, Lunar<Account>::call(L, "deposit", 1, 0, tb) < 0); lua_pushvalue(L, A); report(L, Lunar<Account>::call(L, "show", 0, 0, tb) < 0); lua_pushvalue(L, B); report(L, Lunar<Account>::call(L, "show", 0, 0, tb) < 0); lua_pushliteral(L, "main"); lua_gettable(L, LUA_GLOBALSINDEX); report(L, lua_pcall(L, 0, 0, tb)); return 0; } int main (int argc, char *argv[]) { lua_State *L = lua_open(); luaopen_base(L); luaopen_table(L); luaopen_io(L); luaopen_string(L); luaopen_math(L); luaopen_debug(L); Lunar<Account>::Register(L); if (argc>1) { printf("loading '%s'\n", argv[1]); if (report(L, luaL_loadfile(L, argv[1]) || lua_pcall(L, 0, 0, 0)) == 0) { printf("running application\n"); if (report(L, lua_cpcall(L, &application, 0)) == 0) { printf("okay\n"); } } } lua_setgcthreshold(L, 0); // collected garbage printf("close\n"); lua_close(L); printf("done\n"); return 0; }
This code can be compiled for Lua 5.0 as follows:
g++ -o test account.cc -L/usr/local/lib -llua -llualib
function printf(...) io.write(string.format(unpack(arg))) end function Account:show() printf("Account balance = $%0.02f\n", self:balance()) end parent = {} function parent:rob(amount) amount = amount or self:balance() self:withdraw(amount) return amount end getmetatable(Account).__index = parent function main() print('a =', a) print('b =', b) print('metatable =', getmetatable(a)) print('Account =', Account) table.foreach(Account, print) a:show() a:deposit(50.30) a:show() a:withdraw(25.10) a:show() debug.debug() end
$ ./test setup.lua loading 'setup.lua' running application Account balance = $100.00 Account balance = $30.00 a = Account (0x22fcb0) b = Account (0xa041d98) metatable = table: 0xa044f28 Account = table: 0xa044f28 show function: 0xa0464a0 balance function: 0xa0455f8 withdraw function: 0xa045300 deposit function: 0xa045508 new function: 0xa044fe8 Account balance = $100.00 Account balance = $150.30 Account balance = $125.20 lua_debug> a:show() Account balance = $125.20 lua_debug> b:show() Account balance = $30.00 lua_debug> b:deposit(0.01) b:show() Account balance = $30.01 lua_debug> c = Account(19.99) c:deposit(b:rob()) c:show() Account balance = $50.00 lua_debug> b:show() Account balance = $0.00 lua_debug> b = nil lua_debug> print('c =', c) c = Account (0xa048cf8) lua_debug> cont deleted Account (0x22fcb0) okay deleted Account (0xa041d98) close deleted Account (0xa048cf8) done