Estoy tratando de llamar a una biblioteca C desde dart. El código C como método generador como
a_struc create_struct(void);y luego métodos que toman un puntero a la estructura, como:
const char * get_info(const a_struc * s);Creé la unión de dardos usando ffigen y tengo
class a_struct extends ffi.Struct { } a_struct create_struct() { return create_struct(); } late final create_structPtr = _lookup<ffi.NativeFunction<a_struct Function()>>( 'create_struct'); late final create_struct = create_structPtr .asFunction<a_struct Function()>();y
ffi.Pointer<ffi.Int8> get_info( ffi.Pointer<a_struct> a_struct, ) { return get_info( a_struct, ); } late final get_infoPtr = _lookup< ffi.NativeFunction< ffi.Pointer<ffi.Int8> Function( ffi.Pointer<a_struct>)>>('rcl_publisher_get_topic_name'); late final get_info = get_infoPtr.asFunction< ffi.Pointer<ffi.Int8> Function(ffi.Pointer<a_struct>)>(); Mi problema es que no sé cómo llamar al método get_info desde el dardo a_struct generado por create_struct . De hecho, get_info espera un ffi.Pointer<a_struct> pero solo genero un a_struct sin el puntero. Dado que dart dejó de .addressOf , ¿cómo puedo obtener un ffi.Pointer<a_struct> de create_struct ?
Cuando devuelve una estructura por valor de C a Dart, la estructura se copia en un bloque de memoria reubicable que Dart administra completamente.
Dart no proporciona una API que exponga la dirección subyacente de estos objetos, por lo que si necesita pasar esos datos a C por referencia, deberá almacenarlos en una región de memoria nativa no reubicable (es decir, asignarlos en montón). o use el estado C global) y realice un seguimiento de un puntero en el lado de Dart.
Si no puede (o prefiere no hacerlo) modificar la biblioteca de C++, puede usar el paquete Dart FFI para llamar a malloc<MyStruct>() y luego copiar la estructura en el espacio recién asignado con ref= setter.
final ffi.Pointer<MyStruct> myStructPtr = malloc<MyStruct>() ..ref = createStructFn(); // ... pass myStructPtr to other FFI functions by reference ... malloc.free(myStructPtr); Tenga en cuenta que el setter ref= en StructPointer aterrizó en Dart el 12 de enero de 2022 (muy recientemente, en el momento de escribir este artículo).
Ejemplo completo a continuación.
C++:
// Build command: g++ -shared lib.cc -o lib.dll -Wl,--out-implib,lib.a #include <cstdlib> extern "C" { typedef struct { const char* info; } MyStruct; MyStruct CreateStruct() { return {.info = "Hello Dart!"}; } const char* GetInfo(MyStruct* s) { return s->info; } }Dardo:
import 'dart:ffi' as ffi; import 'package:ffi/ffi.dart'; class MyStruct extends ffi.Struct { external ffi.Pointer<Utf8> info; } typedef CreateStruct = MyStruct Function(); typedef GetInfo = ffi.Pointer<Utf8> Function(ffi.Pointer<MyStruct>); void main() { // Load FFI procs. final lib = ffi.DynamicLibrary.open('lib.dll'); final createStructFn = lib.lookupFunction<CreateStruct, CreateStruct>('CreateStruct'); final getInfoFn = lib.lookupFunction<GetInfo, GetInfo>('GetInfo'); // Allocate a native memory region and copy the returned struct into it. final myStructPtr = malloc<MyStruct>()..ref = createStructFn(); // It's a pointer, so we can pass by reference. final result = getInfoFn(myStructPtr); print(result.toDartString()); // Cleanup! malloc.free(myStructPtr); } Como comentario aparte: antes de que aterrizaran las características de "estructuras de retorno por valor" , todas las estructuras pasadas de C a Dart tenían que pasar por referencia. Esa es la razón por la que addressOf estaba inicialmente disponible en todas las estructuras FFI: todas eran ffi.Pointer s. Para admitir backends de almacenamiento administrados por Dart, ffi.Pointer debía desacoplarse de ffi.Struct . ¡El resultado es una API mucho más sensible! Por ejemplo, puede tener un Pointer<Pointer<Uint8>> y funciona exactamente como lo esperaría.