Easy Tutorial
❮ Lua Database Access Lua Error Handling ❯

Lua Modules and Packages

Modules are akin to encapsulated libraries. Starting from Lua 5.1, Lua introduced a standard module management mechanism, allowing common code to be placed in a file and called elsewhere via API interfaces. This promotes code reuse and reduces code coupling.

A Lua module is a table composed of variables, functions, and other known elements. Therefore, creating a module is straightforward: create a table, add the constants and functions you want to export to it, and then return this table. Below is an example of creating a custom module named module.lua, with the following file code format:

-- File named module.lua
-- Define a module named module
module = {}

-- Define a constant
module.constant = "This is a constant"

-- Define a function
function module.func1()
    io.write("This is a public function!\n")
end

local function func2()
    print("This is a private function!")
end

function module.func3()
    func2()
end

return module

As seen above, the structure of a module is essentially a table structure, so you can manipulate and call constants or functions within the module just as you would with elements in a table.

The function func2 is declared as a local variable within the block, indicating a private function, which cannot be accessed from outside the module. It must be called through a public function within the module.


The require Function

Lua provides a function called require to load modules. To load a module, simply call it like this:

require("<module_name>")

or

require "<module_name>"

After executing require, it returns a table composed of the module's constants and functions, and also defines a global variable containing this table.

test_module.lua File

-- test_module.lua file
-- module module is the module.lua mentioned earlier
require("module")

print(module.constant)

module.func3()

The above code execution result is:

This is a constant
This is a private function!

Alternatively, you can define an alias variable for the loaded module for easier access:

test_module2.lua File

-- test_module2.lua file
-- module module is the module.lua mentioned earlier
-- Alias variable m
local m = require("module")

print(m.constant)

m.func3()

The above code execution result is:

This is a constant
This is a private function!

Loading Mechanism

For custom modules, the module file cannot be placed in any directory. The require function has its own file path loading strategy, attempting to load modules from Lua files or C libraries.

The path for require to search for Lua files is stored in the global variable package.path. When Lua starts, it initializes this variable with the value of the environment variable LUA_PATH. If this environment variable is not found, it uses a default path defined at compile time.

If the LUA_PATH environment variable is not set, you can customize it. Open the .profile file in the current user's root directory (create it if it doesn't exist, or open the .bashrc file), for example, adding the "~/lua/" path to the LUA_PATH environment variable:

#LUA_PATH
export LUA_PATH="~/lua/?.lua;;"

File paths are separated by ";", and the final two ";;" indicate adding the original default path after the new path.

Next, update the environment variable parameters to make them effective immediately:

source ~/.profile

Suppose the value of package.path is:

/Users/dengjoe/lua/?.lua;./?.lua;/usr/local/share/lua/5.1/?.lua;/usr/local/share/lua/5.1/?/init.lua;/usr/local/lib/lua/5.1/?.lua;/usr/local/lib/lua/5.1/?/init.lua

Then calling require("module") will attempt to search the following directories for the target file:

/Users/dengjoe/lua/module.lua;
./module.lua
/usr/local/share/lua/5.1/module.lua
/usr/local/share/lua/5.1/module/init.lua
/usr/local/lib/lua/5.1/module.lua
/usr/local/lib/lua/5.1/module/init.lua

If the target file is found, package.loadfile is used to load the module. Otherwise, it searches for a C library.

The file path for searching is obtained from the global variable package.cpath, which is initialized from the environment variable LUA_CPATH.

The search strategy is the same as above, but now it searches for .so or .dll files. If found, require uses package.loadlib to load it.


C Packages

Lua and C can be easily combined by writing packages in C for Lua.

Unlike writing packages in Lua, C packages must be loaded and linked before use, typically through dynamic linking libraries in most systems.

Lua provides all dynamic linking functionality in a function called loadlib. This function takes two parameters: the absolute path of the library and the initialization function. A typical call example is as follows:

local path = "/usr/local/lua/lib/libluasocket.so"
local f = loadlib(path, "luaopen_socket")

The loadlib function loads the specified library and links it to Lua, but it does not open the library (i.e., it does not call the initialization function). Instead, it returns the initialization function as a Lua function, allowing us to call it directly in Lua.

If there is an error loading the dynamic library or finding the initialization function, loadlib returns nil and an error message. We can modify the previous code to check for errors and then call the initialization function:

local path = "/usr/local/lua/lib/libluasocket.so"
-- Or path = "C:\\windows\\luasocket.dll" for Windows
local f = assert(loadlib(path, "luaopen_socket"))
f()  -- Actually open the library

Typically, we expect the binary distribution library to include a stub file similar to the previous code segment. When installing the binary library, you can place it in any directory, just modify the stub file to reflect the actual path of the binary library.

Add the directory containing the stub file to LUA_PATH, allowing require to load the C library.

❮ Lua Database Access Lua Error Handling ❯