Calling Lua From Cpp

lua-users home
wiki

Description

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.

account.cc C++ code

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;
}

Compiling the Code

This code can be compiled for Lua 5.0 as follows:

g++ -o test  account.cc -L/usr/local/lib -llua -llualib

Lua Test Code

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 Code Output

$ ./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

RecentChanges · preferences
edit · history
Last edited January 6, 2007 7:32 pm GMT (diff)