<轉> lua: userdata的metatable使用


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;

}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM