程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> 關於C語言 >> Lua學習筆記(十六)

Lua學習筆記(十六)

編輯:關於C語言

 

 

看了2天userdata, 寫了百來行代碼, 才對userdata略知一二.

userdata這東西, 可以理解為用戶自定義數據. 它是數據, 不是類型, 其實說白了, 就是一片內存. 通過一個簡單的API, 我們就能獲取一個userdata:

view sourceprint?

void *lua_newuserdata (lua_State *L, size_t size);

這個API一目了然, 創建好的userdata會被妥善安置在lua stack的頂部.

這裡有一個很有趣的地方, 就是我們能夠申請一段由lua管理的內存, 我聽說lua的gc還是蠻不錯的, 如果我可以把許多內存管理的工作扔給lua, 那真是太好了.  另一方面, 我覺得lua實在是不行, 還是自己管理內存比較靠譜, 但是我又需要讓lua能比較直接的操作我寫的C模塊所申請的一片內存. 面對2種不同的需求, lua提供的機制都能夠讓我們一一應對.

1. 申請一片比較大的內存, 將實例放在這片內存裡.

2. 申請小段內存, 在這片內存中保存實例地址, 將實例放在C/C++模塊申請的內存中.

在情況1中, 一旦lua的gc回收內存, C/C++實例就被銷毀. 第2種情況下, C/C++實例可以繼續存在.

這兩種解決方案都有可能被用來解決實際問題, 而第2種情況非常值得寫一個完整的例子來研究. 不過今天只看一下第1種情況吧.

為了研究第1種情況, 我打算做一個簡單的float數組. 下面是數組在C中的定義以及一些lua接口:

view sourceprint?

struct LuaArray

view sourceprint?

{

    int size;

    float data[1];  // 為了簡便, 我就這麼做了.

};

void InitArray(lua_State* pState);      // 這個函數不是向lua提供的接口. 只是用作初始化.

int NewArray(lua_State* pState);

int ReleaseArray(lua_State* pState);

int GetArrayValue(lua_State* pState);

int SetArrayValue(lua_State* pState);

int GetArrayLength(lua_State* pState);

int SumArray(lua_State* pState);

下面是InitArray函數的代碼:

view sourceprint?

static const char* LuaArrayTableName = "LuaArray";

static const luaL_Reg ArrayFunction[] = 

{

    {"__newindex",  SetArrayValue},

    {"__len",       GetArrayLength},

    {"__gc",        ReleaseArray},

    {"get",         GetArrayValue},

    {"sum",         SumArray},

    {"new",         NewArray},

    {NULL, NULL}

};

 

void InitArray(lua_State* pState)

{

    luaL_register(pState, LuaArrayTableName, ArrayFunction);

    lua_pushvalue(pState, -1);

    lua_setfield(pState, -2, "__index");

    lua_pop(pState, 1);

};

我們可以采用類似MFC中消息映射的一些宏來簡化LuaArrayTableName和ArrayFunction.

這裡, 我創建了一個lua table, 並讓這張表本身作為一張元表, 存儲於lua_State的context中.

一旦我們創建了這張元表, 就能讓我們的userdata和這張元表綁定. 這樣, 我們就能在lua中, 對userdata進行元表規定的操作.

先來看一下創建的代碼:

view sourceprint?

int NewArray(lua_State* pState)

{

    int elemCount = luaL_checkint(pState, 1);

    int memSize = sizeof(LuaArray) + elemCount * sizeof(float);

    LuaArray* pUData = (LuaArray*)lua_newuserdata(pState, memSize);

    pUData->size = elemCount;

    pUData->data[0] = 0.0f;

    for (int i = 1; i <= elemCount; ++i)

        pUData->data[i] = 0.0f;

         

    lua_getglobal(pState, LuaArrayTableName);

    lua_setmetatable(pState, -2);

     

    // ----------------------------------------------------------------------------

    // 在gc時使用, 沒有特別的意義.

    float* pExData = new float[10];

    memcpy_s((void*)pUData->data, sizeof(float), (void*)&pExData, sizeof(float*));

    // ----------------------------------------------------------------------------

     

    return 1;

}

第5行創建了userdata, 並在前端存儲LuaArray結構.

在lua中, 我們用這樣的代碼就能創建一個LuaArray:

view sourceprint?

arr = LuaArray.new(10)  -- 創建10個元素的LuaArray

設置LuaArray中的值, 獲取LuaArray中的值(省去所有檢測):

view sourceprint?

int SetArrayValue( lua_State* pState )

{

    LuaArray* pUData = (LuaArray*)lua_touserdata(pState, 1);    // 這裡可以做一些檢測

    int idx = luaL_checkint(pState, 2);

    float val = (float)luaL_checknumber(pState, 3);

    pUData->data[idx] = val;                                 

    return 0;

}

int GetArrayValue( lua_State* pState )

{

    LuaArray* pUData = (LuaArray*)lua_touserdata(pState, 1);

    int idx = luaL_checkint(pState, 2);

    lua_pushnumber(pState, (lua_Number)pUData->data[idx]);

    return 1;

}

lua中設置和獲取值的代碼如下:

view sourceprint?

arr[1] = 100

 

print(arr:get(1)) // 沒有arr[1]的原因在於元表中的__index屬性被用來指向元表本身.

其他函數大同小異. 有意思的是__gc事件.

在lua中的變量都是引用, 當一個對象沒有任何變量引用的時候, 就會被lua的gc回收.

在lua中這樣寫, 就會讓代碼回收:

view sourceprint?

arr = nil

在相應__gc事件的C/C++函數中, 我們就能對剛才申請的內存進行釋放:

view sourceprint?

int ReleaseArray( lua_State* pState )

{

    LuaArray* pUData = (LuaArray*)lua_touserdata(pState, 1);

    float* pExData;

    memcpy_s((void*)&pExData, sizeof(float*), (void*)pUData->data, sizeof(float));

 

    delete [] pExData;

 

    return 0;

}

userdata+metatable的機制, 讓我們能從C/C++的角度為lua提供數據和類型的擴展. 本文中對這套機制的使用方法僅僅是一個簡陋的實驗方法, 具體項目中可以加入許多改進以應對不同需求.

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved