注意:Lua本身并不支持把C++类导入的,只支持函数,所以需要自己实现。
代码:
需要导出的类
class Enemy
{
protected:
char _name[32];
int _life;
public:
Enemy( const char *name )
{
strcpy(_name,name);
_life = 0;
}
~Enemy(void)
{}
void setLife( int life)
{
_life = life;
}
int getLife()
{
return _life;
}
};
中间类,工具类:将C++类导出给Lua
class LuaEnemy
{
public:
LuaEnemy()
{
}
~LuaEnemy(void)
{
}
static void Register( lua_State *L )
{
//创建一个table,压栈
lua_newtable(L);
//获取栈顶
int methodTable = lua_gettop(L);
//2. new metatable for L to save "__metatable", "__index", "__gc", etc
//创建元表,压栈,加入到lua系统注册表里了
luaL_newmetatable(L,"Enemy");
int metaTable = lua_gettop(L);
//3. metatable["__metatable"] = methodtable;同lua_settable(1,metatable)
//压栈,string:__metatable
lua_pushliteral(L,"__metatable");
//将methodTable这个表的副本再压栈
lua_pushvalue(L,methodTable);
//将__metatable和methodTable表做出一个键值对,放入元表中,压栈,并将上面两出栈,类似lua_settable
lua_rawset(L, metaTable);
//4. metatable["__index"] = methodtable ;同 lua_settable(1,metatable)
lua_pushliteral( L, "__index" );
lua_pushvalue( L, methodTable );
lua_rawset( L, metaTable );
//5. metatable["__gc"] = myFunction
lua_pushliteral( L, "__gc" );
lua_pushcfunction( L, gc_Enemy );
lua_rawset( L, metaTable );
//将metatable弹出栈,不在栈上了,还在系统表里
lua_pop(L,1);
//把一个新的 C 闭包压栈,当创建了一个 C 函数后,
//你可以给它关联一些值, 这就是在创建一个 C 闭包;
//接下来无论函数何时被调用,这些值都可以被这个函数访问到。
//为了将一些值关联到一个 C 函数上,
//首先这些值需要先被压入堆栈(如果有多个值,第一个先压)。
//接下来调用 lua_pushcclosure 来创建出闭包并把这个 C 函数压
//到栈上。
//参数 n 告之函数有多少个值需要关联到函数上。
//lua_pushcclosure 也会把这些值从栈上弹出。
lua_pushcclosure(L,LuaEnemy::setLife,0);
//设置到methodtable里
lua_setfield(L, -2, "setLife");
//将methodtable弹栈
lua_pop(L,1);
//导入到Lua
lua_register(L, "Enemy", LuaEnemy::create );
}
static int create( lua_State *L )
{
char* name= (char*)lua_tostring(L,-1);
Enemy* a = new Enemy(name);
void** p = (void**)lua_newuserdata( L, sizeof(void*));
*p = a;
luaL_getmetatable( L, "Enemy" );
//把一张表弹出栈,并将其设为给定索引处的值的元表。将Enemy这个元表关联到p上了
lua_setmetatable( L, -2 );
//返回P
return 1;
}
//释放内存
static int gc_Enemy( lua_State *L )
{
//获取索引处内存地址
Enemy* enemy = (Enemy*)(*(void**)lua_touserdata(L,-1));
delete enemy;
return 0;
}
//调用Enemy的setLife函数
static int setLife(lua_State *L)
{
int life = lua_tonumber(L,2);
//判断栈中索引为1的类型是否为用户自定义类型,如果不是会报错
luaL_checktype( L, 1, LUA_TUSERDATA );
//检查是否是Enemy元表
void* ud = luaL_checkudata( L, 1, "Enemy" );
Enemy* enemy = *(Enemy**)ud;
enemy->setLife(life);
return 0;
}
static int getLife(lua_State *L)
{
return 1;
}
}
Lua文件
print( "test lua access C++ class" )
local function main()
a = Enemy('aa')
b = Enemy('bb')
//相当于a:setLife(self,20),调用的是LuaEnemy里的setLife,所以栈上是有两个变量的
a:setLife(20);
b:setLife(123);
end
main()
print('finish');
使用
int main(int argc,char** argv)
{
lua_State* L = luaL_newstate();
luaopen_base(L);
luaopen_table(L);
luaopen_io(L);
luaopen_string(L);
luaopen_math(L);
LuaEnemy::Register(L);
if( luaL_dofile( L, "main.lua" ))
{
stackDump(L);
}
lua_close(L);
return 0;
}