Módulos y paquetes de Lua

Un módulo es similar a una biblioteca encapsulada. Desde Lua 5.1, Lua incorporó un mecanismo estándar de gestión de módulos, que permite colocar código común en un archivo y llamarlo desde otros lugares en forma de interfaz API, lo que favorece la reutilización del código y reduce el acoplamiento del mismo.

Los módulos de Lua son tablas compuestas por elementos conocidos como variables, funciones, etc. Por lo tanto, crear un módulo es muy sencillo: solo hay que crear una tabla, poner en ella las constantes y funciones que se desean exportar y, finalmente, devolver esa tabla. A continuación se muestra cómo crear un módulo personalizado module.lua, con el siguiente formato de código de archivo:

-- El nombre del archivo es module.lua
-- Definir un módulo llamado module
module = {}
 
-- Definir una constante
module.constant = Esta es una constante
 
-- Definir una función
function module.func1()
    io.write(¡Esta es una función pública!\n")
end
 
local function func2()
    print(¡Esta es una función privada!)
end
 
function module.func3()
    func2()
end
 
return module

Como se puede ver arriba, la estructura de un módulo es la de una tabla, por lo que se pueden manipular y llamar las constantes o funciones del módulo de la misma manera que se manipulan y llaman los elementos de una tabla.

El func2 anterior se declara como variable local del bloque de código, lo que significa que es una función privada. Por lo tanto, esta función privada del módulo no puede ser accedida desde el exterior; debe ser llamada a través de las funciones públicas del módulo.


La función require

Lua proporciona una función llamada require para cargar módulos. Para cargar un módulo, solo hay que llamarla de manera simple. Por ejemplo:

require("<模块名>")

O

require "<模块名>"

Después de ejecutar require, se devuelve una tabla compuesta por las constantes o funciones del módulo, y también se define una variable global que contiene dicha tabla.

Archivo test_module.lua

-- Archivo test_module.lua
-- El módulo module es el module.lua mencionado anteriormente
require("module")
 
print(module.constant)
 
module.func3()

El resultado de ejecutar el código anterior es:

Esto es una constante
¡Esta es una función privada!

O definir una variable de alias para el módulo cargado, para facilitar su llamada:

Archivo test_module2.lua

-- Archivo test_module2.lua
-- El módulo module es el module.lua mencionado anteriormente
-- Variable de alias m
local m = require("module")
 
print(m.constant)
 
m.func3()

El resultado de ejecutar el código anterior es:

Esto es una constante
¡Esta es una función privada!

Mecanismo de carga

Para los módulos personalizados, el archivo del módulo no puede estar en cualquier directorio; la función require tiene su propia estrategia de carga de rutas de archivo. Intentará cargar el módulo desde un archivo Lua o desde una biblioteca de programa C.

La ruta que utiliza require para buscar archivos Lua se almacena en la variable global package.path. Cuando Lua se inicia, esta variable se inicializa con el valor de la variable de entorno LUA_PATH. Si no se encuentra esta variable de entorno, se utiliza una ruta predeterminada definida en tiempo de compilación para inicializarla.

Por supuesto, si no existe la variable de entorno LUA_PATH, también se puede configurar de forma personalizada. Abra el archivo .profile en el directorio raíz del usuario actual (si no existe, créelo; también puede abrir el archivo .bashrc), por ejemplo, agregue la ruta "~/lua/" a la variable de entorno LUA_PATH:

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

Las rutas de archivo se separan con el signo ";"; los dos ";;" finales indican que después de la ruta recién agregada se añade la ruta predeterminada original.

A continuación, actualice los parámetros de la variable de entorno para que surtan efecto de inmediato.

source ~/.profile

En este momento, supongamos que el valor de package.path es:

/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

Entonces, al llamar a require("module"), se intentará abrir los siguientes directorios de archivos para buscar el objetivo.

/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

Si se encuentra el archivo objetivo, se llamará a package.loadfile para cargar el módulo. De lo contrario, se buscará en la biblioteca de programa C.

La ruta de archivo buscada se obtiene de la variable global package.cpath, y esta variable se inicializa mediante la variable de entorno LUA_CPATH.

La estrategia de búsqueda es la misma que la anterior, solo que ahora se buscan archivos de tipo .so o .dll. Si se encuentran, require los cargará mediante package.loadlib.


Paquetes C

Lua y C son fáciles de combinar; se usa C para escribir paquetes para Lua.

A diferencia de escribir paquetes en Lua, los paquetes C deben cargarse y vincularse antes de su uso. En la mayoría de los sistemas, la forma más fácil de implementarlo es mediante el mecanismo de bibliotecas de enlace dinámico.

Lua proporciona toda la funcionalidad de enlace dinámico en una función llamada loadlib. Esta función tiene dos parámetros: la ruta absoluta de la biblioteca y la función de inicialización. Un ejemplo típico de llamada es el siguiente:

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

La función loadlib carga la biblioteca especificada y la conecta a Lua, sin embargo no abre la biblioteca (es decir, no llama a la función de inicialización), sino que devuelve la función de inicialización como una función de Lua, de modo que podemos llamarla directamente desde Lua.

Si ocurre un error al cargar la biblioteca dinámica o al buscar la función de inicialización, loadlib devolverá nil y un mensaje de error. Podemos modificar el código anterior para que detecte el error y luego llame a la función de inicialización:

local path = "/usr/local/lua/lib/libluasocket.so"
-- O path = "C:\\windows\\luasocket.dll", esto es en la plataforma Windows
local f = assert(loadlib(path, "luaopen_socket"))
f()  -- Abrir realmente la biblioteca

En general, se espera que la biblioteca binaria distribuida contenga un archivo stub similar al fragmento de código anterior. Al instalar la biblioteca binaria, se puede colocar en cualquier directorio; solo es necesario modificar la ruta real de la biblioteca binaria correspondiente en el archivo stub.

Agregue el directorio donde se encuentra el archivo stub a LUA_PATH. Con esta configuración, se puede usar la función require para cargar bibliotecas C.

Otras extensiones