Directory Iterator
29.1 Directory Iterator
Sebelumnya, kita menerapkan sebuah fungsi dir yang dikembalikan sebuah tabel dengan semua file dari suatu direktori yang ditentukan. Implementasi kita yang baru akan mengembalikan suatu iterator yang mengembalikan sebuah masukan baru setiap dipanggil. Dengan implementasi baru ini, kita akan dapat menyebrangi suatu direktori dengan suatu perulangan sebagai berikut :
for fname in dir(".") do print(fname) end
Untuk mengiterasi di atas sebuah direktori, pada C, kita membutuhkan sebuah struktur DIR. DIR diciptakan dari opendir dan harus dilepaskan secara eksplisit oleh suatu panggilan ke Untuk mengiterasi di atas sebuah direktori, pada C, kita membutuhkan sebuah struktur DIR. DIR diciptakan dari opendir dan harus dilepaskan secara eksplisit oleh suatu panggilan ke
Disamping perannya yang utama di dalam implementasi kita, userdatum ini mewakili sebuah direktori yang tidak perlu terlihat dari Lua. Fungsi dir mengembalikan sebuah fungsi iterator, ini adalah apa yang Lua lihat. Direktori mungkin adalah sebuah upvalue dari fungsi iterator. Sedemikian, fungsi iterator tersebut memiliki akses langsung ke struktur ini, tetapi kode Lua tidak memilikinya (dan memang tidak perlu).
Dengan itu, kita membutuhkan tiga fungsi C. Pertama, kita membutuhkan fungsi dir, sebuah pabrik yang Lua panggil untuk membuat iterator, fungsi tersebut harus dapat membuka struktur DIR dan meletakkannya sebagai sebuah upvalue dari fungsi iterator. Kedua, kita membutuhkan fungsi iterator. Ketiga, kita membutuhkan metamethod __gc, yang menutup struktur DIR. Seperti biasa, kita juga membutuhkan suatu fungsi ekstra untuk membuat pengaturan awal, seperti untuk membuat sebuah metatable untuk direktori-direktori dan untuk menginisialisasi metatable ini.
Mari kita mulai kodenya dengan fungsi dir :
#include <dirent.h> #include <errno.h>
/* forward declaration for the iterator function */ static int dir_iter (lua_State *L);
static int l_dir (lua_State *L) { const char *path = luaL_checkstring(L, 1);
/* create a userdatum to store a DIR address */ DIR **d = (DIR **)lua_newuserdata(L, sizeof(DIR *));
/* set its metatable */ luaL_getmetatable(L, "LuaBook.dir"); lua_setmetatable(L, -2);
/* try to open the given directory */ *d = opendir(path); if (*d == NULL) /* error opening the directory? */
luaL_error(L, "cannot open %s: %s", path, strerror(errno));
/* creates and returns the iterator function (its sole upvalue, the directory userdatum, is already on the stack top */
lua_pushcclosure(L, dir_iter, 1); return 1;
Suatu titik yang tidak terlihat di sini adalah bahwa kita harus membuat userdatum tersebut sebelum membuka direktori. Jika kita pertama kali membuka direktori tersebut, dan kemudian panggilan lua_newuserdata menaikkan suatu kesalahan, maka kita kehilangan struktur DIR tersebut. Dengan perintah yang benar, struktur DIR tersebut, pertama dibuat, dengan segera dihubungkan dengan userdatum. Apa pun yang terjadi setelah itu, metamethod __gc akan secepatnya melepaskan struktur tersebut.
Fungsi berikutnya adalah iterator itu sendiri :
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 more values to return */
Metamethod __gc menutup suatu direktori, tetapi harus mengambil satu tindakan pencegah : Karena kita membuat userdatum sebelum membuka direktori, userdatum tersebut akan dikumpulkan walau apa pun hasil dari opendir. Jika opendir gagal, tidak akan ada lagi yang dapat ditutup.
static int dir_gc (lua_State *L) { DIR *d = *(DIR **)lua_touserdata(L, 1); if (d) closedir(d); return 0;
Pada akhirnya, ini adalah fungsi yang membuka library one-function :
int luaopen_dir (lua_State *L) { luaL_newmetatable(L, "LuaBook.dir");
/* set its __gc field */ lua_pushstring(L, "__gc"); lua_pushcfunction(L, dir_gc); lua_settable(L, -3);
/* register the `dir' function */ lua_pushcfunction(L, l_dir); lua_setglobal(L, "dir");
return 0; }
Keseluruhan contoh ini memiliki suatu pencari kesalahan yang menarik. Pada mulanya, mungkin nampak dir_gc itu perlu memeriksa apakah argumennya adalah suatu direktori. Dengan cara lain, user yang berniat jahat dapat memanggilnya dengan jenis userdata yang lain (sebuah file, misalnya), dengan konsekuensi celaka. Bagaimanapun, tidak ada cara untuk sebuah program Lua untuk mengakses fungsi ini. Fungsi itu disimpan hanya pada metatable dari direktori dan program Lua tidak pernah mengakses direktori tersebut.