Embutindo Lua em C — Tutorial para Iniciantes

Neste artigo você vai aprender como embutir a linguagem Lua dentro de um programa C. Isso é mais simples do que parece, e abre muitas possibilidades interessantes.


O que é “embutir Lua em C”?

Imagine que você tem um programa em C e quer permitir que os usuários escrevam scripts para personalizar o comportamento dele — sem precisar recompilar o programa. Lua é perfeita para isso.

Ao embutir Lua, seu programa C consegue: - Executar código Lua em tempo real - Ler resultados calculados pelo Lua - Expor funções C para o código Lua usar


Pré-requisitos

Antes de começar, você precisa saber: - O básico de C (variáveis, funções, printf) - Ter gcc instalado no seu sistema

Não é necessário conhecer Lua. Os exemplos são simples o suficiente para entender pelo contexto.


Para que serve isso na prática?

Safe Evaluation (Avaliação Segura)

É possível executar código Lua fornecido pelo usuário sem se preocupar com injeções de código malicioso. Isso é extremamente útil para criar plugins, extensões e plataformas no-code (como é o caso da OUI).

Criação de Plataformas No-Code

Você pode criar um sistema onde seus usuários escrevem scripts em Lua para automatizar tarefas ou criar plugins — sem precisar mexer no código C do seu programa.

Criação de Runtimes

Assim como foi feito no VibeScript e no Darwin, você consegue criar runtimes com bibliotecas embutidas para os mais diversos casos de uso.


Instalação

Neste tutorial usamos o LuaCEmbed, uma biblioteca que simplifica bastante o trabalho com Lua em C.

Passo 1: Baixe a biblioteca com um único comando:

curl -L https://github.com/OUIsolutions/LuaCEmbed/releases/download/0.13.0/LuaCEmbedOne.c -o LuaCEmbedOne.c

Passo 2: Crie um arquivo main.c e inclua a biblioteca no topo:

#include "LuaCEmbedOne.c"  // inclui toda a biblioteca

int main() {
    // seu código aqui
}

A biblioteca é single-file (arquivo único), então não precisa instalar nada — basta incluir.


Compilação

No Linux:

gcc main.c -o programa.out

No Windows com MinGW:

i686-w64-mingw32-gcc main.c -o programa.exe -lws2_32

No Windows com MSVC:

cl.exe main.c /Fe:programa.exe

Exemplo Básico

Vamos começar com o exemplo mais simples possível: executar um cálculo em Lua e ler o resultado de volta em C.

#include "LuaCEmbedOne.c"

int main(int argc, char *argv[]) {
    // 1. Cria uma nova "máquina virtual" Lua
    LuaCEmbed *l = newLuaCEmbedEvaluation();

    // 2. Executa código Lua: define a variável 'r' como 30
    LuaCEmbed_evaluate(l, "r = 30");

    // 3. Avalia uma expressão Lua e lê o resultado como um número inteiro (long)
    long calc = LuaCEmbed_get_evaluation_long(l, "r + 20");
    printf("resultado: %ld\n", calc);  // imprime: 50

    // 4. Verifica se ocorreu algum erro durante a execução
    if (LuaCEmbed_has_errors(l)) {
        printf("erro: %s\n", LuaCEmbed_get_error_message(l));
    }

    // 5. Libera a memória usada pela máquina virtual
    LuaCEmbed_free(l);
    return 0;
}

Saída:

resultado: 50

Padrão que você vai repetir em todos os exemplos: 1. newLuaCEmbedEvaluation() — cria a instância 2. LuaCEmbed_evaluate() — executa código Lua 3. Lê o resultado com as funções get_evaluation_* 4. Verifica erros com LuaCEmbed_has_errors 5. LuaCEmbed_free() — libera a memória


Lendo Valores do Lua

String

#include "LuaCEmbedOne.c"

int main(int argc, char *argv[]) {
    LuaCEmbed *l = newLuaCEmbedEvaluation();
    LuaCEmbed_evaluate(l, "r = 'hello world'");

    // Lê a variável 'r' como texto
    char *result = LuaCEmbed_get_evaluation_string(l, "r");
    printf("resultado: %s\n", result);

    LuaCEmbed_free(l);
    return 0;
}

Saída:

resultado: hello world

Número inteiro (long)

#include "LuaCEmbedOne.c"

int main(int argc, char *argv[]) {
    LuaCEmbed *l = newLuaCEmbedEvaluation();
    LuaCEmbed_evaluate(l, "r = 20 + 30");
    long result = LuaCEmbed_get_evaluation_long(l, "r");
    printf("resultado: %ld\n", result);
    LuaCEmbed_free(l);
    return 0;
}

Saída:

resultado: 50

Número decimal (double)

#include "LuaCEmbedOne.c"

int main(int argc, char *argv[]) {
    LuaCEmbed *l = newLuaCEmbedEvaluation();
    LuaCEmbed_evaluate(l, "r = 20 + 30");
    double result = LuaCEmbed_get_evaluation_double(l, "r");
    printf("resultado: %lf\n", result);
    LuaCEmbed_free(l);
    return 0;
}

Saída:

resultado: 50.000000

Booleano

#include "LuaCEmbedOne.c"

int main(int argc, char *argv[]) {
    LuaCEmbed *l = newLuaCEmbedEvaluation();
    LuaCEmbed_evaluate(l, "r = true");
    bool result = LuaCEmbed_get_evaluation_bool(l, "r");
    printf("resultado: %d\n", result);  // 1 = true, 0 = false
    LuaCEmbed_free(l);
    return 0;
}

Saída:

resultado: 1

Descobrindo o tipo de uma variável

Se você não sabe que tipo a variável tem, pode verificar antes de ler:

#include "LuaCEmbedOne.c"

int main(int argc, char *argv[]) {
    LuaCEmbed *l = newLuaCEmbedEvaluation();
    LuaCEmbed_evaluate(l, "r = 'hello world'");
    int r_type = LuaCEmbed_get_evaluation_type(l, "r");
    const char *type_name = LuaCembed_convert_arg_code(r_type);
    printf("tipo: %s\n", type_name);
    LuaCEmbed_free(l);
    return 0;
}

Saída:

tipo: string

Funções Nativas do Lua

Por padrão, o LuaCEmbed não expõe as funções nativas do Lua (como print, io, os, etc.) para o código avaliado. Isso é intencional — garante que código de terceiros não possa acessar arquivos ou executar comandos no seu sistema.

Se você confia no código que vai executar e precisa dessas funções:

#include "LuaCEmbedOne.c"

int main(int argc, char *argv[]) {
    LuaCEmbed *l = newLuaCEmbedEvaluation();

    // ATENÇÃO: só use isso se você confiar no código que será executado!
    // Isso dá acesso a print, io, os, e outras libs nativas do Lua.
    LuaCEmbed_load_native_libs(l);

    LuaCEmbed_evaluate(l, "print('hello from lua')");

    if (LuaCEmbed_has_errors(l)) {
        printf("erro: %s\n", LuaCEmbed_get_error_message(l));
    }
    LuaCEmbed_free(l);
    return 0;
}

Saída:

hello from lua

Executando um arquivo .lua

Em vez de passar código como string, você pode carregar um arquivo .lua do disco:

#include "LuaCEmbedOne.c"

int main(int argc, char *argv[]) {
    LuaCEmbed *l = newLuaCEmbedEvaluation();
    LuaCEmbed_load_native_libs(l);

    // Carrega e executa o arquivo "script.lua"
    LuaCEmbed_evaluete_file(l, "script.lua");

    if (LuaCEmbed_has_errors(l)) {
        printf("erro: %s\n", LuaCEmbed_get_error_message(l));
    }
    LuaCEmbed_free(l);
    return 0;
}

Callbacks — Expondo Funções C para o Lua

Callbacks são a forma de criar funções em C que podem ser chamadas a partir do código Lua. Isso permite que você defina exatamente o que o usuário pode ou não fazer.

Callback simples (sem argumentos, sem retorno)

#include "LuaCEmbedOne.c"

// Esta função C será chamável a partir do Lua
LuaCEmbedResponse *hello(LuaCEmbed *args) {
    printf("meu primeiro callback\n");
    return NULL;  // NULL significa: não retorna nada (nil no Lua)
}

int main(int argc, char *argv[]) {
    LuaCEmbed *l = newLuaCEmbedEvaluation();

    // Registra a função C com o nome "hello" no Lua
    LuaCEmbed_add_callback(l, "hello", hello);

    // Agora o código Lua pode chamar hello()
    LuaCEmbed_evaluate(l, "hello()");

    if (LuaCEmbed_has_errors(l)) {
        printf("erro: %s\n", LuaCEmbed_get_error_message(l));
    }
    LuaCEmbed_free(l);
    return 0;
}

Saída:

meu primeiro callback

Recebendo argumentos no callback

Os argumentos passados pelo Lua ficam disponíveis dentro da função C. Eles são indexados a partir de 0 (padrão C), mesmo que o Lua normalmente use índices a partir de 1.

#include "LuaCEmbedOne.c"

LuaCEmbedResponse *test_func(LuaCEmbed *args) {
    // Quantos argumentos foram passados?
    long size = LuaCEmbed_get_total_args(args);
    if (size == 0) {
        printf("nenhum argumento fornecido\n");
        return NULL;
    }

    // Lê o primeiro argumento (índice 0)
    int index = 0;
    int arg_type = LuaCEmbed_get_arg_type(args, index);

    // Age de acordo com o tipo recebido
    if (arg_type == LUA_CEMBED_NUMBER) {
        printf("número: %lf\n", LuaCEmbed_get_double_arg(args, index));
    } else if (arg_type == LUA_CEMBED_STRING) {
        printf("string: %s\n", LuaCEmbed_get_str_arg(args, index));
    } else if (arg_type == LUA_CEMBED_BOOL) {
        printf("bool: %d\n", LuaCEmbed_get_bool_arg(args, index));
    } else {
        // Para outros tipos (como tabela), mostra o nome do tipo
        const char *type_name = LuaCembed_convert_arg_code(arg_type);
        printf("tipo: %s\n", type_name);
    }

    return NULL;
}

int main(int argc, char *argv[]) {
    LuaCEmbed *l = newLuaCEmbedEvaluation();
    LuaCEmbed_add_callback(l, "test", test_func);

    // Testa com diferentes tipos de argumento
    LuaCEmbed_evaluate(l, "test()");          // sem argumento
    LuaCEmbed_evaluate(l, "test(10)");        // número
    LuaCEmbed_evaluate(l, "test('hello')");   // string
    LuaCEmbed_evaluate(l, "test(true)");      // booleano
    LuaCEmbed_evaluate(l, "test({a=30})");    // tabela

    if (LuaCEmbed_has_errors(l)) {
        printf("erro: %s\n", LuaCEmbed_get_error_message(l));
    }
    LuaCEmbed_free(l);
    return 0;
}

Saída:

nenhum argumento fornecido
número: 10.000000
string: hello
bool: 1
tipo: table

Retornando valores de um callback

Um callback pode retornar um valor para o Lua usando as funções LuaCEmbed_send_*:

#include "LuaCEmbedOne.c"

LuaCEmbedResponse *add(LuaCEmbed *args) {
    double a = LuaCEmbed_get_double_arg(args, 0);
    double b = LuaCEmbed_get_double_arg(args, 1);

    // Se houve erro ao ler os argumentos (ex: tipo errado), propaga o erro
    if (LuaCEmbed_has_errors(args)) {
        return LuaCEmbed_send_error(LuaCEmbed_get_error_message(args));
    }

    return LuaCEmbed_send_double(a + b);  // retorna a soma para o Lua
}

int main(int argc, char *argv[]) {
    LuaCEmbed *l = newLuaCEmbedEvaluation();
    LuaCEmbed_add_callback(l, "add", add);

    double result = LuaCEmbed_get_evaluation_double(l, "add(10, 20)");
    printf("resultado: %lf\n", result);

    if (LuaCEmbed_has_errors(l)) {
        printf("erro: %s\n", LuaCEmbed_get_error_message(l));
    }
    LuaCEmbed_free(l);
    return 0;
}

Saída:

resultado: 30.000000

Funções de retorno disponíveis:

Função Retorna
LuaCEmbed_send_double(double) número decimal
LuaCEmbed_send_long(long) número inteiro
LuaCEmbed_send_str(char*) texto
LuaCEmbed_send_bool(bool) verdadeiro/falso
LuaCEmbed_send_table(LuaCEmbedTable*) tabela
LuaCEmbed_send_error(char*) propaga um erro para o Lua
return NULL nil (sem retorno)

Tabelas

Tabelas são o principal tipo de dado estruturado do Lua — funcionam como arrays, dicionários e objetos ao mesmo tempo.

Lendo propriedades de uma tabela recebida

#include "LuaCEmbedOne.c"

LuaCEmbedResponse *show_table(LuaCEmbed *args) {
    // Lê o primeiro argumento como tabela
    LuaCEmbedTable *t = LuaCEmbed_get_arg_table(args, 0);
    if (LuaCEmbed_has_errors(args)) {
        return LuaCEmbed_send_error(LuaCEmbed_get_error_message(args));
    }

    // Lê as propriedades pelo nome
    char *name = LuaCembedTable_get_string_prop(t, "name");
    long age   = LuaCembedTable_get_long_prop(t, "age");

    printf("nome: %s\n", name);
    printf("idade: %ld\n", age);
    return NULL;
}

int main(int argc, char *argv[]) {
    LuaCEmbed *l = newLuaCEmbedEvaluation();
    LuaCEmbed_add_callback(l, "show_table", show_table);

    LuaCEmbed_evaluate(l, "show_table({ name = 'Mateus', age = 27 })");

    if (LuaCEmbed_has_errors(l)) {
        printf("erro: %s\n", LuaCEmbed_get_error_message(l));
    }
    LuaCEmbed_free(l);
    return 0;
}

Saída:

nome: Mateus
idade: 27

Tamanho de uma tabela

#include "LuaCEmbedOne.c"

int main(int argc, char *argv[]) {
    LuaCEmbed *l = newLuaCEmbedEvaluation();
    LuaCEmbed_evaluate(l, "r = {1, 2, 3}");
    long size = LuaCEmbed_get_evaluation_table_size(l, "r");
    printf("tamanho: %ld\n", size);  // 3
    LuaCEmbed_free(l);
    return 0;
}

Saída:

tamanho: 3

Iterando sobre os elementos de uma tabela

#include "LuaCEmbedOne.c"

LuaCEmbedResponse *show_table(LuaCEmbed *args) {
    LuaCEmbedTable *t = LuaCEmbed_get_arg_table(args, 0);
    if (LuaCEmbed_has_errors(args)) {
        return LuaCEmbed_send_error(LuaCEmbed_get_error_message(args));
    }

    long size = LuaCEmbedTable_get_listable_size(t);
    for (int i = 0; i < size; i++) {
        printf("índice: %d\n", i);

        // Verifica se o elemento tem uma chave (string) associada
        const char *key = "sem chave";
        if (LuaCembedTable_has_key_at_index(t, i)) {
            key = LuaCembedTable_get_key_by_index(t, i);
        }
        printf("chave: %s\n", key);

        // Lê o tipo e o valor do elemento
        int type = LuaCEmbedTable_get_type_by_index(t, i);
        printf("tipo: %s\n", LuaCembed_convert_arg_code(type));

        if (type == LUA_CEMBED_NUMBER) {
            printf("valor: %lf\n", LuaCEmbedTable_get_double_by_index(t, i));
        } else if (type == LUA_CEMBED_STRING) {
            printf("valor: %s\n", LuaCEmbedTable_get_string_by_index(t, i));
        } else if (type == LUA_CEMBED_BOOL) {
            printf("valor: %d\n", LuaCEmbedTable_get_bool_by_index(t, i));
        }
        printf("--\n");
    }
    return NULL;
}

int main(int argc, char *argv[]) {
    LuaCEmbed *l = newLuaCEmbedEvaluation();
    LuaCEmbed_add_callback(l, "show_table", show_table);

    LuaCEmbed_evaluate(l, "show_table({10, 'hello', true, x=42})");

    if (LuaCEmbed_has_errors(l)) {
        printf("erro: %s\n", LuaCEmbed_get_error_message(l));
    }
    LuaCEmbed_free(l);
    return 0;
}

Lendo sub-tabelas (tabelas dentro de tabelas)

#include "LuaCEmbedOne.c"

LuaCEmbedResponse *show_people(LuaCEmbed *args) {
    LuaCEmbedTable *t = LuaCEmbed_get_arg_table(args, 0);
    if (LuaCEmbed_has_errors(args)) {
        return LuaCEmbed_send_error(LuaCEmbed_get_error_message(args));
    }

    long size = LuaCEmbedTable_get_listable_size(t);
    for (int i = 0; i < size; i++) {
        // Cada elemento é uma tabela com "name" e "age"
        LuaCEmbedTable *item = LuaCEmbedTable_get_sub_table_by_index(t, i);
        char *name = LuaCembedTable_get_string_prop(item, "name");
        long age   = LuaCembedTable_get_long_prop(item, "age");
        printf("nome: %s, idade: %ld\n", name, age);
    }
    return NULL;
}

int main(int argc, char *argv[]) {
    LuaCEmbed *l = newLuaCEmbedEvaluation();
    LuaCEmbed_add_callback(l, "show_people", show_people);

    LuaCEmbed_evaluate(l, "show_people({{name='Alice', age=30}, {name='Bob', age=25}})");

    if (LuaCEmbed_has_errors(l)) {
        printf("erro: %s\n", LuaCEmbed_get_error_message(l));
    }
    LuaCEmbed_free(l);
    return 0;
}

Saída:

nome: Alice, idade: 30
nome: Bob, idade: 25

Criando e retornando uma tabela de C para Lua

#include "LuaCEmbedOne.c"

LuaCEmbedResponse *create_table(LuaCEmbed *args) {
    // Cria uma nova tabela vazia
    LuaCEmbedTable *t = LuaCembed_new_anonymous_table(args);

    // Define as propriedades
    LuaCEmbedTable_set_string_prop(t, "name",    "Mateus");
    LuaCEmbedTable_set_long_prop  (t, "age",     27);
    LuaCEmbedTable_set_double_prop(t, "height",  1.82);
    LuaCEmbedTable_set_bool_prop  (t, "married", false);

    // Retorna a tabela para o Lua
    return LuaCEmbed_send_table(t);
}

int main(int argc, char *argv[]) {
    LuaCEmbed *l = newLuaCEmbedEvaluation();
    LuaCEmbed_add_callback(l, "create_table", create_table);
    LuaCEmbed_load_native_libs(l);

    LuaCEmbed_evaluate(l, "p = create_table(); print(p.name, p.age, p.height, p.married)");

    if (LuaCEmbed_has_errors(l)) {
        printf("erro: %s\n", LuaCEmbed_get_error_message(l));
    }
    LuaCEmbed_free(l);
    return 0;
}

Saída:

Mateus  27  1.82    false

Adicionando métodos a uma tabela

Você pode criar tabelas que se comportam como objetos, com métodos chamáveis pelo Lua:

#include "LuaCEmbedOne.c"

// O método recebe a própria tabela como 'self' (como em orientação a objetos)
LuaCEmbedResponse *describe(LuaCEmbedTable *self, LuaCEmbed *args) {
    char *name = LuaCembedTable_get_string_prop(self, "name");
    long age   = LuaCembedTable_get_long_prop(self, "age");
    printf("nome: %s, idade: %ld\n", name, age);
    return NULL;
}

LuaCEmbedResponse *create_table(LuaCEmbed *args) {
    LuaCEmbedTable *t = LuaCembed_new_anonymous_table(args);
    LuaCEmbedTable_set_string_prop(t, "name", "Mateus");
    LuaCEmbedTable_set_long_prop  (t, "age",  27);
    LuaCEmbedTable_set_method     (t, "describe", describe);
    return LuaCEmbed_send_table(t);
}

int main(int argc, char *argv[]) {
    LuaCEmbed *l = newLuaCEmbedEvaluation();
    LuaCEmbed_add_callback(l, "create_table", create_table);

    LuaCEmbed_evaluate(l, "local p = create_table(); p:describe()");

    if (LuaCEmbed_has_errors(l)) {
        printf("erro: %s\n", LuaCEmbed_get_error_message(l));
    }
    LuaCEmbed_free(l);
    return 0;
}

Saída:

nome: Mateus, idade: 27

Meta-métodos

Meta-métodos permitem interceptar operações especiais do Lua, como acesso a propriedades (__index) e destruição pelo garbage collector (__gc):

#include "LuaCEmbedOne.c"

// Chamado quando o Lua acessa uma propriedade da tabela
LuaCEmbedResponse *index_callback(LuaCEmbedTable *self, LuaCEmbed *args) {
    int value_type = LuaCEmbed_get_arg_type(args, 1);
    if (value_type == LUA_CEMBED_STRING) {
        printf("acesso ao índice: %s\n", LuaCEmbed_get_str_arg(args, 1));
    }
    return NULL;
}

// Chamado quando o garbage collector do Lua destrói a tabela
LuaCEmbedResponse *gc_callback(LuaCEmbedTable *self, LuaCEmbed *args) {
    printf("tabela destruída pelo GC\n");
    return NULL;
}

LuaCEmbedResponse *create_table(LuaCEmbed *args) {
    LuaCEmbedTable *t = LuaCembed_new_anonymous_table(args);
    LuaCEmbedTable_set_method(t, "__index", index_callback);
    LuaCEmbedTable_set_method(t, "__gc",    gc_callback);
    return LuaCEmbed_send_table(t);
}

int main(int argc, char *argv[]) {
    LuaCEmbed *l = newLuaCEmbedEvaluation();
    LuaCEmbed_add_callback(l, "create_table", create_table);

    LuaCEmbed_evaluate(l, "local t = create_table(); local x = t.foo");

    if (LuaCEmbed_has_errors(l)) {
        printf("erro: %s\n", LuaCEmbed_get_error_message(l));
    }
    LuaCEmbed_free(l);
    return 0;
}

Saída:

acesso ao índice: foo
tabela destruída pelo GC

Criando uma Biblioteca (.so/.dll)

Além de executar Lua a partir de C, você também pode criar o caminho inverso: compilar C como uma biblioteca dinâmica e importá-la diretamente do Lua com require.

#include "LuaCEmbedOne.c"

LuaCEmbedResponse *add_cfunc(LuaCEmbed *args) {
    double a = LuaCEmbed_get_double_arg(args, 0);
    double b = LuaCEmbed_get_double_arg(args, 1);
    if (LuaCEmbed_has_errors(args)) {
        return LuaCEmbed_send_error(LuaCEmbed_get_error_message(args));
    }
    return LuaCEmbed_send_double(a + b);
}

LuaCEmbedResponse *sub_cfunc(LuaCEmbed *args) {
    double a = LuaCEmbed_get_double_arg(args, 0);
    double b = LuaCEmbed_get_double_arg(args, 1);
    if (LuaCEmbed_has_errors(args)) {
        return LuaCEmbed_send_error(LuaCEmbed_get_error_message(args));
    }
    return LuaCEmbed_send_double(a - b);
}

// O nome da função DEVE ser "luaopen_" + nome_da_biblioteca
int luaopen_my_lib(lua_State *state) {
    LuaCEmbed *l = newLuaCEmbedLib(state);
    LuaCEmbed_add_callback(l, "add", add_cfunc);
    LuaCEmbed_add_callback(l, "sub", sub_cfunc);
    return LuaCembed_perform(l);
}

Compile como biblioteca compartilhada:

gcc -shared -fPIC -o my_lib.so main.c

Use em Lua:

local lib = require("my_lib")

print(lib.add(10, 20))  -- 30.0
print(lib.sub(20, 5))   -- 15.0

Propriedades estáticas na biblioteca

Você também pode expor constantes e valores fixos:

int luaopen_my_lib(lua_State *state) {
    LuaCEmbed *l = newLuaCEmbedLib(state);
    LuaCEmbed_set_long_lib_prop  (l, "version", 1);
    LuaCEmbed_set_string_lib_prop(l, "author",  "Mateus");
    LuaCEmbed_set_double_lib_prop(l, "pi",      3.14);
    LuaCEmbed_set_bool_lib_prop  (l, "debug",   false);
    return LuaCembed_perform(l);
}

Em Lua:

local lib = require("my_lib")
print(lib.version)  -- 1
print(lib.author)   -- Mateus

Segurança: Timeout e Limite de Memória

Quando você executa código enviado por usuários, é importante proteger seu programa contra loops infinitos e uso excessivo de memória.

Timeout

Interrompe a execução após um número de segundos:

#include "LuaCEmbedOne.c"

int main(int argc, char *argv[]) {
    LuaCEmbed *l = newLuaCEmbedEvaluation();

    LuaCEmbed_set_timeout(2);  // interrompe após 2 segundos

    LuaCEmbed_evaluate(l, "while true do end");  // loop infinito

    if (LuaCEmbed_has_errors(l)) {
        printf("erro: %s\n", LuaCEmbed_get_error_message(l));
    }

    // Após um timeout, você pode limpar o erro e continuar usando a instância
    LuaCEmbed_clear_errors(l);
    LuaCEmbed_evaluate(l, "r = 'ainda funciona'");
    printf("%s\n", LuaCEmbed_get_evaluation_string(l, "r"));

    LuaCEmbed_free(l);
    return 0;
}

Saída:

erro: timeout error
ainda funciona

Limite de Memória

Impede que o código Lua consuma memória sem controle (padrão: 100 MB):

#include "LuaCEmbedOne.c"

int main(int argc, char *argv[]) {
    LuaCEmbed *l = newLuaCEmbedEvaluation();

    LuaCEmbed_set_memory_limit(l, 1);  // limita a 1 MB

    // Código que tenta crescer uma string indefinidamente
    LuaCEmbed_evaluate(l, "t = 'a'; while true do t = t .. t end");

    if (LuaCEmbed_has_errors(l)) {
        printf("erro: %s\n", LuaCEmbed_get_error_message(l));
    }

    LuaCEmbed_free(l);
    return 0;
}

Saída:

erro: not enough memory

Tratamento de Erros

Sempre verifique erros após avaliações. Se não verificar, erros silenciosos podem causar comportamentos inesperados.

#include "LuaCEmbedOne.c"

LuaCEmbedResponse *minha_funcao(LuaCEmbed *args) {
    double val = LuaCEmbed_get_double_arg(args, 0);
    if (LuaCEmbed_has_errors(args)) {
        // Propaga o erro para o Lua em vez de continuar com valor inválido
        return LuaCEmbed_send_error(LuaCEmbed_get_error_message(args));
    }
    return LuaCEmbed_send_double(val * 2);
}

int main(int argc, char *argv[]) {
    LuaCEmbed *l = newLuaCEmbedEvaluation();

    LuaCEmbed_evaluate(l, "código inválido %%%");

    if (LuaCEmbed_has_errors(l)) {
        printf("erro: %s\n", LuaCEmbed_get_error_message(l));

        // Limpa o erro para continuar usando a mesma instância
        LuaCEmbed_clear_errors(l);
    }

    LuaCEmbed_add_callback(l, "minha_funcao", minha_funcao);
    double result = LuaCEmbed_get_evaluation_double(l, "minha_funcao(21)");
    printf("resultado: %lf\n", result);

    LuaCEmbed_free(l);
    return 0;
}

Saída:

erro: [string "código inválido %%%"]:1: unexpected symbol near '%'
resultado: 42.000000

Referência Rápida

Função Descrição
newLuaCEmbedEvaluation() Cria uma nova instância
LuaCEmbed_evaluate(l, code) Executa código Lua (string)
LuaCEmbed_evaluete_file(l, path) Executa um arquivo Lua
LuaCEmbed_load_native_libs(l) Habilita libs nativas (print, io, os…)
LuaCEmbed_add_callback(l, name, fn) Registra uma função C no Lua
LuaCEmbed_get_evaluation_string(l, expr) Lê resultado como string
LuaCEmbed_get_evaluation_long(l, expr) Lê resultado como inteiro
LuaCEmbed_get_evaluation_double(l, expr) Lê resultado como decimal
LuaCEmbed_get_evaluation_bool(l, expr) Lê resultado como booleano
LuaCEmbed_get_evaluation_type(l, expr) Retorna o tipo da expressão
LuaCEmbed_get_evaluation_table_size(l, expr) Retorna o tamanho de uma tabela
LuaCEmbed_has_errors(l) Verifica se houve erro
LuaCEmbed_get_error_message(l) Obtém a mensagem de erro
LuaCEmbed_clear_errors(l) Limpa o estado de erro
LuaCEmbed_set_timeout(seconds) Define timeout de execução
LuaCEmbed_set_memory_limit(l, mb) Define limite de memória em MB
LuaCEmbed_free(l) Libera a instância