Com ela é possível criar módulos em C, explorando todo poder e flexibilidade dessa incrível linguagem.
De fato a linguagem Lua não passa de uma linguagem de extensão para C e o interpretador Lua é uma programa escrito em C para ser uma máquina virtual.
A criação de um módulo de baixo nível – como são conhecidos os módulos em C para Lua – não tem nenhum truque ou sortilégio. =)
Basicamente você precisa de uma função
int luaopen_módulo(lua_State *)
que chame a função lua_register()
para registrar o módulo. Essa função recebe três parâmetros: o estado Lua, o nome do módulo e um vetor do tipo luaL_reg
com a lista de funções a serem exportadas. A função deve retornar 1.As funções a serem exportadas devem ser estáticas, recebendo o estado Lua e retornando inteiro.
Os parâmetros vindos de Lua são recolhidos com as funções
lua_totipo()
e o retorno é dado pelas funções lua_pushtipo()
. A função C real deve retornar um número representando a quantidade de valores a serem retornados para Lua.Confuso até aqui, né? Mas tudo se esclarecerá com um exemplo.
Exemplo
Vamos criar um módulo que permita mudar e informar o diretório atual. Para tanto, precisaremos do cabeçalho
unistd.h
.Precisaremos também do cabeçalho
stdlib.h
– para o malloc()
– e dos cabeçalhos de Lua:#include <stdlib.h>
#include <unistd.h>
#include <lua.h>
#include <luaxlib.h>
#include <lualib.h>
Agora podemos declarar os cabeçalhos das funções para mudar e informar o diretório atual, e uma função para ajustar informações sobre o módulo:
static int dir_chdir(lua_State *);
static int dir_cwd(lua_State *);
static void set_info(lua_State *);
Repare que todas as funções recebem como único argumento o estado Lua.
Vamos agora ao vetor que define as funções do módulo. Deve ser um vetor do tipo
luaL_reg
. Esse tipo é uma estrutura onde o primeiro elemento é uma string representando o nome da funções e o segundo elemento a função em si. O último elemento do vetor deve ser equivalente a nulo.O último elemento do vetor deve ser nulo:
static const luaL_reg Funcs[] = {
{ "chdir", dir_chdir },
{ "cwd", dir_cwd },
{ NULL, NULL }
}
Agora já podemos criar a função de abertura do módulo, como já vimos,
luaopen_dir()
:int luaopen_dir(lua_State *L) {
luaL_register(L, "dir", Funcs);
set_info(L);
return 1;
}
Em
set_info()
, o que vemos é lua_pushliteral()
, que empilha dados literais no estado, depois «agrupamos» os dados literais com lua_settable()
– não vou entrar em detalhes sobre isso.static void set_info(lua_State *L) {
lua_pushliteral(L, "_COPYRIGHT");
lua_pushliteral(L, "Copyright (C) 2007 Rodrigo Cacilhas");
lua_settable(L, -3);
lua_pushliteral(L, "_DESCRIPTION");
lua_pushliteral(L, "Directory control");
lua_settable(L, -3);
lua_pushliteral(L, "_NAME");
lua_pushliteral(L, "dir");
lua_settable(L, -3);
lua_pushliteral(L, "_VERSION");
lua_pushliteral(L, "1.0");
lua_settable(L, -3);
}
As funções exportadas
Vamos agora às funções exportadas para Lua.
O cabeçalho da primeira função é:
static int dir_chdir(lua_State *L) {
int r;
Usaremos
r
para fornecer o retorno.Em Lua, a função
chdir()
deverá receber um parâmetro string. Para pegar esse parâmetro na função em C usamos: char *path = (char *) lua_tostring(L, 1);
A chamada da função
lua_tostring()
recebe como primeiro parâmetro o estado Lua que executou a função e, como segundo parâmetro, o índice do parâmetro passado para a função Lua – 1 indica o primeiro parâmetro.Então a linha acima recolherá o primeiro parâmetro passado em Lua como uma string.
Agora é código C simples:
if (chdir(path) == 0)
r = 1;
else
r = 0;
Ou seja, se o status for zero, o retorno será 1 (verdadeiro), senão será 0 (falso).
Agora precisamos empilhar a resposta no estado Lua como booleano:
lua_pushboolean(L, r);
Por último avisamos o estado de que um valor foi retornado:
return 1;
}
Vamos agora à segunda função: ela retornará uma string representando o diretório atual ou nulo caso ocorra um erro – veja que é possível retornar quantidades e tipos diferentes em chamadas diferentes:
static int dir_cwd(lua_State *L) {
char *path = (char *) malloc(sizeof(char) * 1024);
getcwd(path, 1023);
if (path == NULL) {
lua_pushnil(L);
lua_pushstring(L, "getcwd error");
return 2; // dois retornos
} else {
lua_pushstring(L, path);
return 1; // um retorno
}
}
Compilando
Agora é preciso compilar o módulo – chamei o arquivo de
dir.c
:bash$ gcc -fPIC -Wall -c dir.c
bash$ ld -lm -shared dir.o -o dir.so
Agora, como superusuário, mova a biblioteca compartilhada
dir.so
para o diretório $LUA_CPATH
– geralmente /usr/local/lib/lua/5.1/
ou /usr/lib/lua/5.1/
.Testando
Vamos então testar! Levante a máquina virtual Lua e experimente:
lua> require "dir"
lua> print(dir.chdir "/tmp")
true
lua> print(dir.cwd())
/tmp
Se tudo correu bem, você terá algo similar a isso. =)
Dúvidas nos comentários, por favor!
[]'s
Cacilhas