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

Lua_第28章 資源管理(上)

編輯:關於C

在前面一章介紹的數組實現方法,我們不必擔心如何管理資源,只需要分配內存。 每一個表示數組的 userdatum 都有自己的內存,這個內存由 Lua 管理。當數組變為垃圾 (也就是說,當程序不需要)的時候,Lua 會自動收集並釋放內存。

生活總是不那麼如意。有時候,一個對象除了需要物理內存以外,還需要文件描述符、窗口句柄等類似的資源。(通常這些資源也是內存,但由系統的其他部分來管理)。 在這種情況下,當一個對象成為垃圾並被收集的時候,這些相關的資源也應該被釋放。一些面向對象的語言為了這種需要提供了一種特殊的機制(稱為 finalizer或者析構器)。Lua以_gc 元方法的方式提供了 finalizers。這個元方法只對 userdata 類型的值有效。當一個userdatum 將被收集的時候,並且usedatum 有一個_gc 域,Lua 會調用這個域的值 (應該是一個函數):以userdatum 作為這個函數的參數調用。這個函數負責釋放與 userdatum 相關的所有資源。

為了闡明如何將這個元方法和 API作為一個整體使用,這一章我們將使用 Lua擴展應用的方式,介紹兩個例子。第一個例子是前面己經介紹的遍歷一個目錄的函數的另一種實現。第二個例子是一個綁定 ExpatCExpat 開源的 XML 解析器)實現的 XML 解析 器。

29.1 目錄迭代器

前面我們實現了一個 dir 函數,給定一個目錄作為參數,這個函數以一個table的方 式返回目錄下所有文件。我們新版本的dir函數將返回一個迭代子,每次調用這個迭代 子的時候會返回目錄中的一個入口(entry)。按新版本的實現方式,我們可以使用循環 來遍歷整個目錄:

 

for fname in dir(".") do print(fname)  end

 

在 C 語言中,我們需要 DIR這種結構才能夠迭代一個目錄。通過 opendir 才能創建 一個 DIR 的實例,並且必須顯式的調用 closedir 來釋放資源。我們以前實現的 dir 用一個 本地變量保存 DIR 的實例,並且在獲取目錄中最後一個文件名之後關閉實例。但我們新實現的 dir中不能在本地變量中保存 DIR 的實例,因為有很多個調用都要訪問這個值,另外,也不能僅僅在獲取目錄中最後一個文件名之後關閉目錄。如果程序循環過程中中斷退出,迭代子根本就不會取得最後一個文件名,所以,為了保證 DIR 的實例一定能夠被釋放掉,我們將它的地址保存在一個 userdatum 中,並使用這個 userdatum 的 gc的 元方法來釋放目錄結構。

盡管我們實現中userdatum的作用很重要,但這個用來表示一個目錄的userdatum,並不需要在Lua可見范圍之內。Dir函數返回一個迭代子函數,迭代子函數需要在Lua的可見 范圍之內。目錄可能是迭代子函數的一個upvalue。這樣一來,迭代子函數就可以直接訪問這個結構(指目錄結構,即userdatum),但是Lua不可以(也不需要)訪問這個結構。

總的來說,我們需要三個 C 函數第一,dir 函數,一個 Lua 調用他產生迭代器的 工廠,這個函數必須打開 DIR結構並將他作為迭代函數的 upvalue。第二,我們需要一 個迭代函數。第三,_gc 元方法,負責關閉 DIR 結構。一般來說,我們還需要一個額外的函數來進行一些初始的操作,比如為目錄創建 metatable,並初始化這個 metatable。

首先看我們的 dir 函數:

 

#include 
#include 
 
/* forward declaration for the iteratorfunction */
static int dir_iter (lua_State *L);
 
static int l_dir (lua_State *L) {
   const char *path = luaL_checkstring(L, 1);
 
     /* create auserdatum to storea DIR address */
    DIR **d = (DIR**)lua_newuserdata(L, sizeof(DIR *));
 
     /* set its metatable */ 
     luaL_getmetatable(L, "LuaBook.dir"); 
     ua_setmetatable(L, -2);
 
      /* try toopen the givendirectory */
      *d =opendir(path);
       if (*d == NULL)/* error openingthe directory? */
       luaL_error(L, "cannot open %s: %s", path, strerror(errno));
 
         /* createsand returns theiterator function (its sole upvalue,the directory userdatum, is already on the stacktop */
         lua_pushcclosure(L, dir_iter, 1);
         return 1;
}

這兒有一點需要注意的,我們必須在打開目錄之前創建 userdatum。如果我們先打開 目錄,然後調用 lua_newuserdata 會拋出錯誤,這樣我們就無法獲取DIR 結構。按照正確 的順序,DIR結構一旦被創建,就會立刻和 userdatum 關聯起來;之後不管發生什麼,_gc元方法都會自動的釋放這個結構。

第二個函數是迭代器:

 

static int dir_iter (lua_State *L) {
    DIR *d = *(DIR**)lua_touserdata(L, lua_upvalueindex(1));
     struct dirent *entry;
     if ((entry = readdir(d)) != NULL) { 
      lua_pushstring(L, entry->d_name);
      return 1;
}
else return 0; /* no morevalues to return*/
}

gc 元方法用來關閉目錄,但有一點需要小心:因為我們在打開目錄之前創建 userdatum,所以不管 opendir 的結果是什麼userdatum 將來都會被收集。如果 opendir 失敗,將來就沒有什麼可以關閉的了:

 

static int dir_gc (lua_State *L) {
   DIR *d = *(DIR**)lua_touserdata(L, 1);
   if (d) closedir(d);
   return 0;
}

最後一個函數打開這個只有一個函數的庫:

 

int luaopen_dir (lua_State *L) {
    luaL_newmetatable(L, "LuaBook.dir");
   /* set its  gcfield */ 
   lua_pushstring(L, "_gc"); 
   lua_pushcfunction(L, dir_gc); 
   lua_settable(L,-3);

   /* register the  dirfunction */ 
   lua_pushcfunction(L, l_dir); 
   lua_setglobal(L, "dir");
 
return 0;
}

整個例子有一個注意點。開始的時候,dir_gc 看起來應該檢查他的參數是否是一個目錄。否則,一個惡意的使用者可能用其他類型的參數(比如,文件)調用這個函數導 致嚴重的後果。然而,在 Lua 程序中無法訪問這個函數:他被存放在目錄的 metatable 中,Lua 程序從來不會訪問這些目錄。

 

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