Cpp Object Binding |
|
Trying to gain access into your C++ Objects from Lua (without using any add-ons to your project)? You can expose your C++ methods to a Lua script and allow the script to call methods inside the object using lightuserdata to pass pointers around. Note that I am using luna.h from the * SimplerCppBinding page.
First, start off with an object you want to expose to Lua- this is a simple class with an integer attribute and a string.
#include "object.h"
GameObject::GameObject(int x){
attribute = x;
}
int GameObject::getAttr(){
return (int)attribute;
}
void GameObject::setAttr(int set){
attribute = set;
}
void GameObject::setMessage(const char* new_message){
message.assign(new_message);
}
const char* GameObject::getMessage(){
return message.c_str();
}
GameObject::~GameObject(){
printf("deleted Object (%p)\n", this);
}
/**
* This is the main object that is actually used in the C++ code.
* It is to be manipulated by Lua through the Lua wrapper object (which will
* contain a pointer to this object).
*/
#ifndef _object_h_
#define _object_h_
// Notice that I don't need ANY Lua stuff in here...
#include <stdio.h>
#include <string>
class GameObject{
public:
GameObject(int x);
~GameObject();
int getAttr(void);
void setAttr(int balance);
void setMessage(const char* new_message);
const char* getMessage(void);
private:
int attribute;
std::string message;
};
#endif
After you have your object that can now be modified through C++, you need to write a class to expose the object to Lua.
#include "luaobject.h"
LuaGameObject::LuaGameObject(lua_State *L){
real_object = (GameObject*)lua_touserdata(L, 1);
}
void LuaGameObject::setObject(lua_State *L){
real_object = (GameObject*)lua_touserdata(L, 1);
}
int LuaGameObject::setAttr(lua_State *L){
real_object->setAttr((int)luaL_checknumber(L, 1));
return 0;
}
int LuaGameObject::getAttr(lua_State *L){
lua_pushnumber(L, real_object->getAttr());
return 1;
}
int LuaGameObject::setMessage(lua_State *L){
real_object->setMessage(lua_tostring(L, 1));
return 0;
}
int LuaGameObject::getMessage(lua_State *L){
lua_pushstring(L, real_object->getMessage());
return 1;
}
LuaGameObject::~LuaGameObject(){
printf("deleted Lua Object (%p)\n", this);
}
/**
* This is the wrapper around the C++ object found in object.cc
* Everything this object has done to it is passed on FROM Lua to the real C++
* object through the pointer 'real_object'
* Notice that I kept the function names the same for simplicity.
*/
#ifndef _luaobject_h_
#define _luaobject_h_
// Need to include lua headers this way
extern "C" {
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
}
// I am using luna
#include "luna.h"
// The header file for the real C++ object
#include "object.h"
class LuaGameObject{
public:
// Constants
static const char className[];
static Luna<LuaGameObject>::RegType methods[];
// Initialize the pointer
LuaGameObject(lua_State *L);
~LuaGameObject();
void setObject(lua_State *L);
// Methods we will need to use
int getAttr(lua_State *L);
int setAttr(lua_State *L);
int getMessage(lua_State *L);
int setMessage(lua_State *L);
private:
// The pointer to the 'real object' defined in object.cc
GameObject* real_object;
};
#endif
Alright, now we have a way for Lua to gain access to this object, let us bring it together. Notice the key here is to pass the pointer to Lua using lightuserdata.
/**
* Main program to actually run the Lua code against the C++ object
*/
#include "object.h"
#include "luaobject.h"
// Define the Lua ClassName
const char LuaGameObject::className[] = "LuaGameObject";
// Define the methods we will expose to Lua
// Check luaobject.h for the definitions...
#define method(class, name) {#name, &class::name}
Luna<LuaGameObject>::RegType LuaGameObject::methods[] = {
method(LuaGameObject, setAttr),
method(LuaGameObject, getAttr),
method(LuaGameObject, getMessage),
method(LuaGameObject, setMessage),
{0,0}
};
int main(int argc, char *argv[]){
// Init Lua
lua_State *L = lua_open();
luaopen_base(L);
luaopen_table(L);
luaopen_io(L);
luaopen_string(L);
luaopen_math(L);
luaopen_debug(L);
// Register the LuaGameObject data type with Lua
Luna<LuaGameObject>::Register(L);
// In C++ - Create a GameObject for use in our program
GameObject temp(20);
temp.setMessage("I'm set in C++");
// Push a pointer to this GameObject to the Lua stack
lua_pushlightuserdata(L, (void*)&temp);
// And set the global name of this pointer
lua_setglobal(L,"gameobject");
printf("In C: %p => %d, %s\n", &temp, temp.getAttr(), temp.getMessage());
printf("Loading lua----------\n");
luaL_loadfile(L, argv[1]);
printf("lua is loaded--------\n");
printf("Running lua----------\n");
lua_pcall(L, 0, 0, 0);
printf("Lua is done----------\n");
//lua_setgcthreshold(L, 0); // collected garbage
luaL_loadfile(L, argv[1]);
printf("Running lua2---------\n");
lua_pcall(L, 0, 0, 0);
printf("Lua is done2---------\n");
// GC + Close out Lua
lua_close(L);
printf("In C++: %p => %d, %s\n", &temp, temp.getAttr(), temp.getMessage());
return 0;
}
Now, lets run a test program like this:
--[[ gameobject comes from the global parameters and is a pointer to the REAL C++ data. This is saved as lightuserdata in Lua and we preserve this pointer in the LuaGameObject. From there, we can manipulate ANYTHING from this pointer. --]] -- Lua Functions function printf(...) io.write(string.format(unpack(arg))) end -- This function uses the getX() methods in our target class function LuaGameObject:show() printf("LuaGameObject attribute = %d - %s\n", self:getAttr(), self:getMessage()) end -- Start up a new LuaGameObject wrapper class and pass the global gameobject -- C++ lightuserdata pointer into it b = LuaGameObject(gameobject) -- Call a Lua function on this object b:show() print('Now to work on the C++ object') -- Modify some of the parameters (gameobject->modify) b:setAttr(120) b:setMessage('Hey - Lua changes a string!') print('Lua is done changing...') b:show()
Finally, here's what happens:
./main gameobject.lua In C: 0xbfb1d630 => 20, I'm set in C Loading lua---------- lua is loaded-------- Running lua---------- LuaGameObject balance = 20 - I'm set in C Now to work on the C++ object Lua is done changing... LuaGameObject attribute = 120 - Hey - Lua changes a string! Lua is done---------- Running lua2--------- LuaGameObject attribute = 120 - Hey - Lua changes a string! Now to work on the C++ object Lua is done changing... LuaGameObject attribute = 120 - Hey - Lua changes a string! Lua is done2--------- deleted Lua Object (0x80642d0) deleted Lua Object (0x8064c40) In C: 0xbfb1d630 => 120, Hey - Lua changes a string! deleted Object (0xbfb1d630)
I hope you find this example useful- Lua is awesome!