Lua Class Wrapper Tutorial
Posted: Sat Aug 18, 2012 11:58 am
I think I should contribute to the community by making this tutorial, I assume it is the only one of it's kind as I spent hours searching for the code snippet. Basically if you use C++ and want to integrate your classes into Lua but don't know how then I have an 81 line solution for you. Keep in mind, I wont go over setting up lua as there is a tutorial on this forum here: viewtopic.php?f=6&t=6564&p=76794&hilit= ... %3B#p76794
Requirments:
To use the class make a header file and paste the following code in:
The code may look a bit complicated but essentially, it is quite simple, the 'Register' function takes your class and all it's functions and passes them into a metatable. The 'contructor' function is used to let lua know what your classes contructor is.
The functions 'thunk' and 'RegisterTable' I have not used yet, you may experiment with them at your own will.
Now this is the important part, you must now setup a lua class, here is an example of a class I am using to draw a sprite from lua:
Two things to notice, all the functions MUST have an integer return type, the second thing is that if you look at the bottom of the class you will see these two lines:
These lines are for setting up our class with lua, the only line that will matter the most is the first one as we will be telling lua what are class is called using that variable.
So now we need to move onto telling lua what are class does, I recommend putting this in your main.cpp are where you have setup lua, also you do not need to put it in a function like 'main'. The following snippet tells lua the functions of the class above:
Now to explain, the first line uses the 'className[]' variable from the class definition, here we are setting it to 'Sprite', this is what we will type into lua to create the object. The following line just gets ready to tell lua the functions. You may notice that it looks a bit like an 'enum' definition', lets take the first function as an example:
Firstly, the 'GetX' in double quotations is what we will type into in lua to use the function, you do not have to call it exactly what the function is called, but I recommend you do so to avoid future confusion. The second part is getting a reference of the function from the class, notice that you don't need the brackets after the function.
Dont worry, we are almost there!
Now in a function, preferably 'main' and after you have setup a 'lua_State', we need to register our class, this will send the 'Register[]' array, from the snippet above, to lua. Here is the line of code:
You may now realise why we need to put all this code where we have setup lua, we are registering the class to a lua_State, mine is called 'L', you may call yours something different.
Finally (Yes!) I will show you how to use our class in lua, before hand make sure you are calling your script from C++ using 'luaL_dofile' before doing this:
I hope this snippet is self explanitory, the only thing you need to keep in mind is the ':' before you type in the function.
That's it! Also, please note the class example I have given you may work, I am not sure, it would be best to use your own. Hope this helps!
Requirments:
- - A C++ IDE
- Lua setup up with your IDE
- A Brain (Optional)
To use the class make a header file and paste the following code in:
Code: Select all
template<class T> class Luna {
public:
static void Register(lua_State *L) {
lua_pushcfunction(L, &Luna<T>::constructor);
lua_setglobal(L, T::className);
luaL_newmetatable(L, T::className);
lua_pushstring(L, "__gc");
lua_pushcfunction(L, &Luna<T>::gc_obj);
lua_settable(L, -3);
}
static int constructor(lua_State *L) {
T* obj = new T(L);
lua_newtable(L);
lua_pushnumber(L, 0);
T** a = (T**)lua_newuserdata(L, sizeof(T*));
*a = obj;
luaL_getmetatable(L, T::className);
lua_setmetatable(L, -2);
lua_settable(L, -3); // table[0] = obj;
for (int i = 0; T::Register[i].name; i++) {
lua_pushstring(L, T::Register[i].name);
lua_pushnumber(L, i);
lua_pushcclosure(L, &Luna<T>::thunk, 1);
lua_settable(L, -3);
}
return 1;
}
static int thunk(lua_State *L) {
int i = (int)lua_tonumber(L, lua_upvalueindex(1));
lua_pushnumber(L, 0);
lua_gettable(L, 1);
T** obj = static_cast<T**>(luaL_checkudata(L, -1, T::className));
lua_remove(L, -1);
return ((*obj)->*(T::Register[i].mfunc))(L);
}
static int gc_obj(lua_State *L) {
T** obj = static_cast<T**>(luaL_checkudata(L, -1, T::className));
delete (*obj);
return 0;
}
struct RegType {
const char *name;
int(T::*mfunc)(lua_State*);
};
// Directly add the new class
static T* RegisterTable(lua_State *L)
{
luaL_newmetatable(L, T::className);
lua_pushstring(L, "__gc");
lua_pushcfunction(L, &Luna<T>::gc_obj);
lua_settable(L, -3);
T* obj = new T(L);
lua_newtable(L);
lua_pushnumber(L, 0);
T** a = (T**)lua_newuserdata(L, sizeof(T*));
*a = obj;
luaL_getmetatable(L, T::className);
lua_setmetatable(L, -2);
lua_settable(L, -3); // table[0] = obj;
for (int i = 0; T::Register[i].name; i++)
{
lua_pushstring(L, T::Register[i].name);
lua_pushnumber(L, i);
lua_pushcclosure(L, &Luna<T>::thunk, 1);
lua_settable(L, -3);
}
lua_setglobal(L, T::className);
return obj;
}
};
The code may look a bit complicated but essentially, it is quite simple, the 'Register' function takes your class and all it's functions and passes them into a metatable. The 'contructor' function is used to let lua know what your classes contructor is.
The functions 'thunk' and 'RegisterTable' I have not used yet, you may experiment with them at your own will.
Now this is the important part, you must now setup a lua class, here is an example of a class I am using to draw a sprite from lua:
Code: Select all
class LuaSprite {
private:
SDL_Surface* surface;
SDL_Rect rect;
int xvel, yvel;
std::string tag;
int alpha;
public:
LuaSprite(lua_State* L) {
}
int GetSurface(lua_State *L) {
lua_pushlightuserdata(L, (void*)surface);
return 1;
}
int GetRect(lua_State *L) {
lua_pushlightuserdata(L, (void*)&rect);
return 1;
}
int GetWidth(lua_State *L) {
lua_pushnumber(L, rect.w);
return 1;
}
int GetHeight(lua_State *L) {
lua_pushnumber(L, rect.h);
return 1;
}
int GetAlpha(lua_State *L) {
lua_pushnumber(L, alpha);
return 1;
}
//~LuaSprite() {
// SDL_FreeSurface(surface);
//}
int Update(lua_State *L);
int Show(lua_State *L)
{
SDL_SetAlpha(surface, SDL_SRCALPHA, alpha);
SDL_BlitSurface(surface, NULL, SDL_GetVideoSurface(), &rect);
return 1;
}
int LuaSprite::LoadImage(lua_State* L)
{
SDL_Surface* o;
o = IMG_Load(luaL_checkstring(L, 2));
SDL_DisplayFormatAlpha(o);
SDL_SetColorKey(o, SDL_SRCCOLORKEY | SDL_RLEACCEL, 0xFF00FF);
alpha = 255;
surface = o;
return 1;
}
int SetX(lua_State *L) {
rect.x = (int)luaL_checknumber(L, 2);
return 1;
}
int SetY(lua_State *L) {
rect.y = (int)luaL_checknumber(L, 2);
return 1;
}
int SetAlpha(lua_State *L) {
alpha = (int)luaL_checknumber(L, 2);
return 1;
}
int SetTag(lua_State* L) {
tag = luaL_checkstring(L, 2);
return 1;
}
int GetX(lua_State *L) {
lua_pushnumber(L, rect.x);
return 1;
}
int GetY(lua_State *L) {
lua_pushnumber(L, rect.y);
return 1;
}
int GetTag(lua_State *L) {
lua_pushstring(L, tag.c_str());
return 1;
}
static const char className[];
static const Luna<LuaSprite>::RegType Register[];
};
Code: Select all
static const char className[];
static const Luna<LuaSprite>::RegType Register[];
So now we need to move onto telling lua what are class does, I recommend putting this in your main.cpp are where you have setup lua, also you do not need to put it in a function like 'main'. The following snippet tells lua the functions of the class above:
Code: Select all
const char LuaSprite::className[] = "Sprite";
const Luna<LuaSprite>::RegType LuaSprite::Register[] = {
{ "GetX", &LuaSprite::GetX },
{ "GetY", &LuaSprite::GetY },
{ "GetWidth", &LuaSprite::GetWidth },
{ "GetHeight", &LuaSprite::GetHeight },
{ "GetAlpha", &LuaSprite::GetAlpha },
{ "GetTag", &LuaSprite::GetTag },
{ "SetX", &LuaSprite::SetX },
{ "SetY", &LuaSprite::SetY },
{ "SetAlpha", &LuaSprite::SetAlpha },
{ "SetTag", &LuaSprite::SetTag },
{ "GetRect", &LuaSprite::GetRect },
{ "Show", &LuaSprite::Show },
{ "LoadImage", &LuaSprite::LoadImage },
{ 0 }
};
Code: Select all
{ "GetX", &LuaSprite::GetX },
Dont worry, we are almost there!
Now in a function, preferably 'main' and after you have setup a 'lua_State', we need to register our class, this will send the 'Register[]' array, from the snippet above, to lua. Here is the line of code:
Code: Select all
Luna<LuaSprite>::Register(L);
Finally (Yes!) I will show you how to use our class in lua, before hand make sure you are calling your script from C++ using 'luaL_dofile' before doing this:
Code: Select all
mysprite = Sprite() --Make a Sprite object
function SomeFunction()
mysprite:LoadImage("imagepath")
mysprite:SetX(45)
--etc...
end
That's it! Also, please note the class example I have given you may work, I am not sure, it would be best to use your own. Hope this helps!