1 如何封裝c++的指針
對於c++對象的lua包裝,我們可以使用
template<typename T>
struct luaUserdataWrapper
{
luaUserdataWrapper() {}
luaUserdataWrapper(const T& d) : data(d) {}
T data;
};
class CObject
{
public:
int v[10];
};
typedef luaUserdataWrapper<CObject*> luaObject;
這樣就可以在c代碼中,按照如下方法向lua中添加生成CObject的對象的C函數:
int NewObject( lua_State* L )
{
luaObject* wrapper = (luaObject*) lua_newuserdata( L, sizeof(luaObject) );
wrapper->data = new CObject;
return 1;
}
lua_newuserdata函數把wrapper存放在棧頂位置,作為NewObject的返回值。
wrapper的生存期由lua負責,而wrapper->data的生命期則由程序員自己負責。
在lua代碼中的使用方法是:
obj = NewObject() --調用C函數
2 使用metatable
如果此時我們想在lua中使用如下語法:
obj[5]=20
value = obj[5]
則需要我們為luaObject添加metatable屬性。
步驟1:
在lua代碼中的普通表,不能作為userdata的metatable。必須使用luaL_newmetatable創建的表才能作為userdata的metatable。
在openlib函數中,添加一個userdata 的 metatable表,
int OnOpenlib( lua_State* L )
{
...
luaL_newmetatable( L, “ObjectMetatable");
}
luaL_newmetatable把新創建的表放在棧頂。
注意:新創建的ObjectMetatable表僅在棧中被聲明,並沒有加入到lua代碼中。如果在以后的lua代碼中使用ObjectMetatable.__index等操作,會提示ObjectMetatable:a nil value。
步驟2:
這是我們重寫上面的New方法。
int NewObject( lua_State* L )
{
luaObject* wrapper = (luaObject*) lua_newuserdata( L, sizeof(luaObject) );
wrapper->data = new CObject;
luaL_getmetatable( L, ”ObjectMetatable“);
lua_setmetatable( L, -2 );
return 1;
}
這樣我們就為新生成的luaObject對象添加metatable。
luaL_getmetatable( L, ”ObjectMetatable“)獲取ObjectMetatable表,並放入棧頂。
lua_setmetatable( L, -2 )則把新生成的userdata的metatable設置為ObjectMetatable。
步驟3:
value = obj[5]的取下標操作對應的是__index域,而
obj[5]=2;對應的是__newindex域。
所以我們需要添加ObjectMetatable的__index,__newindex域。
我們重寫int OnOpenlib( lua_State* L )方法
int OnOpenlib( lua_State* L )
{
...
luaL_newmetatable( L, “ObjectMetatable");
lua_pushstring( L, "__index" );
lua_pushcfunction( L, GetValue );
lua_rawset( L, -3 ); // ObjectMetatable.__index = GetHorizonValue
lua_pushstring( L, "__newindex" );
lua_pushcfunction( L, SetValue );
lua_rawset( L, -3 ); // ObjectMetatable.__newindex = GetHorizonValue
}
GetValue 與SetValue 是自定義的C函數,可以不用被注冊到lua代碼中。
在lua中調用
v=obj[5]
時,會觸發元函數metatable.__index,obj、5會被依次入棧。
所以GetValue方法我們可以寫為
int GetValue(lua_State* L)
{
luaL_checktype(L, -1, LUA_TNUMBER);
luaL_checktype(L, -2, LUA_TUSERDATA);
luaObject* wrapper = (luaObject*) lua_touserdata(L, -2);
ASSERT( wrapper->data != NULL );
if ( wrapper->data == NULL )
{
lua_pushstring( L, "GetHorizonValue: NULL wrapper " );
lua_error(L);
return 1;
}
int index = (int)(float)lua_tonumber(L, -1);
int value = wrapper->data.v[index];
lua_pushnumber( L, value );
return 1;
}