diff --git a/build/libaegisub/libaegisub.vcxproj b/build/libaegisub/libaegisub.vcxproj
index 370c3177a..db84d57e4 100644
--- a/build/libaegisub/libaegisub.vcxproj
+++ b/build/libaegisub/libaegisub.vcxproj
@@ -93,6 +93,7 @@
+
diff --git a/build/libaegisub/libaegisub.vcxproj.filters b/build/libaegisub/libaegisub.vcxproj.filters
index 7288ab0e1..06a808a0d 100644
--- a/build/libaegisub/libaegisub.vcxproj.filters
+++ b/build/libaegisub/libaegisub.vcxproj.filters
@@ -203,6 +203,9 @@
Lua\Modules
+
+ Lua
+
Lua
diff --git a/libaegisub/include/libaegisub/lua/ffi.h b/libaegisub/include/libaegisub/lua/ffi.h
new file mode 100644
index 000000000..0dc09f563
--- /dev/null
+++ b/libaegisub/include/libaegisub/lua/ffi.h
@@ -0,0 +1,62 @@
+// Copyright (c) 2014, Thomas Goyne
+//
+// Permission to use, copy, modify, and distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+//
+// Aegisub Project http://www.aegisub.org/
+
+#include
+
+#include
+
+namespace agi { namespace lua {
+static void register_lib_functions(lua_State *) {
+ // Base case of recursion; nothing to do
+}
+
+template
+void register_lib_functions(lua_State *L, const char *name, Func *func, Rest... rest) {
+ lua_pushvalue(L, -2); // push cast function
+ lua_pushstring(L, type_name::name().c_str());
+ // This cast isn't legal, but LuaJIT internally requires that it work, so we can rely on it too
+ lua_pushlightuserdata(L, (void *)func);
+ lua_call(L, 2, 1);
+ lua_setfield(L, -2, name);
+
+ register_lib_functions(L, rest...);
+}
+
+template
+void register_lib_table(lua_State *L, std::initializer_list types, Args... functions) {
+ static_assert((sizeof...(functions) & 1) == 0, "Functions must be alternating names and function pointers");
+
+ lua_getglobal(L, "require");
+ lua_pushstring(L, "ffi");
+ lua_call(L, 1, 1);
+
+ // Register all passed type with the ffi
+ for (auto type : types) {
+ lua_getfield(L, -1, "cdef");
+ lua_pushfstring(L, "typedef struct %s %s;", type, type);
+ lua_call(L, 1, 0);
+ }
+
+ lua_getfield(L, -1, "cast");
+ lua_remove(L, -2); // ffi table
+
+ lua_createtable(L, 0, sizeof...(functions) / 2);
+ register_lib_functions(L, functions...);
+ lua_remove(L, -2); // ffi.cast function
+ // Leaves lib table on the stack
+}
+
+} }
diff --git a/libaegisub/lua/modules/lfs.cpp b/libaegisub/lua/modules/lfs.cpp
index 7f81b2afd..2f6d377b1 100644
--- a/libaegisub/lua/modules/lfs.cpp
+++ b/libaegisub/lua/modules/lfs.cpp
@@ -15,11 +15,10 @@
// Aegisub Project http://www.aegisub.org/
#include "libaegisub/fs.h"
-#include "libaegisub/type_name.h"
+#include "libaegisub/lua/ffi.h"
#include
#include
-#include
using namespace agi::fs;
namespace bfs = boost::filesystem;
@@ -121,45 +120,21 @@ time_t get_mtime(const char *path, char **err) {
uintmax_t get_size(const char *path, char **err) {
return wrap(err, [=] { return Size(path); });
}
-
-template
-void push_ffi_function(lua_State *L, const char *name, T *func) {
- lua_pushvalue(L, -2); // push cast function
- lua_pushstring(L, agi::type_name::name().c_str());
- // This cast isn't legal, but LuaJIT internally requires that it work
- lua_pushlightuserdata(L, (void *)func);
- lua_call(L, 2, 1);
- lua_setfield(L, -2, name);
-}
}
extern "C" int luaopen_lfs_impl(lua_State *L) {
- lua_getglobal(L, "require");
- lua_pushstring(L, "ffi");
- lua_call(L, 1, 1);
-
- lua_getfield(L, -1, "cdef");
- lua_pushstring(L, "typedef struct DirectoryIterator DirectoryIterator;");
- lua_call(L, 1, 0);
-
- lua_getfield(L, -1, "cast");
- lua_remove(L, -2); // ffi table
-
- lua_createtable(L, 0, 12);
- push_ffi_function(L, "chdir", lfs_chdir);
- push_ffi_function(L, "currentdir", currentdir);
- push_ffi_function(L, "mkdir", mkdir);
- push_ffi_function(L, "rmdir", lfs_rmdir);
- push_ffi_function(L, "touch", touch);
- push_ffi_function(L, "get_mtime", get_mtime);
- push_ffi_function(L, "get_mode", get_mode);
- push_ffi_function(L, "get_size", get_size);
-
- push_ffi_function(L, "dir_new", dir_new);
- push_ffi_function(L, "dir_free", dir_free);
- push_ffi_function(L, "dir_next", dir_next);
- push_ffi_function(L, "dir_close", dir_close);
-
- lua_remove(L, -2); // ffi.cast function
+ agi::lua::register_lib_table(L, {"DirectoryIterator"},
+ "chdir", lfs_chdir,
+ "currentdir", currentdir,
+ "mkdir", mkdir,
+ "rmdir", lfs_rmdir,
+ "touch", touch,
+ "get_mtime", get_mtime,
+ "get_mode", get_mode,
+ "get_size", get_size,
+ "dir_new", dir_new,
+ "dir_free", dir_free,
+ "dir_next", dir_next,
+ "dir_close", dir_close);
return 1;
}
diff --git a/libaegisub/lua/modules/unicode.cpp b/libaegisub/lua/modules/unicode.cpp
index a11487449..0141c8d89 100644
--- a/libaegisub/lua/modules/unicode.cpp
+++ b/libaegisub/lua/modules/unicode.cpp
@@ -14,22 +14,11 @@
//
// Aegisub Project http://www.aegisub.org/
-#include
+#include
#include
-#include
namespace {
-template
-void push_ffi_function(lua_State *L, const char *name, T *func) {
- lua_pushvalue(L, -2); // push cast function
- lua_pushstring(L, agi::type_name::name().c_str());
- // This cast isn't legal, but LuaJIT internally requires that it work
- lua_pushlightuserdata(L, (void *)func);
- lua_call(L, 2, 1);
- lua_setfield(L, -2, name);
-}
-
template
char *wrap(const char *str, char **err) {
try {
@@ -42,17 +31,9 @@ char *wrap(const char *str, char **err) {
}
extern "C" int luaopen_unicode_impl(lua_State *L) {
- lua_getglobal(L, "require");
- lua_pushstring(L, "ffi");
- lua_call(L, 1, 1);
- lua_getfield(L, -1, "cast");
- lua_remove(L, -2); // ffi table
-
- lua_createtable(L, 0, 3);
- push_ffi_function(L, "to_upper_case", wrap>);
- push_ffi_function(L, "to_lower_case", wrap>);
- push_ffi_function(L, "to_fold_case", wrap>);
-
- lua_remove(L, -2); // ffi.cast function
+ agi::lua::register_lib_table(L, {},
+ "to_upper_case", wrap>,
+ "to_lower_case", wrap>,
+ "to_fold_case", wrap>);
return 1;
}