Simple Cpp Binding

lua-users home
wiki

Difference (from prior author revision) (no other diffs)

Changed: 4c4
If you prefer, you can factor out all the nasty bits and put then
If you prefer, you can factor out all the nasty bits and put them

Description

Here is an example of a Lua binding for a simple C++ class. If you prefer, you can factor out all the nasty bits and put them in a template. SimplerCppBinding shows how to do this using Luna for Lua 5.0

C++ code


extern "C" {
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
}

class Account {
  public:
    Account(double balance)      { m_balance = balance; }
    void deposit(double amount)  { m_balance += amount; }
    void withdraw(double amount) { m_balance -= amount; }
    double balance(void)         { return m_balance; }
  private:
    double m_balance;
};

class LuaAccount {
    static const char className[];
    static const luaL_reg methods[];

    static Account *checkaccount(lua_State *L, int narg) {
      luaL_checktype(L, narg, LUA_TUSERDATA);
      void *ud = luaL_checkudata(L, narg, className);
      if(!ud) luaL_typerror(L, narg, className);
      return *(Account**)ud;  // unbox pointer
    }

    static int create_account(lua_State *L) {
      double balance = luaL_checknumber(L, 1);
      Account *a = new Account(balance);
      lua_boxpointer(L, a);
      luaL_getmetatable(L, className);
      lua_setmetatable(L, -2);
      return 1;
    }
    static int deposit(lua_State *L) {
      Account *a = checkaccount(L, 1);
      double amount = luaL_checknumber(L, 2);
      a->deposit(amount);
      return 0;
    }
    static int withdraw(lua_State *L) {
      Account *a = checkaccount(L, 1);
      double amount = luaL_checknumber(L, 2);
      a->withdraw(amount);
      return 0;
    }
    static int balance(lua_State *L) {
      Account *a = checkaccount(L, 1);
      double balance = a->balance();
      lua_pushnumber(L, balance);
      return 1;
    }
    static int gc_account(lua_State *L) {
      Account *a = (Account*)lua_unboxpointer(L, 1);
      delete a;
      return 0;
    }

  public:
    static void Register(lua_State* L) {
      lua_newtable(L);                 int methodtable = lua_gettop(L);
      luaL_newmetatable(L, className); int metatable   = lua_gettop(L);

      lua_pushliteral(L, "__metatable");
      lua_pushvalue(L, methodtable);
      lua_settable(L, metatable);  // hide metatable from Lua getmetatable()

      lua_pushliteral(L, "__index");
      lua_pushvalue(L, methodtable);
      lua_settable(L, metatable);

      lua_pushliteral(L, "__gc");
      lua_pushcfunction(L, gc_account);
      lua_settable(L, metatable);

      lua_pop(L, 1);  // drop metatable

      luaL_openlib(L, 0, methods, 0);  // fill methodtable
      lua_pop(L, 1);  // drop methodtable

      lua_register(L, className, create_account);
    }
};

const char LuaAccount::className[] = "Account";

#define method(class, name) {#name, class::name}

const luaL_reg LuaAccount::methods[] = {
  method(LuaAccount, deposit),
  method(LuaAccount, withdraw),
  method(LuaAccount, balance),
  {0,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);

  LuaAccount::Register(L);

  if(argc>1) lua_dofile(L, argv[1]);

  lua_close(L);
  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

local a = Account(100)
printf("Account balance = $%0.02f\n", a:balance() )
a:deposit(50.30)
printf("Account balance = $%0.02f\n", a:balance() )
a:withdraw(25.10)
printf("Account balance = $%0.02f\n", a:balance() )

Test Code Output

$ ./test account.lua
Account balance = $100.00
Account balance = $150.30
Account balance = $125.20

RecentChanges · preferences
edit · history
Last edited May 5, 2012 2:54 am GMT (diff)