Embebiendo Lua en C — Tutorial para Principiantes

En este artículo aprenderás cómo embeber (integrar) el lenguaje Lua dentro de un programa C. Esto es más sencillo de lo que parece, y abre muchas posibilidades interesantes.


¿Qué es “embeber Lua en C”?

Imagina que tienes un programa en C y quieres permitir que los usuarios escriban scripts para personalizar su comportamiento — sin necesidad de recompilar el programa. Lua es perfecta para esto.

Al embeber Lua, tu programa C puede: - Ejecutar código Lua en tiempo real - Leer resultados calculados por Lua - Exponer funciones C para que el código Lua las use


Requisitos previos

Antes de comenzar, necesitas saber: - Lo básico de C (variables, funciones, printf) - Tener gcc instalado en tu sistema

No es necesario saber Lua. Los ejemplos son lo suficientemente simples como para entenderlos por contexto.


¿Para qué sirve esto en la práctica?

Evaluación Segura (Safe Evaluation)

Es posible ejecutar código Lua proporcionado por el usuario sin preocuparse por inyecciones de código malicioso. Esto es extremadamente útil para crear plugins, extensiones y plataformas no-code (como es el caso de OUI).

Creación de Plataformas No-Code

Puedes crear un sistema donde tus usuarios escriban scripts en Lua para automatizar tareas o crear plugins — sin necesidad de tocar el código C de tu programa.

Creación de Runtimes

Al igual que se hizo en VibeScript y en Darwin, puedes crear runtimes con bibliotecas embebidas para los casos de uso más diversos.


Instalación

En este tutorial usamos LuaCEmbed, una biblioteca que simplifica bastante el trabajo con Lua en C.

Paso 1: Descarga la biblioteca con un solo comando:

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

Paso 2: Crea un archivo main.c e incluye la biblioteca en la parte superior:

#include "LuaCEmbedOne.c"  // incluye toda la biblioteca

int main() {
    // tu código aquí
}

La biblioteca es single-file (archivo único), por lo que no es necesario instalar nada — basta con incluirla.


Compilación

En Linux:

gcc main.c -o programa.out

En Windows con MinGW:

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

En Windows con MSVC:

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

Ejemplo Básico

Vamos a empezar con el ejemplo más simple posible: ejecutar un cálculo en Lua y leer el resultado de vuelta en C.

#include "LuaCEmbedOne.c"

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

    // 2. Ejecuta código Lua: define la variable 'r' como 30
    LuaCEmbed_evaluate(l, "r = 30");

    // 3. Evalúa una expresión Lua y lee el resultado como un número entero (long)
    long calc = LuaCEmbed_get_evaluation_long(l, "r + 20");
    printf("resultado: %ld\n", calc);  // imprime: 50

    // 4. Verifica si ocurrió algún error durante la ejecución
    if (LuaCEmbed_has_errors(l)) {
        printf("error: %s\n", LuaCEmbed_get_error_message(l));
    }

    // 5. Libera la memoria usada por la máquina virtual
    LuaCEmbed_free(l);
    return 0;
}

Salida:

resultado: 50

Patrón que repetirás en todos los ejemplos: 1. newLuaCEmbedEvaluation() — crea la instancia 2. LuaCEmbed_evaluate() — ejecuta código Lua 3. Lee el resultado con las funciones get_evaluation_* 4. Verifica errores con LuaCEmbed_has_errors 5. LuaCEmbed_free() — libera la memoria


Leyendo Valores de Lua

String

#include "LuaCEmbedOne.c"

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

    // Lee la variable 'r' como texto
    char *result = LuaCEmbed_get_evaluation_string(l, "r");
    printf("resultado: %s\n", result);

    LuaCEmbed_free(l);
    return 0;
}

Salida:

resultado: hello world

Número entero (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;
}

Salida:

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;
}

Salida:

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;
}

Salida:

resultado: 1

Descubriendo el tipo de una variable

Si no sabes qué tipo tiene la variable, puedes comprobarlo antes de leer:

#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;
}

Salida:

tipo: string

Funciones Nativas de Lua

Por defecto, LuaCEmbed no expone las funciones nativas de Lua (como print, io, os, etc.) al código evaluado. Esto es intencional — garantiza que código de terceros no pueda acceder a archivos o ejecutar comandos en tu sistema.

Si confías en el código que vas a ejecutar y necesitas estas funciones:

#include "LuaCEmbedOne.c"

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

    // ATENCIÓN: ¡solo usa esto si confías en el código que será ejecutado!
    // Esto da acceso a print, io, os, y otras librerías nativas de Lua.
    LuaCEmbed_load_native_libs(l);

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

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

Salida:

hello from lua

Ejecutando un archivo .lua

En lugar de pasar código como string, puedes cargar un archivo .lua del disco:

#include "LuaCEmbedOne.c"

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

    // Carga y ejecuta el archivo "script.lua"
    LuaCEmbed_evaluete_file(l, "script.lua");

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

Callbacks — Exponiendo Funciones C a Lua

Los callbacks son la manera de crear funciones en C que pueden ser llamadas desde el código Lua. Esto te permite definir exactamente qué puede o no puede hacer el usuario.

Callback simple (sin argumentos, sin retorno)

#include "LuaCEmbedOne.c"

// Esta función C será llamable desde Lua
LuaCEmbedResponse *hello(LuaCEmbed *args) {
    printf("mi primer callback\n");
    return NULL;  // NULL significa: no devuelve nada (nil en Lua)
}

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

    // Registra la función C con el nombre "hello" en Lua
    LuaCEmbed_add_callback(l, "hello", hello);

    // Ahora el código Lua puede llamar a hello()
    LuaCEmbed_evaluate(l, "hello()");

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

Salida:

mi primer callback

Recibiendo argumentos en el callback

Los argumentos pasados por Lua están disponibles dentro de la función C. Se indexan a partir de 0 (estándar de C), aunque Lua normalmente use índices a partir de 1.

#include "LuaCEmbedOne.c"

LuaCEmbedResponse *test_func(LuaCEmbed *args) {
    // ¿Cuántos argumentos se proporcionaron?
    long size = LuaCEmbed_get_total_args(args);
    if (size == 0) {
        printf("ningún argumento proporcionado\n");
        return NULL;
    }

    // Lee el primer argumento (índice 0)
    int index = 0;
    int arg_type = LuaCEmbed_get_arg_type(args, index);

    // Actúa según el tipo recibido
    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 otros tipos (como tabla), muestra el nombre del 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);

    // Prueba con diferentes tipos de argumento
    LuaCEmbed_evaluate(l, "test()");          // sin 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})");    // tabla

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

Salida:

ningún argumento proporcionado
número: 10.000000
string: hello
bool: 1
tipo: table

Retornando valores desde un callback

Un callback puede devolver un valor a Lua usando las funciones 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);

    // Si hubo un error al leer los argumentos (ej: tipo incorrecto), propaga el error
    if (LuaCEmbed_has_errors(args)) {
        return LuaCEmbed_send_error(LuaCEmbed_get_error_message(args));
    }

    return LuaCEmbed_send_double(a + b);  // devuelve la suma a 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("error: %s\n", LuaCEmbed_get_error_message(l));
    }
    LuaCEmbed_free(l);
    return 0;
}

Salida:

resultado: 30.000000

Funciones de retorno disponibles:

Función Retorna
LuaCEmbed_send_double(double) número decimal
LuaCEmbed_send_long(long) número entero
LuaCEmbed_send_str(char*) texto
LuaCEmbed_send_bool(bool) verdadero/falso
LuaCEmbed_send_table(LuaCEmbedTable*) tabla
LuaCEmbed_send_error(char*) propaga un error a Lua
return NULL nil (sin retorno)

Tablas

Las tablas son el principal tipo de estructura de datos de Lua — funcionan como arrays, diccionarios y objetos al mismo tiempo.

Leyendo propiedades de una tabla recibida

#include "LuaCEmbedOne.c"

LuaCEmbedResponse *show_table(LuaCEmbed *args) {
    // Lee el primer argumento como tabla
    LuaCEmbedTable *t = LuaCEmbed_get_arg_table(args, 0);
    if (LuaCEmbed_has_errors(args)) {
        return LuaCEmbed_send_error(LuaCEmbed_get_error_message(args));
    }

    // Lee las propiedades por el nombre
    char *name = LuaCembedTable_get_string_prop(t, "name");
    long age   = LuaCembedTable_get_long_prop(t, "age");

    printf("nombre: %s\n", name);
    printf("edad: %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("error: %s\n", LuaCEmbed_get_error_message(l));
    }
    LuaCEmbed_free(l);
    return 0;
}

Salida:

nombre: Mateus
edad: 27

Tamaño de una tabla

#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("tamaño: %ld\n", size);  // 3
    LuaCEmbed_free(l);
    return 0;
}

Salida:

tamaño: 3

Iterando sobre los elementos de una tabla

#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 si el elemento tiene una clave (string) asociada
        const char *key = "sin clave";
        if (LuaCembedTable_has_key_at_index(t, i)) {
            key = LuaCembedTable_get_key_by_index(t, i);
        }
        printf("clave: %s\n", key);

        // Lee el tipo y el valor del 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("error: %s\n", LuaCEmbed_get_error_message(l));
    }
    LuaCEmbed_free(l);
    return 0;
}

Leyendo sub-tablas (tablas dentro de tablas)

#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 es una tabla con "name" y "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("nombre: %s, edad: %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("error: %s\n", LuaCEmbed_get_error_message(l));
    }
    LuaCEmbed_free(l);
    return 0;
}

Salida:

nombre: Alice, edad: 30
nombre: Bob, edad: 25

Creando y retornando una tabla de C a Lua

#include "LuaCEmbedOne.c"

LuaCEmbedResponse *create_table(LuaCEmbed *args) {
    // Crea una nueva tabla vacía
    LuaCEmbedTable *t = LuaCembed_new_anonymous_table(args);

    // Define las propiedades
    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);

    // Devuelve la tabla a 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("error: %s\n", LuaCEmbed_get_error_message(l));
    }
    LuaCEmbed_free(l);
    return 0;
}

Salida:

Mateus  27  1.82    false

Añadiendo métodos a una tabla

Puedes crear tablas que se comportan como objetos, con métodos invocables por Lua:

#include "LuaCEmbedOne.c"

// El método recibe la propia tabla como 'self' (como en orientación 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("nombre: %s, edad: %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("error: %s\n", LuaCEmbed_get_error_message(l));
    }
    LuaCEmbed_free(l);
    return 0;
}

Salida:

nombre: Mateus, edad: 27

Meta-métodos

Los meta-métodos te permiten interceptar operaciones especiales de Lua, como el acceso a propiedades (__index) y la destrucción por el recolector de basura (__gc):

#include "LuaCEmbedOne.c"

// Invocado cuando Lua accede a una propiedad de la tabla
LuaCEmbedResponse *index_callback(LuaCEmbedTable *self, LuaCEmbed *args) {
    int value_type = LuaCEmbed_get_arg_type(args, 1);
    if (value_type == LUA_CEMBED_STRING) {
        printf("acceso al índice: %s\n", LuaCEmbed_get_str_arg(args, 1));
    }
    return NULL;
}

// Invocado cuando el recolector de basura de Lua destruye la tabla
LuaCEmbedResponse *gc_callback(LuaCEmbedTable *self, LuaCEmbed *args) {
    printf("tabla destruida por el 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("error: %s\n", LuaCEmbed_get_error_message(l));
    }
    LuaCEmbed_free(l);
    return 0;
}

Salida:

acceso al índice: foo
tabla destruida por el GC

Creando una Biblioteca (.so/.dll)

Además de ejecutar Lua desde C, también puedes hacer el camino inverso: compilar C como una biblioteca dinámica e importarla directamente desde Lua con 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);
}

// El nombre de la función DEBE ser "luaopen_" + nombre_de_la_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);
}

Compila como biblioteca compartida:

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

Usa en Lua:

local lib = require("my_lib")

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

Propiedades estáticas en la biblioteca

También puedes exponer constantes y valores fijos:

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);
}

En Lua:

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

Seguridad: Timeout y Límite de Memoria

Cuando ejecutas código enviado por usuarios, es importante proteger tu programa contra bucles infinitos y uso excesivo de memoria.

Timeout

Interrumpe la ejecución después de un número de segundos:

#include "LuaCEmbedOne.c"

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

    LuaCEmbed_set_timeout(2);  // interrumpe tras 2 segundos

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

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

    // Tras un timeout, puedes limpiar el error y seguir usando la instancia
    LuaCEmbed_clear_errors(l);
    LuaCEmbed_evaluate(l, "r = 'todavía funciona'");
    printf("%s\n", LuaCEmbed_get_evaluation_string(l, "r"));

    LuaCEmbed_free(l);
    return 0;
}

Salida:

error: timeout error
todavía funciona

Límite de Memoria

Impide que el código Lua consuma memoria de manera incontrolada (por defecto: 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 intenta aumentar una cadena indefinidamente
    LuaCEmbed_evaluate(l, "t = 'a'; while true do t = t .. t end");

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

    LuaCEmbed_free(l);
    return 0;
}

Salida:

error: not enough memory

Tratamiento de Errores

Siempre comprueba si hay errores después de cada evaluación. Si no lo haces, errores silenciosos podrían causar un comportamiento inesperado.

#include "LuaCEmbedOne.c"

LuaCEmbedResponse *mi_funcion(LuaCEmbed *args) {
    double val = LuaCEmbed_get_double_arg(args, 0);
    if (LuaCEmbed_has_errors(args)) {
        // Propaga el error a Lua en vez de continuar con un 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("error: %s\n", LuaCEmbed_get_error_message(l));

        // Limpia el error para continuar usando la misma instancia
        LuaCEmbed_clear_errors(l);
    }

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

    LuaCEmbed_free(l);
    return 0;
}

Salida:

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

Referencia Rápida

Función Descripción
newLuaCEmbedEvaluation() Crea una nueva instancia
LuaCEmbed_evaluate(l, code) Ejecuta código Lua (string)
LuaCEmbed_evaluete_file(l, path) Ejecuta un archivo Lua
LuaCEmbed_load_native_libs(l) Habilita libs nativas (print, io, os…)
LuaCEmbed_add_callback(l, name, fn) Registra una función C en Lua
LuaCEmbed_get_evaluation_string(l, expr) Lee el resultado como string
LuaCEmbed_get_evaluation_long(l, expr) Lee el resultado como entero
LuaCEmbed_get_evaluation_double(l, expr) Lee el resultado como decimal
LuaCEmbed_get_evaluation_bool(l, expr) Lee el resultado como booleano
LuaCEmbed_get_evaluation_type(l, expr) Retorna el tipo de la expresión
LuaCEmbed_get_evaluation_table_size(l, expr) Retorna el tamaño de una tabla
LuaCEmbed_has_errors(l) Comprueba si hubo un error
LuaCEmbed_get_error_message(l) Obtiene el mensaje de error
LuaCEmbed_clear_errors(l) Limpia el estado de error
LuaCEmbed_set_timeout(seconds) Define el timeout de ejecución
LuaCEmbed_set_memory_limit(l, mb) Define el límite de memoria en MB
LuaCEmbed_free(l) Libera la instancia