flutter Dart:dart:ffi中的回调仅支持从本机代码调用静态Dart函数

wwwo4jvm  于 6个月前  发布在  Flutter
关注(0)|答案(3)|浏览(165)

这篇文章是Github Issue here的副本。
dart --version Dart SDK版本:2.15.0-116.0.dev(dev)(Thu Sep 16 09:47:01 2021 -0700)on“linux_x64”
我一直在寻找examples的回调,我试图让回调为我在FFI工作。

我的现状

我的库中有一个函数,它需要一个指向函数的指针。ffigen生成的绑定对我来说似乎是正确的。

int SetCallback(
    CallbackType callback,
  ) {
    return _SetCallback(
      callback,
    );
  }

  late final _SetCallbackPtr =
      _lookup<NativeFunction<Int32 Function(CallbackType)>>(
          'SetCallback');
  late final _SetCallback =
      _SetCallbackPtr.asFunction<int Function(CallbackType)>();

字符串
其中,typedef CallbackType = Pointer<NativeFunction<Void Function(Uint32)>>;
我想在这里做的是在Dart中设置这个回调函数,将其传递给FFI,本质上将其用作我在C中的回调函数。在我的API中,它从FFI代码中抽象出来(这意味着我有一个充满静态函数的类MyLibrary,用户将直接调用这些静态函数,然后从我创建的类MyNativeLibrary的object _nativeLibrary调用函数),我有:

static int SetCallback({required CallbackFuncDart callback}) {
    Pointer<NativeFunction<CallbackFunc>> pointer = Pointer.fromFunction(callback);

    int status = _nativeLibrary.SetCallback(
      pointer,
    );
    if (STATUS_OK != status) {
      throw LibLexemeException(status);
    }
    return status;
  }

typedef CallbackFunc = Void Function(Uint32);
typedef CallbackFuncDart = void Function(int);


虽然the sqlite ffi example在这里指出,
dart:ffi尚不支持的功能:

Callbacks from C back into Dart.


我相信文档还没有更新以反映the samples here.的变化。示例还不是很清楚,因为它们没有任何C/C++文件,或者C函数是如何工作的。即便如此,我认为this example包含一个段(最后一个代码块)其中一个Dart函数被作为回调传递,我已经在我的程序中复制了它。我不清楚这将如何工作,但在尝试编译我的我得到的程序:

ERROR: ../lib/library_lexeme.dart:180:74: Error: fromFunction expects a static function as parameter. dart:ffi only supports calling static Dart functions from native code. Closures and tear-offs are not supported because they can capture context.
ERROR:     Pointer<NativeFunction<CallbackFunc>> pointer = Pointer.fromFunction(callback);

1zmg4dgp

1zmg4dgp1#

简单来说,你不能把回调作为参数传递:

static int SetCallback({required CallbackFuncDart callback}) {
    Pointer<NativeFunction<CallbackFunc>> pointer = Pointer.fromFunction(callback); // <-- this isn't considered a static function

字符串
这很烦人,但你必须使用一个预先定义的静态函数,以便从C调用dart回调。

envsm3lx

envsm3lx2#

显然,目前只有静态函数可以通过ffi传递。但是如果你必须访问一个示例的数据,并且你确定该示例存在,你可以使用我的解决方案。我对示例使用静态列表。这是愚蠢和丑陋的,但它对我有效:

class CallbackClass {
  static Int8 classCallback(int id) { 
    final instance = instanceList[id];
    return instance.instanceCallback();
  }

  Int8 instanceCallback() { return instanceId; }

  static List<CallbackClass> instanceList = <CallbackClass>[];
  late final int instanceId;

  CallbackClass {
    instanceId = instanceList.length;
    instanceList.insert(instanceId, this);
    myFFImapping.passCallback(instanceId, Pointer.fromFunction<>(classCallback);)
  }
}

字符串
为了清晰起见,我省略了必要的c代码、FFIMap和类型转换,因此它显然不会像这样编译。

mxg2im7a

mxg2im7a3#

使用Dart 3.2,您现在可以从任何Dart函数或闭包创建回调,扩展到静态或顶级函数之外。
目前有两种方法可以实现这一点:

***NativeCallable.isolateLocal:**这构造了一个NativeCallable,必须从创建它的 * 同一个 * 线程调用。它构建在Pointer.fromFunction提供的功能之上,仅限于从静态或顶级函数创建函数指针。
***NativeCallable.listener:**这将构造一个可以从 * 任何 * 线程调用的NativeCallable。此功能在Dart 3.1中引入,仅支持void函数。

下面是示例的修改版本,现在包含了NativeCallable.isolateLocal

static int SetCallback({required CallbackFuncDart callback}) {
  final nativeCallable = NativeCallable<CallbackFunc>.isolateLocal(callback);
  int status = _nativeLibrary.SetCallback(nativeCallable.nativeFunction);
  if (STATUS_OK != status) throw LibLexemeException(status);
  return status;
}

typedef CallbackFunc = Void Function(Uint32);
typedef CallbackFuncDart = void Function(int);

字符串

相关问题