C++导出类到lua中,对metatable和闭包的应用

注意: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;
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值