03、Flutter FFI 函数

 
Flutter FFI 学习笔记系列

  1. 《Flutter FFI 最简示例》
  2. 《Flutter FFI 基础数据类型》
  3. 《Flutter FFI 函数》
  4. 《Flutter FFI 字符串》
  5. 《Flutter FFI 结构体》
  6. 《Flutter FFI 类》
  7. 《Flutter FFI 数组》
  8. 《Flutter FFI 内存管理》
  9. 《Flutter FFI Dart Native API》
      
     

  在前面的章节中,演示了如何在 Dart 中访问 C 中的函数。接下来将详细介绍 C 和 Dart 函数的相互调用。
  
 

1、Dart 调用 C 函数

1.1 C函数的定义

  Dart 语言只能调用 C 语言风格的函数,不能调用 C++ 语言风格的函数,因此,函数需要加上 extern "C" 前缀,这表示告诉编译器按C语言风格编译该函数。同时,为了避免编译器在编译优化阶段把没有使用到的符号删除掉,需要在函数前面加上 __attribute__((visibility("default")))__attribute__((used))
  
  C 语言函数的定义格式如下:

#include <stdint.h>

extern "C" __attribute__((visibility("default"))) __attribute__((used))
int32_t native_add(int32_t x, int32_t y) {
    
    
    return x + y;
}

  为了编写方便,可以使用宏定义来简化代码:

#include <stdint.h>
#define DART_API extern "C" __attribute__((visibility("default"))) __attribute__((used))

DART_API int32_t native_add(int32_t x, int32_t y) {
    
    
    return x + y;
}

1.2 Dart函数类型定义

  在 Dart 中,需要定义与 C 函数相对应的函数类型,以便在 Dart 中调用。
  这里需要使用 Dart Function 定义两个函数类型,一个用于表示 C ,一个用于 Dart,如下:

///对应C语言函数声明:int32_t native_add(int32_t x, int32_t y)
typedef Native_add = Int32 Function(Int32 x, Int32 y);

///Dart使用的函数
typedef FFI_add = int Function(int x, int y);

  说明:

  • 上面的 Native_add 函数类型,是 C 函数在 Dart 中的表示形式,函数的参数和返回值都属性 NativeType (例如:Int32);
  • 上面的 FFI_add 函数类型,是 Dart 风格的函数,供 Dart 调用,函数参数和返回值都是 Dart 中的数据类型;
  • 当使用 FFI 绑定这两个函数类型的时候,数据类型的转换将由 FFI 内部完成。
     

1.3 Dart调用C函数

  完成函数的声明和定义之后,就可以通过以下步骤在 Dart 中调用 C 函数了:

  • Android 平台使用 DynamicLibrary.open() 加载符号信息,iOS 平台使用 DynamicLibrary.process() 加载符号信息;
  • 通过 DynamicLibrary.lookup() 或者 DynamicLibrary.lookupFunction() 来查找函数符号,并转为 Dart 函数;

  调用 Dart 函数。示例代码如下:

//加载符号
DynamicLibrary nativeApi = Platform.isAndroid
        ? DynamicLibrary.open("libnative_ffi.so")
        : DynamicLibrary.process();

//方法1 - 查找函数符号,并转为Dart函数
final addFunc1 = nativeApi
    .lookupFunction<Native_add, FFI_add>("native_add");

//方法2 - 查找函数符号,并转为Dart函数
FFI_add addFunc2 = nativeApi
    .lookup<NativeFunction<Native_add>>("native_add")
    .asFunction();

//此时,调用Dart函数就是调用C函数
int result = addFunc1(1, addFunc2(1, 2));
print("1+(1+2)=$result");

//输出信息:
//1+(1+2)=4

  说明:

  • lookupFunction() 其实就是 lookup()asFunction() 的封装,简化代码。
     

1.4 变长参数函数

  C 语言中具有变长参数函数,但是在 FFI 中,必须要指定参数类型和个数才行。
  
  下面这个示例演示了如何在 Dart 调用 C 的变长参数函数:
  首先,在 C 中定义一个函数:

#include <stdint.h>
#include <stdarg.h>
#define DART_API extern "C" __attribute__((visibility("default"))) __attribute__((used))

DART_API int multi_sum(int nr_count, ...) {
    
    
    va_list nums;
    va_start(nums, nr_count);
    int sum = 0;
    for (int i = 0; i < nr_count; i++)
    {
    
    
        sum += va_arg(nums, int);
    }
    va_end(nums);
    return sum;
}

  然后,在 Dart 中定义两个函数类型,与 C 中的函数进行映射:

///对应C语言函数
typedef Native_multi_sum = Int32 Function(
    Int32 numCount, Int32 a, Int32 b, Int32 c);

///供Dart所使用的函数
typedef FFI_multi_sum = int Function(int numCount, int a, int b, int c);
 

  最后,通过 lookupFunction() 找到该符号并调用:

final sumFunc = nativeApi
    .lookupFunction<Native_multi_sum,FFI_multi_sum>("multi_sum");

print("result:${sumFunc(3, 1, 2, 3)}");

//输出结果
//result:6

  说明:


  •  

2、C 调用 Dart 函数

  上面介绍了如何在 Dart 中调用 C 的函数,下面看看如何在 C 中调用 Dart 的函数。
  在 Dart 中,只有全局函数才能给被 C 调用。我们可以通过 Pointer.fromFunction() 函数将 Dart Function 转为 C 的函数指针。fromFunction() 函数声明如下:

/// Convert Dart function to a C function pointer, automatically marshalling
/// the arguments and return value
///
/// If an exception is thrown while calling `f()`, the native function will
/// return `exceptionalReturn`, which must be assignable to return type of `f`.
///
/// The returned function address can only be invoked on the mutator (main)
/// thread of the current isolate. It will abort the process if invoked on any
/// other thread.
///
/// The pointer returned will remain alive for the duration of the current
/// isolate's lifetime. After the isolate it was created in is terminated,
/// invoking it from native code will cause undefined behavior.
///
/// Does not accept dynamic invocations -- where the type of the receiver is
/// [dynamic].
external static Pointer<NativeFunction<T>> fromFunction<T extends Function>(
    @DartRepresentationOf("T") Function f,
    [Object? exceptionalReturn]);

  下面通过一个示例来演示 C 如何调用 Dart 函数。
  首先,在 C 定义一个函数:

#include <malloc.h>

#define DART_API extern "C" __attribute__((visibility("default"))) __attribute__((used))

DART_API
void calc(int32_t src, void (*callback)(int32_t, int32_t)) {
    
    
    int result = src * 10000;
    callback(src, result);
}

  然后在 Dart 定义相应的函数类型以及 全局回调函数:

typedef Callback = Void Function(Int32, Int32);
typedef Native_calc = Void Function(Int32, Pointer<NativeFunction<Callback>>);
typedef FFI_calc = void Function(int, Pointer<NativeFunction<Callback>>);

void globalCallback(int src, int result) {
    
    
  print("globalCallback src=$src, result=$result");
}

  说明:

  • 函数指针的类型为:Pointer<NativeFunction<...>
  • globalCallback() 是一个全局函数,用于给 C 调用。

  最后,在 Dart 调用 C 的函数:

//加载 C 符号
DynamicLibrary nativeApi = Platform.isAndroid
        ? DynamicLibrary.open("libnative_ffi.so")
        : DynamicLibrary.process();

//查找函数
FFI_calc calcFunc = nativeApi.lookupFunction<Native_calc, FFI_calc>("calc");

//调用函数
calcFunc(32, Pointer.fromFunction(globalCallback));

//输出结果:
//I/flutter ( 3920): globalCallback src=32, result=320000

  说明:

  • 我们在 Dart 中调用了 C 的 calc() 函数,并将 globalCallback 转为函数指针作为 calc() 的参数;
  • 在 C 的 calc() 函数内,直接把 Dart 的 globalCallback 当作函数指针进行调用;

  最后 globalCallback 被调用,打印了相应的信息。
 

3、总结

  
  上面介绍如何通过 FFI 实现 C 与 Dart 的相互调用,在后面的章节中,将会介绍字符串、结构体、数组、内存管理等知识,欢迎关注。
  
  
  
 

猜你喜欢

转载自blog.csdn.net/eieihihi/article/details/119151671