03. Flutter FFI function

 
Flutter FFI Study Notes Series

  1. "The Simplest Example of Flutter FFI"
  2. "Flutter FFI Basic Data Type"
  3. "Flutter FFI Function"
  4. "Flutter FFI String"
  5. "Flutter FFI Structure"
  6. "Flutter FFI Class"
  7. "Flutter FFI Array"
  8. "Flutter FFI Memory Management"
  9. 《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_addThe function type above is the representation of the C function in Dart, and the parameters and return values ​​of the function are attributes NativeType(for example: Int32);
  • FFI_addThe 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 uses DynamicLibrary.process()to load symbol information;
  • Use DynamicLibrary.lookup()or DynamicLibrary.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 of lookup()and asFunction()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 it globalCallbackinto a function pointer as a parameter calc()of ;
  • In the C calc()function , directly globalCallbackcall Dart's as a function pointer;

  Finally, globalCallbackis 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.
  
  
  
 

Guess you like

Origin blog.csdn.net/eieihihi/article/details/119151671