Documento: “Lua – Conceitos Básicos e API C”
Tutorial elaborado pela comunidade de desenvolvedores do (hoje extinto) projeto Kepler, em 2008.
Público-alvo
Este documento é útil tanto para quem deseja aprender a programar em Lua, como para quem deseja embutir a biblioteca Lua em outros programas, usando a API C. O documento pressupõe conhecimento prévio de programação em C (sobretudo a partir do ponto que explica a API C), embora seja possível usufruir dele sem esse conhecimento para aprender sobre a linguagem Lua, nos capítulos iniciais.
Estrutura
A apostila se divide em 9 capítulos:
- Capítulo 1: Lua
- Capítulo 2: Funções
- Capítulo 3: Cadeias de caracteres
- Capítulo 4: Tabelas
- Capítulo 5: Módulos
- Capítulo 6: API C
- Capítulo 7: Usando Lua em uma aplicação C
- Capítulo 8: Usando Funções C em Lua
- Capítulo 9: Definindo Novos Tipos de Dados em C
Resumo
O capítulo 1 apresenta os principais elementos da linguagem Lua que um programador poderia buscar saber para iniciar sua jornada, sem aprofundar muito, mas já permitindo um contato inicial. Alguns conceitos como os tipos de dados (simples e complexos), alguns operadores e estruturas de controle e repetição são apresentados neste primeiro capítulo.
O capítulo 2 detalha um pouco mais as pecularidades das funções dentro do ambiente de desenvolvimento Lua, tal como a possibilidade de múltiplos retornos (que inclusive podem variar entre chamadas), o fato de serem valores de primeira classe e como isso pode ser explorado para construir fechos, o fato de poderem ser anônimas, dentre outros detalhes particulares em Lua.
O capítulo 3 apresenta o conceito de cadeia de caracteres (string) em Lua, como um dos tipos básicos, e algumas funções presentes na biblioteca padrão que tem a finalidade de manipular estas cadeias.
O capítulo 4 fala sobre tabelas, algumas operações básicas (inserção e remoção de itens, ver tamanho), e também algumas formas de usar esse tipo para algumas finalidades, como descrição de dados e até mesmo uma forma de orientação a objetos, fazendo uso do conceito de metatabelas e metamétodos, que dão grande versatilidade às tabelas.
O capítulo 5 explica o sistema de módulos, que reaproveita vários conceitos nativos da linguagem para estruturar bibliotecas. Este capítulo cobre também a criação, instalação e o uso de módulos.
O capítulo 6 inicia a explicação sobre a API C de Lua, apresentando o conceito da pilha virtual que faz a comunicação entre o código C e o código Lua, separando tanto o sistema de tipos como o controle de memória, visto que ambos diferem nas duas linguagens. Alguns exemplos de código são utilizados para demonstrar as funções de criação, consulta e manipulação dessa pilha.
O capítulo 7 mostra uma das aplicabilidades da integração entre C e Lua, fazendo uso da API. Neste caso, usa-se a um arquivo Lua para configurar uma aplicação principal (escrita em C). Portanto, a linguagem Lua neste cenário é usada para descrição de dados.
Ainda assim, fica evidente que, para além de um arquivo de configuração convencional, esta abordagem permite incluir até mesmo funções no código Lua que serão chamadas a partir do programa principal, o que também caracteriza Lua como uma linguagem de extensão.
O capítulo 8 trata sobre funções C exportadas para o ambiente Lua. Ou seja, se por um lado o capítulo anterior fala sobre execução de código Lua em C, este capítulo fala sobre execução de código C em Lua. Primeiro ele mostra o protocolo a ser seguido para criar funções desse tipo, e em seguida como funções C podem ser agrupadas em uma biblioteca para uso em Lua.
Ainda neste capítulo se fala também sobre a criação de funções C que guardam estado, isto é, funções que trabalham com valores persistidos entre chamadas, utilizando de três métodos diferentes: usando um registro global disponível pela biblioteca para uso em código C, usando ambientes e usando variáveis locais externas (ou upvalues).
O capítulo 9 ensina a criar tipos de usuário específicos, ou seja, tipos de dados que são definidos em C, porém acessíveis em Lua, por uma interface pré-definida. Ele começa apresentando um exemplo básico e depois ajustando para utilizar metamétodos (o que permite uma abordagem orientada a objetos).
Por fim ele mostra uma terceira abordagem alternativa, que separa o tipo C e funções associadas de uma interface Lua que apenas reaproveita essa estrutura pré-existente. Neste caso, funções Lua encapsulam as funções C existentes e são associadas ao tipo (userdata) criado em Lua.
Comentários
O nome “Lua” é bastante apropriado porque para um observador terrestre, o satélite natural homônimo possui duas faces, uma visível e outra oculta. Neste sentido, é comum abordar Lua mencionando a sua face visível (uma linguagem de alto nível), porém esquecer sua face oculta (uma biblioteca C), que são indissociáveis e igualmente importantes. Este é um ótimo material introdutório, que explora por igual esses dois lados.
Ou seria a face oculta a linguagem?
O documento pode ser dividido logicamente em três partes. Do capítulo 1 ao 4, são tratados assuntos relativos apenas à linguagem Lua em si. No meio, o capítulo 5 ainda se refere à linguagem, porém já menciona questões relacionadas à implementação, em especial por tratar sobre módulos, diretórios padrão e variáveis de ambiente.
Do capítulo 6 ao 9 o tema central passa a ser a API Lua, seguindo um roteiro relativamente comum ao abordar esse assunto, que passa por quatro tópicos principais: apresentação da pilha virtual, execução de código Lua dentro da aplicação C, criação de bibliotecas C para execução a partir do código Lua e por fim a criação de novos tipos definidos em C para uso em Lua, usando o tipo userdata.
Sobre o capítulo 5 uma ressalva deve ser feita: embora não esteja totalmente desatualizado, e ainda tenha relevância para a explicação sobre o funcionamento de módulos, houve uma mudança significativa na maneira como eles funcionam a partir da versão 5.2,em parte por causa da mudança no conceito de ambientes e da convenção de que módulos não sejam variáveis globais a partir de então.
É importante frisar também que o conceito de ambientes não foi abordado
neste documento, porém citado no capítulo 5, para explicar a função
module()
. Ocorre que este é um conceito importante para uma
compreensão mais abrangente sobre módulos. Depois, no capítulo 8, é citado
novamente, em referência a um dos métodos utilizados para criar funções C com
estado em Lua.
Além do conceito de ambientes, outro tópico que não foi abordado neste documento é o mecanismo de co-rotinas, que é um dos pilares da linguagem, embora programas mais simples raramente façam uso desse mecanismo. E ainda outro conceito que ficou de fora foi a interface de depuração, que oferece diversos mecanismos para inspecionar e manipular internamente um programa Lua, a fim de identificar possíveis falhas ou comportamentos imprevisíveis.
Evidentemente, esse material é proveitoso para mais de um público-alvo. Para quem deseja apenas programar em Lua, seja para construir pequenos programas ou realizar configurações ou extensões de programas que já utilizam Lua, os capítulos 1 a 5 serão de maior interesse. Para quem deseja integrar Lua a uma outra aplicação, biblioteca ou sistemas embarcados, a atenção maior estará sobre os capítulos 6 a 9.
Pontos de atenção
-
No capítulo 1 (página 5) parece haver um erro no terceiro parágrafo, no trecho “o acesso a variáveis locais é bastante mais rápido do que o acesso a variáveis locais”. Aparentemente a intenção do autor para o segundo uso da palavra “locais” (aqui grifado) seria na verdade globais.
-
O capítulo 1 (página 12) apresenta um exemplo de uso da estrutura condicional if, que já utiliza também as cláusulas elseif e else, sem que estas tenham sido previamente ou posteriormente explicadas. Um leitor mais atento ou já familiarizado com programação não terá problemas para compreender o que isso significa.
De todo modo, para maior clareza, a cláusula elseif determina o que será feito caso a primeira condição não seja atendida, estabelecendo uma nova condição, enquanto a cláusula else abrange todas as condições que não as especificadas em if e elseif de um mesmo bloco.
-
No capítulo 3 (página 19) parece haver outro equívoco, no trecho “Caso o primeiro parâmetro de string.sub seja negativo”, pois o primeiro parâmetro não é numérico e sim a própria cadeia a ser manipulada, salvo quando essa função é chamada na notação de método, conceito que até esse trecho ainda não havia sido explicado.
-
No capítulo 5 (página 31), são enumerados os módulos que compõem a biblioteca padrão. Quanto a isto, cabe notar que a versão 5.2 (que é posterior a este documento) oferece um novo módulo (bit32), para suporte a operações bit a bit. Este módulo depois é depreciado em favor de operadores nativos, na versão 5.3, e efetivamente removido na versão 5.4.
Além dele, surge também um novo módulo (utf8) na versão 5.3, que adiciona um suporte básico a UTF-8 em Lua.
-
No capítulo 5 (página 32), a função module é citada, como um mecanismo padronizado para construção de novos módulos. Entretanto, essa função, que surgiu na versão 5.1 caiu em depreciação na versão 5.2, e a partir da versão 5.3 foi efetivamente removida.
-
No capítulo 5 (página 32), a função require é utilizada de um modo que pressupõe que o módulo criará uma tabela em escopo global, com o nome do módulo em questão. De fato, isto ocorria com módulos criados utilizando a função module. Entretanto, atualmente essa premissa já não necessariamente pode ser considerada verdadeira.
-
O capítulo 5 (página 34) menciona as variáveis de ambiente PATH e CPATH, usadas para carregar módulos Lua e módulos C. No entanto, é bom observar que essas variáveis não são lidas diretamente por require. Em vez disso, elas são usadas para configurar as variáveis
package.path
epackage.cpath
, respectivamente. Este comportamento já se observa na versão 5.1. -
O capítulo 6 (página 38) utiliza um exemplo de código no qual se espera que as funções
luaL_loadbuffer()
elua_pcall()
retornem um valor verdadeiro para trechos e execuções sem erros, e um valor falso, caso contrário.Esta construção permanece válida, mas convém notar que a partir da versão 5.2, há uma macro definida no cabeçalho lua.h chamada
LUA_OK
(igual a zero) que pode ser usada para comparar o resultado destas duas funções. Isto é uma boa prática, considerando que o valor esperado poderá mudar em versões futuras (embora esse tipo de mudança seja pouco provável sem um bom motivo). -
No capítulo 7 (página 47) um trecho de código logo no início da página mostra um exemplo de obtenção de um campo de uma tabela. No final deste trecho de código, há uma chamada à função
lua_pop()
, na qual ficou faltando o primeiro argumento (isto é, uma referência à pilha virtual).Isto fica mais claro quando, posteriormente, um exemplo de abstração deste exemplo na função
c_getfield()
(na mesma página) mostra a chamada alua_pop()
novamente, mas desta vez com a parametrização correta. -
No capítulo 8 (página 52) há um exemplo de uso da função
luaL_register()
. Na versão 5.1, que era a mais atual quando este documento foi elaborado, este era o método empregado para registrar novas bibliotecas por meio da API. No entanto, a partir da versão 5.2, essa função foi tornada obsoleta, tendo em vista que novos módulos já não criariam objetos em escopo global, por padrão.Para obter resultado semelhante, deve-se usar a função
luaL_newlib()
, com a diferença que um nome de biblioteca não é especificado. Em vez disso, quando a funçãorequire()
for usada em Lua para obter o módulo, uma tabela com as funções registradas será retornada. -
No capítulo 8 (páginas 53 e 54) um exemplo é dado com o uso do pseudo-índice
LUA_ENVIRONINDEX
, para criar uma variável atrelada a um ambiente acessível por funções C, mas não exposta no contexto do programa Lua. Isto funciona na versão 5.1, porém a partir da versão 5.2, funções C não possuem mais ambientes, logo esse pseudo-índice não existe mais.Como solução, é possível utilizar a função
luaL_setfuncs()
para definir uma quantidade de upvalues comum (embora não compartilhado) a todas as funções. Para que compartilhar upvalues entre diferentes funções C, convém utilizar uma tabela que agrupe todos os valores a serem compartilhados. -
No capítulo 8 (página 55) há um trecho de código que usa uma função
luaL_checkint()
(na linha 10, especificamente), que não existe. Provavelmente o que se pretendia nesse caso era usar a funçãoluaL_checkinteger()
. -
No capítulo 9 (páginas 61 e 62), há um exemplo que mostra o uso da função
luaL_register()
, que conforme já foi comentado, foi tornada obsoleta na versão 5.2. Neste exemplo específico, ela é usada para vincular os metamétodos criados em C aos nomes correspondentes na metatabela do tipo criado.Para obter o mesmo efeito a partir de então, será necessário usar a função
luaL_setfuncs()
, que associa o conjunto de funções informado a uma tabela existente (e no topo da pilha).
Conteúdo relacionado
- Glossário – API C
- Glossário – Ambiente
- Glossário – Ambiente Global
- Glossário – Biblioteca auxiliar
- Glossário – Biblioteca padrão
- Glossário – Bloco
- Glossário – Compilação Lua
- Glossário – Chamada protegida
- Glossário – Coletor de lixo
- Glossário – Escopo Léxico
- Glossário – Estado Lua
- Glossário – Fecho
- Glossário – Finalizador
- Glossário – Função anônima
- Glossário – Interpretador Lua
- Glossário – Máquina Virtual
- Glossário – Metamétodo
- Glossário – Metatabela
- Glossário – Módulo
- Glossário – Pilha virtual
- Glossário – Tabela
- Glossário – Trecho
- Glossário – Userdata
- Glossário – Valores de primeira classe
- Glossário – Variável global
- Glossário – Variável local
- Glossário – Variável local externa
- Tutoriais – Executando código Lua