3

我正在尝试从 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>)>();

我的问题是我不知道如何get_infoa_struct. create_struct确实,get_info期望 affi.Pointer<a_struct>但我只生成a_struct没有指针的 a 。由于 dart 已弃用.addressOf,我如何获得ffi.Pointer<a_struct>from create_struct

4

1 回答 1

4

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

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

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

final ffi.Pointer<MyStruct> myStructPtr = malloc<MyStruct>()
  ..ref = createStructFn();

// ... pass myStructPtr to other FFI functions by reference ...

malloc.free(myStructPtr);

请注意,ref=二传手于2022 年 1 月 12 日StructPointer登陆 Dart (在撰写本文时是最近的事)。

下面的完整示例。

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

顺便说一句:在“按值返回结构”功能落地之前,从 C 传递到 Dart 的所有结构都必须通过引用传递。这addressOf就是最初在所有 FFI 结构上都可用的原因——它们都是 ffi.Pointers. 为了支持 Dart 管理的存储后端,ffi.Pointer需要与ffi.Struct. 结果是一个更明智的 API!例如,您可以拥有 aPointer<Pointer<Uint8>>并且它的功能完全符合您的预期。

于 2022-01-25T12:33:11.783 回答