手动将c++类注册进lua中

本片博文是基于上篇博文“tolua++初始化过程”的中tolua++将C++类注册进lua的思想来写的。使用tolua++是为了方便,而手动将C++类注册进lua则是为了熟悉整个过程,成为高手的路总是孤独的。

首先,定义一个要注册进lua环境的C++类,定义如下:

CTest:

 1 class CTest
 2 {
 3 public:
 4     CTest();
 5     ~CTest();
 6 
 7 private:
 8     int a, b;
 9 
10 public:
11     void setA(int a);
12     void setB(int b);
13     int getAdd();
14 };
View Code

这个类的主要功能就是为类属性a和b赋值,然后求和,仅此而已。分别对应的函数为void setA(int a);void setB(int b);int getAdd();。

下面为这个CTest类做一个外壳,用于注册进lua环境。代码如下:

static int wrap_new(lua_State* L)
{
    CTest* ud = new CTest();
    *(CTest**)lua_newuserdata(L, sizeof(CTest*)) = ud;
    luaL_getmetatable(L, "CTest");
    if (lua_istable(L, -1))
    {
        lua_setmetatable(L, -2);
    }

    return 1;
}

static int wrap_collecor(lua_State*L)
{
    if (lua_isuserdata(L, 1))
    {
        CTest* ud = (CTest*)lua_touserdata(L, 1);
        delete ud;
    }
    else
    {
        lua_error(L);
    }
    
    return 0;
}

static int wrap_setA(lua_State*L)
{
    if (lua_isuserdata(L, 1))
    {
        CTest*ud = (CTest*)lua_touserdata(L, 1);
        int a = lua_tointeger(L, 2);
        ud->setA(a);
    }
    else
    {
        lua_error(L);
    }

    return 0;
}

static int wrap_setB(lua_State*L)
{
    if (lua_isuserdata(L, 1))
    {
        CTest*ud = (CTest*)lua_touserdata(L, 1);
        int a = lua_tointeger(L, 2);
        ud->setB(a);
    }
    else
    {
        lua_error(L);
    }

    return 0;
}

static int wrap_getAdd(lua_State*L)
{
    if (lua_isuserdata(L, 1))
    {
        CTest*ud = (CTest*)lua_touserdata(L, 1);
        int sum = ud->getAdd();
        lua_pushinteger(L, sum);
    }
    else
    {
        lua_error(L);
    }

    return 1;
}

static int wrap_index_event(lua_State*L)
{
    if (!lua_isuserdata(L, 1))
    {
        lua_error(L);
        return 0;
    }
    
    if (lua_getmetatable(L, 1))
    {
        lua_pushvalue(L, 2);
        lua_rawget(L, -2);
        if (lua_isfunction(L, -1))
            return 1;
        else
            lua_error(L);
    }
}
View Code

下面的两个函数是将上面的函数注册进lua环境的动作:

void Register(lua_State*L, char* name, luaL_Reg* st)
{
    luaL_getmetatable(L, name);
    if (lua_isnil(L, -1))
    {
        luaL_newmetatable(L, name);

        for (int i = 0; st[i].func; i++)
        {
            lua_pushstring(L, st[i].name);
            lua_pushcfunction(L, st[i].func);
            lua_rawset(L, -3);
        }
    }
    lua_setglobal(L, name);

    return;
}

void Test_Register(lua_State*L)
{
    luaL_Reg st[] = 
    {
        { "__index", wrap_index_event },
        { "__gc", wrap_collecor },
        { "new", wrap_new },
        { "setA", wrap_setA },
        { "setB", wrap_setB },
        {"getAdd", wrap_getAdd},
        {NULL, NULL}
    };    

    Register(L, "CTest", st);
}
View Code

从代码可以看到,在Test_Register中调用了Register函数,在Test_Register中将相应的外壳函数组成一个luaL_Reg型的数组,然后调用Register进行注册。假如要增加新的函数,则只需在luaL_Reg的数组中增加一条即可,但需要在{NULL, NULL}的前面。

接下来就可以在main函数中进行注册,并调用执行lua文件。main函数的代码如下:

int _tmain(int argc, _TCHAR* argv[])
{
    lua_State*L = luaL_newstate();
    Test_Register(L);

    if (luaL_loadfile(L, "test.lua") || lua_pcall(L, 0, 0, 0))
    {

    }

    lua_close(L);

    return 0;
}

lua文件如下:

test = CTest:new()

test:setA(1)
test:setB(2)

print(test:getAdd())

其中的技巧,在于为__index注册的回调函数,在其中查找对应类的成员函数的外壳函数。

在每一个回调函数中,栈底为userdata,如果有参数的话,则后面跟的就是参数。

 

posted @ 2016-10-11 16:00  太极者  阅读(400)  评论(0编辑  收藏  举报