【问题标题】:Dart FFI keep Struct pointerDart FFI 保持结构指针
【发布时间】:2022-01-01 12:01:42
【问题描述】:

我正在尝试从 dart 调用 C 库。 C代码作为生成器方法如

a_struc  create_struct(void);

然后是接受结构指针的方法,例如:

const char *
 get_info(const a_struc * s);

我已经使用 ffigen 创建了飞镖绑定

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

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

我的问题是我不知道如何从create_struct生成的darta_struct调用方法get_info。事实上,get_info 需要一个 ffi.Pointer&lt;a_struct&gt;,但我只生成一个没有指针的 a_struct。由于 dart 已弃用 .addressOf,我如何从 create_struct 获取 ffi.Pointer&lt;a_struct&gt;

【问题讨论】:

    标签: c flutter dart ffi dart-ffi


    【解决方案1】:

    当你从 C 中按值返回结构体到 Dart 时,结构体被复制到完全由 Dart 管理的可重定位内存块中。

    Dart 不提供公开这些对象底层地址的 API,因此如果您需要通过引用将该数据传递给 C,则需要将其存储在不可重定位的本机内存区域中(即堆分配它或使用全局 C 状态)并在 Dart 端跟踪指向它的指针。

    如果您不能(或不想)修改 C++ 库,您可以使用 Dart FFI 包调用 malloc&lt;MyStruct&gt;(),然后使用 ref= setter 将结构复制到新分配的空间中。

    final ffi.Pointer<MyStruct> myStructPtr = malloc<MyStruct>()
      ..ref = createStructFn();
    
    // ... pass myStructPtr to other FFI functions by reference ...
    
    malloc.free(myStructPtr);
    

    请注意,StructPointer 中的 ref= 设置器在 Dart on Jan 12, 2022 中出现(最近,在撰写本文时)。

    下面的完整示例。

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

    飞镖:

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

    顺便说一句:在"returning structs by value" features 登陆之前,从 C 传递到 Dart 的 所有 结构都必须通过引用传递。这就是 addressOf 最初可用于所有 FFI 结构的原因——它们都ffi.Pointers。为了支持 Dart 管理的存储后端,ffi.Pointer 需要与 ffi.Struct 分离。 结果是一个更明智的 API!例如,您可以拥有一个Pointer&lt;Pointer&lt;Uint8&gt;&gt;,它的功能与您期望的完全一样。

    【讨论】:

      猜你喜欢
      • 2018-09-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-07-20
      • 2021-10-11
      相关资源
      最近更新 更多