Flutter FFI Study Notes Series
- "The Simplest Example of Flutter FFI"
- "Flutter FFI Basic Data Type"
- "Flutter FFI Function"
- "Flutter FFI String"
- "Flutter FFI Structure"
- "Flutter FFI Class"
- "Flutter FFI Array"
- "Flutter FFI Memory Management"
- 《Flutter FFI Dart Native API》
In the previous chapters, it was demonstrated how to access functions in C from Dart. Next, we will introduce the mutual calling of C and Dart functions in detail.
1. Dart calls C function
1.1 Definition of C function
The Dart language can only call functions in the C language style, and cannot call functions in the C++ language style. Therefore, the function needs to be extern "C"
prefixed , which means telling the compiler to compile the function in the C language style. At the same time, in order to prevent the compiler from deleting unused symbols during the compilation optimization stage, it is necessary to add __attribute__((visibility("default")))
and __attribute__((used))
.
The definition format of a C language function is as follows:
#include <stdint.h>
extern "C" __attribute__((visibility("default"))) __attribute__((used))
int32_t native_add(int32_t x, int32_t y) {
return x + y;
}
For writing convenience, macro definitions can be used to simplify the code:
#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 function type definition
In Dart, a function type corresponding to a C function needs to be defined in order to be called in Dart.
Here you need to use Dart Function to define two function types, one for C and one for Dart, as follows:
///对应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);
illustrate:
Native_add
The function type above is the representation of the C function in Dart, and the parameters and return values of the function are attributesNativeType
(for example:Int32
);FFI_add
The function type above is a Dart-style function for Dart to call, and the function parameters and return values are all data types in Dart;- When using FFI to bind these two function types, the conversion of data types will be completed by FFI internally.
1.3 Dart calls C function
After completing the declaration and definition of the function, you can call the C function in Dart through the following steps:
- The Android platform uses
DynamicLibrary.open()
to load symbol information, and the iOS platform usesDynamicLibrary.process()
to load symbol information; - Use
DynamicLibrary.lookup()
orDynamicLibrary.lookupFunction()
to find the function symbol and convert it to a Dart function;
Call the Dart function. The sample code is as follows:
//加载符号
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
illustrate:
lookupFunction()
In fact, it is the encapsulation oflookup()
andasFunction()
to simplify the code.
1.4 Variable-length parameter functions
There are variable-length parameter functions in C language, but in FFI, the type and number of parameters must be specified.
The following example demonstrates how to call C variable-length parameter functions in Dart:
First, define a function in 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;
}
Then, define two function types in Dart to map with functions in 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);
Finally, lookupFunction()
find and call:
final sumFunc = nativeApi
.lookupFunction<Native_multi_sum,FFI_multi_sum>("multi_sum");
print("result:${sumFunc(3, 1, 2, 3)}");
//输出结果
//result:6
illustrate:
- none
2. C calls Dart function
The above describes how to call C functions in Dart, let's see how to call Dart functions in C.
In Dart, only global functions can be called from C. We can convert Dart Function to C function pointer through Pointer.fromFunction()
function . fromFunction()
The function declaration is as follows:
/// 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]);
Below is an example to demonstrate how C calls Dart functions.
First, define a function in 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);
}
Then define the corresponding function type and global callback function in 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");
}
illustrate:
- The type of the function pointer is:
Pointer<NativeFunction<...>
; globalCallback()
Is a global function for calling to C.
Finally, call the C function from Dart:
//加载 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
illustrate:
- We call
calc()
the function , and convert itglobalCallback
into a function pointer as a parametercalc()
of ; - In the C
calc()
function , directlyglobalCallback
call Dart's as a function pointer;
Finally, globalCallback
is called and the corresponding information is printed.
3. Summary
The above describes how to realize the mutual calling between C and Dart through FFI. In the following chapters, we will introduce the knowledge of strings, structures, arrays, memory management, etc. Welcome to pay attention.