Repassar grandes estruturas em C para o formato de tabelas em Lua de forma otimizada

48 views
Skip to first unread message

Augusto Rodrigues

unread,
Apr 4, 2025, 10:46:28 PMApr 4
to lua...@googlegroups.com
Caros(as),

Imagine que tem uma aplicação em C que contém as seguintes declarações de tipos:

typedef struct {
char field_1;
/* N declarações de campos aqui */
int field_N;
} STRUCT_1;

/* Aqui várias declarações de estruturas de dados - STRUCT_2, ...., STRUCT_N-1*/

typedef struct {
char field_1;
/* N declarações de campos aqui */
int field_N;
} STRUCT_N;

Baseado nos tipos declarados acima, é construída o seguinte tipo de dado:
typedef struct {
STRUCT_1 struct_1;
/ N declarações de campos do tipo struct */
STRUCT_N struct_N;
} BIG_STRUCT;

A aplicação preenche todos os campos de uma variavel do tipo BIG_STRUCT e, se usar a maneira convencional de transformar estruturas em C em tabelas Lua, serei obrigado usar um lua_push* e um lua_setfield para campo da variável do tipo BIG_STRUCT.

Gostaria de saber se existe um outro jeito de transformar a variavel do tipo  BIG_STRUCT em C em tabelas Lua que serão processadas pelos scripts Lua.

Pesquisei sobre algumas alternativas:
- Usar o LuaJit com FFI: Entendi que o ffi.cdef consegue criar uma tabela a partir de um trecho de código em C através do uso de ffi.cdef e ffi.new

Consigo repassar o conteúdo da variável do tipo BIG_STRUCT para o Lua via lua_pushlstring. Mas no lado do script Lua não consigo visualizar uma forma de repassar esse stream de bytes para o objeto criado pela função ffi.new.

Seria possível fazer isso? Se sim, como ? Tem alguma função da biblioteca FFI que permita isso?

-Usar o pack e unpack: Mas aqui eu precisaria montar setters e getters para cada campo que compõe a estrutura BIG_STRUCT :-(

-Outra sugestão... qual?

Se alguem souber de um tutorial com exemplos em ingles sobre o uso do FFI tambem ajudaria muito. 

Atenciosamente
Augusto de Miranda Rodrigues

Luiz Henrique

unread,
Apr 5, 2025, 5:59:58 AMApr 5
to lua...@googlegroups.com
Sim, existe uma forma mais eficiente de transformar uma estrutura complexa como BIG_STRUCT em uma tabela Lua, sem ter que fazer lua_push* e lua_setfield para cada campo individualmente: usando userdata com metatables, ou serialização automática via bindings como Lua C API + introspecção ou bibliotecas externas.

Aqui estão três abordagens alternativas, dependendo do seu objetivo:


---

1. Criar um userdata e expor via metatable (acesso em Lua como se fosse tabela)

Você pode empacotar o BIG_STRUCT como um userdata e definir uma metatable com os métodos __index e __newindex que acessam os campos da estrutura dinamicamente:

BIG_STRUCT* lua_pushBIG_STRUCT(lua_State* L, BIG_STRUCT* data) {
    BIG_STRUCT* udata = (BIG_STRUCT*)lua_newuserdata(L, sizeof(BIG_STRUCT));
    memcpy(udata, data, sizeof(BIG_STRUCT));

    luaL_getmetatable(L, "BIG_STRUCT_MT");
    lua_setmetatable(L, -2);
    return udata;
}

Depois, você cria uma metatable:

int big_struct_index(lua_State* L) {
    BIG_STRUCT* data = (BIG_STRUCT*)luaL_checkudata(L, 1, "BIG_STRUCT_MT");
    const char* key = luaL_checkstring(L, 2);

    if (strcmp(key, "field_1") == 0) {
        lua_pushstring(L, &data->struct_1.field_1);
        return 1;
    }
    // etc...

    return 0;
}

void register_big_struct(lua_State* L) {
    luaL_newmetatable(L, "BIG_STRUCT_MT");

    lua_pushcfunction(L, big_struct_index);
    lua_setfield(L, -2, "__index");

    lua_pop(L, 1); // remove metatable
}

Vantagem: Você encapsula tudo e evita vários lua_push*.


---

2. Serialização automática com uma biblioteca como luaL_pack ou SWIG/sol2/LuaBridge

Com bibliotecas externas, você pode automatizar a conversão de structs em tabelas sem escrever manualmente cada lua_push*. Exemplos:

SWIG: Gera bindings automáticos entre C/C++ e Lua.

sol2 (C++): Permite acessar structs diretamente como tabelas em Lua.

LuaBridge (C++): Similar ao sol2.



---

3. Geração automática de tabela Lua com introspecção e lua_newtable

Se quiser continuar com a tabela, mas automatizar o processo, pode fazer uma função que percorra os campos da BIG_STRUCT usando macros ou metaprogramação (em C++ é mais fácil) para fazer um dump de toda estrutura em uma tabela Lua:

void push_BIG_STRUCT_as_table(lua_State* L, BIG_STRUCT* data) {
    lua_newtable(L);

    // struct_1
    lua_pushstring(L, "struct_1");
    lua_newtable(L);
    lua_pushstring(L, "field_1");
    lua_pushlstring(L, &data->struct_1.field_1, 1);
    lua_settable(L, -3);
    // etc...
    lua_settable(L, -3);

    // struct_N
    lua_pushstring(L, "struct_N");
    lua_newtable(L);
    lua_pushstring(L, "field_N");
    lua_pushinteger(L, data->struct_N.field_N);
    lua_settable(L, -3);
    lua_settable(L, -3);
}

Você ainda precisa escrever campo a campo, mas pode automatizar isso com macros.


--
Lua BR - https://groups.google.com/g/lua-br
---
Você recebeu essa mensagem porque está inscrito no grupo "Lua BR" dos Grupos do Google.
Para cancelar inscrição nesse grupo e parar de receber e-mails dele, envie um e-mail para lua-br+un...@googlegroups.com.
Para ver esta conversa, acesse https://groups.google.com/d/msgid/lua-br/CABoUv5QQt%3D5WZwMyBCvGFvPEm7QcNgtB2Q%2BcEoN09kON%2Be2GGQ%40mail.gmail.com.

Augusto Rodrigues

unread,
Apr 5, 2025, 4:19:43 PMApr 5
to lua...@googlegroups.com
Luiz Henrique,

Irei pesquisar sobre o SWIG. Já tinha visto o LuaBrigde e o Sol2 mas ambos são para C++. A aplicação em questão é, por enquanto, escrita essencialmente em C.

Porém acredito que o FFI com o LuaJit pode resolver o problema de não ter que gerar as funções de acesso à leitura e escrita dos campos da tabela.
Segue um exemplo obtido do chatGPT (não testei o código):

local ffi = require("ffi")
-- Define a C struct
ffi.cdef[[
    typedef struct {
        int x;
        int y;
    } Point;
]]
local pt = ffi.new("Point") 
-- Create a new instance of the struct
local byte_stream = "\0\0\0\10\0\0\0\20"  -- equivalent to {x = 10, y = 20}
ffi.copy(pt, byte_stream, ffi.sizeof(pt)) 
-- Copy the Lua string into the Point structure
print("x: ", pt.x)  -- Output: x: 10
print("y: ", pt.y)  -- Output: y: 20


A variavel byte_stream poderia ser preenchida através de uma chamada fornecida pela camada em C. Algo assim:
local byte_stream = get_stream_bytes_from_point_struct()

Esta função iria obter o conteúdo de uma variavel do tipo Point e já preenchida pela aplicação e passaria este 
conteúdo para o Lua na forma de string Lua.

Através de outra função binding C Lua ou no final do script Lua, o conteudo da variavel instanciada pela função ffi.new poderia ser 
retornado para a camada em C na forma de stream de bytes. A camada em C gravaria esse stream de bytes na 
variavel que foi lida pela função get_stream_bytes_from_point_struct()

Algo assim (Nota: código abaixo não foi testado)
local ffi = require("ffi")
-- Define a C struct
ffi.cdef[[
    typedef struct {
        int x;
        int y;
    } Point;
]]
local pt = ffi.new("Point") 
 -- Create a new instance of the struct
local byte_stream = 
get_stream_bytes_from_point_struct()
ffi.copy(pt, byte_stream, ffi.sizeof(pt)) 
 -- Copy the Lua string into the Point structure
print("x: ", pt.x)  -- Output: x: 10
print("y: ", pt.y)  -- Output: y: 20
-- Run script here with any changes in pt's variable
return pt -- Or use binding C Lua called put_stream_bytes_from_point_struct()

Preciso testar a ideia acima pois somente tenho o LuaJit com FFI no ambiente de trabalho, porém se der certo, 
acredito que resolveria o dilema de criar setters e getters para variáveis derivadas de estruturas com grande 
quantidade mista de tipos de campos.

Vou testar a ideia acima e depois notifico vocês se deu certo ou não. E também irei analisar a sua sugestão de uso
do SWIG.

Atenciosamente,
Augusto de Miranda Rodrigues


Augusto Rodrigues

unread,
Apr 13, 2025, 12:50:20 PMApr 13
to lua...@googlegroups.com
Caros(as),

Validei o algoritmo acima utilizando o FII com LuaJit para transferir dados entre a camada C para o LuaJit sem criar funções para coletar ou gravar dados nas variaveis geradas a partir 
de grandes estruturas.

Vou verificar o lance de ponteiros que permite que os dados na variavel global declarada em C seja diretamente alterada pelos scritps Lua usando o FFI e LuaJit. Se isso der certo, posso 
obter algum ganho de performance.

De qualquer forma valeu pelas dicas.

Fui

Reply all
Reply to author
Forward
0 new messages