Lua 5.2 and Luna Five Example

This post covers a basic C++ example using Luna Five with Lua 5.2. The example implements an account class in C++ and binds the class to the Lua environment using Luna Five and was inspired by the example in [3].

If one starts with C++ Account class header, seen below, one can see that the methods take the lua_State as a parameter. This allows these functions to push and pop parameters on and off the Lua stack. When a function pushes elements onto the stack, it returns the number of parameters it pushed onto the Lua stack.
The class also contains the following public static variables:

  • className - The name used to address the class type in a Lua script
  • properties - An array of all the properties and each of their accessor and mutator methods.
  • methods - An array of methods provided by the class.
//account.h file
class Account {
  public:
    Account(lua_State *L);
    ~Account(void);

    int Deposit(lua_State *L);
    int Withdraw(lua_State *L);
    int Balance(lua_State *L);

    //Class Constants
    static const char className[];
    static const Luna<Account>::PropertyType properties[];
    static const Luna<Account>::FunctionType methods[];

  private:
    int m_balance; //Account balance
};

Below is the implementation of the account header:

//account.cpp file
#include "account.h"

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

//Name to use to address the class in Lua
const char Account::className[] = "Account";

//List of class properties that one can set/get from Lua
const Luna::PropertyType Account::properties[] = {
  {0,0}
};

//List of class methods to make available in Lua
const Luna::FunctionType Account::methods[] = {
  method(Account, Deposit),
  method(Account, Withdraw),
  method(Account, Balance),
  {0,0}
};

/**
Constructor
@param L The Lua state
*/
Account::Account(lua_State *L) {
  m_balance = lua_tointeger(L, 1);
}

/**
Destructor
*/
Account::~Account() {
  printf("C++: Deleted Account (%p)\n", this);
}

/**
Deposits an amount into the account
@param L The Lua state
@return The number of returned variables pushed on to the Lua stack.
*/
int Account::Deposit(lua_State *L) {
  m_balance += lua_tointeger(L, 1);
  return 0;
}

/**
Withdraws an amount from the account
@param L The Lua state
@return The number of returned variables pushed on to the Lua stack.
*/
int Account::Withdraw(lua_State *L) {
  m_balance -= lua_tointeger(L, 1);
  return 0;
}

/**
Accessor Method
Get the balance for the account
@param L The Lua state
@return The number of returned variables pushed on to the Lua stack.
*/
int Account::Balance(lua_State *L) {
  lua_pushnumber(L, m_balance);
  return 1;
}

lua_tointeger is used to get C++ integer values from the Lua stack and lua_pushnumber is used to push an integer onto the Lua stack such that it can be accessed from within the Lua script.
Below is the main function used to create the Lua state, register the account class with the Lua start and run a Lua script:

//main.cpp
#include "account.h"

int main(int argc, char *argv[])
{
  //Check if a Lua Script was specified
  if(argc != 2){
    printf("Error! No Lua script or too many scripts were specified.\n");
    printf("Usage: %s <file>.lua\n", argv[0]);
    return -1;
  }

  //Create a new Lua state
  lua_State *L = luaL_newstate(); 

  //Load Lua base library
  luaopen_base(L);

  //Register "Account" Class with Lua
  Luna<Account>::Register(L);

  //Execute the Lua script
  printf("C++: Executing Lua Script: %s\n",argv[1]);
  if(luaL_dofile(L, argv[1]) != 0){
    printf("Lua Error: %s\n", lua_tostring(L,-1));
  }

  //Close Lua state
  lua_close(L);
  return 0;
}

Here one can see how simple Luna makes it to register C++ classes with Lua. Below is a script to demonstrate the interaction between Lua and C++:

//account.lua Script
-- Override Lua Print()
local print = function (x)
  print("Lua: "..x)
end

local a1 = Account(100)
print("Created Account 1 with $"..a1:Balance().." balance")

cash = 50
a1.Deposit(cash)
print("Account 1: Depositing $"..cash)

print("Account 1: New Balance is $"..a1:Balance())

cash = 25
a1.Withdraw(cash)
print("Account 1: Withdrawing $"..cash)

print("Account 1: New Balance is $"..a1:Balance())

-- Open a 2nd Account
local a2 = Account(1000)
print("Created Account 2 with $"..a2:Balance().." balance")

cash = 500
a2.Deposit(cash)
print("Account 2: Depositing $"..cash)

print("Account 2: New Balance is $"..a2:Balance())

cash = 250
a2.Withdraw(cash)
print("Account 2: Withdrawing $"..cash)

print("Account 2: New Balance is $"..a2:Balance())

-- Check the 1st Account Again
print("Account 1: New Balance is $"..a1:Balance())
cash = 25
a1.Withdraw(cash)
print("Account 1: Withdrawing $"..cash)

print("Account 1: New Balance is $"..a1:Balance())

print("End of Script")

One is able to create multiple instances of the class and manipulate the balance variable within each of these instances easily. All the files can be downloaded used the download link provided below.

Program Output
$ ./account account.lua
C++: Executing Lua Script: account.lua
Lua: Created Account 1 with $100 balance
Lua: Account 1: Depositing $50
Lua: Account 1: New Balance is $150
Lua: Account 1: Withdrawing $25
Lua: Account 1: New Balance is $125
Lua: Created Account 2 with $1000 balance
Lua: Account 2: Depositing $500
Lua: Account 2: New Balance is $1500
Lua: Account 2: Withdrawing $250
Lua: Account 2: New Balance is $1250
Lua: Account 1: New Balance is $125
Lua: Account 1: Withdrawing $25
Lua: Account 1: New Balance is $100
Lua: End of Script
C++: Deleted Account (0x97059f0)
C++: Deleted Account (0x97053a0)

Download
Download Lua 5.2 and Luna Five Account Example

References
[1] Lua 5.2 Reference Manual, http://www.lua.org/manual/5.2/
[2] Luna Five, http://lua-users.org/wiki/LunaFive, Viewed 15 May 2013
[3] Cpp Binding with Lunar, http://lua-users.org/wiki/CppBindingWithLunar

About Poul
Software Engineer in Denmark.
This entry was posted in C++, General, Lua and tagged , , . Bookmark the permalink.

Comments are closed.