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!