// Exercise 30.1: // Modify the dir_iter function in the directory example // so that it closes the DIR structure when it reaches // the end of the traversal. // With this change, // the program does not need to wait for a garbage collection // to release a resource that it knows it will not need anymore. // (When you close the directory, // you should set the address stored in the userdatum to NULL, // to signal to the finalizer that the directory is already closed. // Also, function dir_iter will have to check whether the directory // is not closed before using it.) #define LUA_COMPAT_APIINTCASTS #include #include #include #include #include #include #include #include #include #include #include #include "lua.hpp" namespace { bool AbortWithMessage(const char* message) { std::cout << message << std::endl; std::abort(); } bool LoadFile(lua_State* state, const std::string& filename) { return !(luaL_loadfile(state, filename.c_str()) || lua_pcall(state, 0, 0, 0)); } int dir_iter(lua_State *L) { DIR** dir_ptr = (DIR**) lua_touserdata(L, lua_upvalueindex(1)); struct dirent* entry; if (*dir_ptr && (entry = readdir(*dir_ptr)) != NULL) { lua_pushstring(L, entry->d_name); return 1; } // Directory is empty. if (*dir_ptr) { closedir(*dir_ptr); *dir_ptr = nullptr; } return 0; } int l_dir(lua_State *L) { const char *path = luaL_checkstring(L, 1); DIR** d = (DIR**) lua_newuserdata(L, sizeof(DIR*)); luaL_getmetatable(L, "LuaBook.dir"); lua_setmetatable(L, -2); *d = opendir(path); if (*d == NULL) { luaL_error(L, "cannot open %s: %s", path, strerror(errno)); } lua_pushcclosure(L, dir_iter, 1); return 1; } int dir_gc(lua_State *L) { DIR* d = *(DIR**) lua_touserdata(L, 1); if (d) { closedir(d); } return 0; } const struct luaL_Reg dirlib[] = { {"open", l_dir}, {NULL, NULL} }; int luaopen_dir(lua_State *L) { luaL_newmetatable(L, "LuaBook.dir"); lua_pushcfunction(L, dir_gc); lua_setfield(L, -2, "__gc"); luaL_newlib(L, dirlib); lua_setglobal(L, "dir"); return 0; } } // namespace int main(int argc, char* argv[]) { if (argc < 2) { std::cout << "Please, specify lua script file to run." << std::endl; return 0; } lua_State* L = luaL_newstate(); luaL_openlibs(L); luaopen_dir(L); if (!LoadFile(L, std::string(argv[1]))) { AbortWithMessage(lua_tostring(L, -1)); return -1; } return 0; }