按路径要求 Lua 中的自定义 C 模块

Require custom C module in Lua by path

这是一个困扰我很久的问题

在我将自定义函数编写并编译到共享库中后,require('mylib') 仅当共享库与我从中调用它的脚本直接位于同一目录中时才有效。

尝试 require('/path/to/mylib') 或类似绝对路径的任何尝试都失败了。此外,"backtracking" 通过相对路径(即使用 .. 也失败了。

那么如何指定bin目录,或者共享库输出到哪里呢?

好吧,根据 Lua documentation on the require call(这里使用 Lua 5.2),加载程序在几个地方查找这些可加载模块。

似乎 require() 使用所谓的 "searchers"(上面链接的文档)来确定在哪里可以找到这些模块。总共有四个搜索器。来自文档:

The first searcher simply looks for a loader in the package.preload table.

The second searcher looks for a loader as a Lua library, using the path stored at package.path. The search is done as described in function package.searchpath.

The third searcher looks for a loader as a C library, using the path given by the variable package.cpath. Again, the search is done as described in function package.searchpath. For instance, if the C path is the string "./?.so;./?.dll;/usr/local/?/init.so" the searcher for module foo will try to open the files ./foo.so, ./foo.dll, and /usr/local/foo/init.so, in that order. Once it finds a C library, this searcher first uses a dynamic link facility to link the application with the library. Then it tries to find a C function inside the library to be used as the loader. The name of this C function is the string "luaopen_" concatenated with a copy of the module name where each dot is replaced by an underscore. Moreover, if the module name has a hyphen, its prefix up to (and including) the first hyphen is removed. For instance, if the module name is a.v1-b.c, the function name will be luaopen_b_c.

The fourth searcher tries an all-in-one loader. It searches the C path for a library for the root name of the given module. For instance, when requiring a.b.c, it will search for a C library for a. If found, it looks into it for an open function for the submodule; in our example, that would be luaopen_a_b_c. With this facility, a package can pack several C submodules into one single library, with each submodule keeping its original open function.

我们使用的搜索器是第三个:它用于任何共享库(.dll 或 .so),这通常是我们自定义 C 模块的构建方式。

使用 模板 字符串(带问号的那个),搜索器将查找每个指定的路径,替换 require() 的参数的问号。为了指定第三个搜索器的路径,必须设置(或附加到)package.cpath,然后调用 require().

所以也许您的目录结构为

- ROOT
    |-lua
    |-bin

其中 lua 包含 script.luabin 包含 mylib.so

加载mylib.so,只需要script.lua中的这两行代码:

package.cpath = '/ROOT/bin/?.so;' .. package.cpath
libfuncs = require('mylib')

注意:注意分号。如果您附加(与上面的前置相反),请确保在添加的路径上以分号开头。它不存在购买默认值。否则,您的新路径将合并到当前默认的 cpath,即 ./?.so.

如果您只想将系统配置为使用某些路径来存储 那里的库最好的方法就是使用 LUA_PATHLUA_CPATH 环境变量。 例如LUA_CPATH=/path/to/?.so。或者对于特定的 Lua 版本,例如 LUA_CPATH_5_3=/path/to/?.so.

但是如果你有一些库的完整路径并且只想从这个目录加载这个特定的库你可以使用 package.loadlib 函数。

local function loadlib(path, name)
  local sep = string.sub(package.config, 1, 1)
  local file = path .. sep .. name .. ((sep == '/') and '.so' or '.dll')
  local func = 'luaopen_' .. name
  local loader, msg = package.loadlib(file, func)
  assert(loader, msg)
  return assert(loader())
end

local mylib = loadlib([[/path/to]], 'mylib')