Dart FFI使用 C调用Dart函数

2,736 阅读3分钟

这是我参与11月更文挑战的第5天,活动详情查看:2021最后一次更文挑战

之前我们知道了Dart如何调用C的函数,那么C函数如何能调用Dart函数呢。下面我们通过示例来了解一下。

C调Dart函数

简单示例

原理: C本身是没有提供调用Dart函数的方法的,但是我们可以在程序启动后通过Dart将函数当做参数传入C中,C中缓存起来Dart的函数指针,就可以在需要的时候实现C调用Dart。

首先,我们先在Dart上定义一个函数。需要注意的是Dart函数需要是顶级函数或者静态函数才能被调用,否则会报错.

void dartFunction() {
  debugPrint("[Dart]: Dart 函数被调用了");
}

我们在C中定义一个注册函数 sample.h

void callDart(void (*callback)());

sample.c

void callDart(void (*callback)()) {
    printf("[CPP]: 现在调用Dart函数");
    callback();
}

其中的callback就是接收到的Dart的函数,这里我们为了看效果,就在注册后直接调用Dart函数了。

然后我们将Dart函数转换成Pointer类型,并通过调用C的callDart函数传入到C中。

late final _callDartPtr = _lookup<
          ffi.NativeFunction<
              ffi.Void Function(
                  ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>>)>>(
      'callDart');
late final _callDart = _callDartPtr.asFunction<
    void Function(ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>>)>();
_callDart(ffi.Pointer.fromFunction(dartFunction));

这里,我们试用结果ffi.Pointer.fromFunction方法将Dart函数转换成C函数指针的Dart映射,然后通过_callDart来调用C的callDart函数。

运行后输出:

[CPP]: 现在调用Dart函数
[Dart]: Dart 函数被调用了

成功!

带参数的Dart函数

C如何调用带参数的Dart函数呢,我们下面来定义一个Dart函数

static void add(int num1,int num2) {
    print("[Dart]: num1: ${num1}, num2: ${num2}");
}

上面函数被调用后会输出num1num2的值。

然后我们改造一下callDart函数 sample.h

void callDart(void (*callback)(), void (*add)(int, int));

sample.c

void callDart(void (*callback)(), void (*add)(int, int)) {
    printf("现在调用Dart函数");
    callback();

    printf("调用Dart Add函数");
    add(1, 2);
}

dart端

late final _callDartPtr = _lookup<
      ffi.NativeFunction<
          ffi.Void Function(
              ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>>,
              ffi.Pointer<
                  ffi.NativeFunction<
                      ffi.Void Function(ffi.Int32, ffi.Int32)>>)>>('callDart');
late final _callDart = _callDartPtr.asFunction<
    void Function(
        ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>>,
        ffi.Pointer<
            ffi.NativeFunction<ffi.Void Function(ffi.Int32, ffi.Int32)>>)>();

_callDart(ffi.Pointer.fromFunction(DartFunctions.dartFunction),ffi.Pointer.fromFunction(DartFunctions.add));

返回输出

[CPP]: 现在调用Dart函数
[Dart]: Dart 方法被调用了
[CPP]: 调用Dart Add函数
[Dart]: num1: 1, num2: 2

这样,参数就从C传到Dart端了。

获取返回值

上面的示例都只是调用Dart函数,并没有从Dart端获取返回值。我们再来改造一下add方法,让它可以返回num1 num2相加的值。

static int add(int num1, int num2) {
    return num1 + num2;
}

sample.h

void callDart(void (*callback)(), int (*add)(int, int));

sample.c

void callDart(void (*callback)(), int (*add)(int, int)) {
    printf("现在调用Dart函数");
    callback();

    printf("调用Dart Add函数");
    int result = add(1, 2);
    printf("Add 结果 %d", result);
}

main.dart

late final _callDartPtr = _lookup<
    ffi.NativeFunction<
        ffi.Void Function(
            ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>>,
            ffi.Pointer<
                ffi.NativeFunction<
                    ffi.Int32 Function(ffi.Int32, ffi.Int32)>>)>>('callDart');
late final _callDart = _callDartPtr.asFunction<
    void Function(
        ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>>,
        ffi.Pointer<
            ffi.NativeFunction<ffi.Int32 Function(ffi.Int32, ffi.Int32)>>)>();
_callDart(ffi.Pointer.fromFunction(DartFunctions.dartFunction),ffi.Pointer.fromFunction(DartFunctions.add, 0));

需要注意的是,如果Dart函数有返回值,fromFunction的第二个参数就需要传入当出错时返回的值。

输出结果

[CPP]: 现在调用Dart函数
[Dart]: Dart 方法被调用了
[CPP]: 调用Dart Add函数
[Dart]: num1: 1, num2: 2
[CPP]: Add 结果 3

好了,现在我们就学会了如何使用C调用Dart函数了。当然实际项目中,我们一般需要定义一个初始函数,把想要C调用的Dart函数传入到C的内存中,在C需要的时候调用。

上面代码我都提交到我的Github仓库中,有学习需要自取,传送门