[LINUX] Call the C function with dart: ffi and call the Dart function callback

Introduction

How to use dart: ffi that calls C function from Dart is documented on the following page.

However, all of them were examples of simple synchronization function calls, so I will write the implementation method of the callback function call as a reminder. If you have any mistakes or better ways, please let us know.

The document was not found, Sample in dart-lang / sdk. dev / samples / ffi / sample_ffi_functions_callbacks.dart) was found, so I referred to it.

The operation check environment is as follows. In addition, we have confirmed the operation on Linux Desktop as the platform of Flutter.

$ uname -a
Linux chama 5.5.8 #1 SMP Sat Mar 7 22:29:22 JST 2020 x86_64 x86_64 x86_64 GNU/Linux
$ lsb_release -d
Description:    Ubuntu 18.04.5 LTS
$ flutter --version
Flutter 1.24.0-8.0.pre.117 • channel master • https://github.com/flutter/flutter
Framework • revision 8cb2665118 (8 hours ago) • 2020-11-06 16:02:19 +0800
Engine • revision 0693ee04d1
Tools • Dart 2.12.0 (build 2.12.0-21.0.dev)

Callback function to be implemented this time

This time I chose qsort (3) as a function to call the famous callback function that can be used anywhere. Actually, I think I will use qsort_r (3), but since it is not the main point of this time, I use qsort (3) to keep the code simple.

Here is a prototype and description of qsort (3) from man.

void qsort(void *base, size_t nmemb, size_t size,
           int (*compar)(const void *, const void *));

[man qsort(3)] The qsort () function sorts an array with nmemb size size elements. The base argument is a pointer to the beginning of the array. The compar-pointed comparison function sorts the contents of the array in ascending order (the higher the value, the later). The argument of the comparison function is a pointer to the two objects being compared.

The comparison function is 1) an integer less than zero, 2) zero, 3) less than zero, depending on whether the first argument is 1) less, 2) equal, or 3) greater than the second argument. Must return one of the larger integers. When the comparison results of the two elements are equal, the order of the two is not specified in the rearranged array.

Use this qsort (3) to sort an array of ints in ascending order. If implemented in C language, it is as follows.

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>

//callback function
static int compar(const void* rhs_ptr, const void* lhs_ptr) {
  int32_t rhs = *(int32_t*)rhs_ptr;
  int32_t lhs = *(int32_t*)lhs_ptr;
  if (rhs > lhs) {
    return 1;
  } else if (rhs < lhs) {
    return -1;
  } else {
    return 0;
  }
}

int main(void) {
  int32_t array[] = {1, 5, -10, 3, 9, 8, 7, 13};

  qsort(array, sizeof(array) / sizeof(array[0]), sizeof(array[0]), compar);

  for (uint32_t i = 0; i < sizeof(array) / sizeof(array[0]); i++) {
    printf("%d\n", array[i]);
  }
  return 0;
}

This time we call the same process via dart: ffi.

Call qsort (3) from Dart

Now, I will post the implementation in Dart immediately.

//Type definition
typedef NativeCompar = Int32 Function(Pointer<Int32>, Pointer<Int32>);
typedef NativeQsort = Void Function(
    Pointer<Int32>, Uint64, Uint64, Pointer<NativeFunction<NativeCompar>>);
typedef Qsort = void Function(
    Pointer<Int32>, int, int, Pointer<NativeFunction<NativeCompar>>);

//callback function
int compar(Pointer<Int32> rhsPtr, Pointer<Int32> lhsPtr) {
  final rhs = rhsPtr.value;
  final lhs = lhsPtr.value;
  if (rhs > lhs) {
    return 1;
  } else if (rhs < lhs) {
    return -1;
  } else {
    return 0;
  }
}

List<int> qsort(final List<int> data, _compar) {
  //Convert Dart List to C array
  final dataPtr = intListToArray(data);

  // qsort(3)Get the handler for libc that defines
  final libc = DynamicLibrary.open('libc.so.6');

  //Get the address of the symbol qsort from libc and convert it to the Dart function
  final qsort =
      libc.lookup<NativeFunction<NativeQsort>>('qsort').asFunction<Qsort>();

  //Convert Dart function compar to C function pointer
  Pointer<NativeFunction<NativeCompar>> pointer =
      Pointer.fromFunction(compar, 0);

  //libc qsort(3)Called by passing an array, number of elements, element size, function pointer to
  qsort(dataPtr, data.length, sizeOf<Int32>(), pointer);

  //Convert an array of C to a List of Dart
  return arrayToIntList(dataPtr, data.length);
}

The processing flow is described as a comment.

The point when passing the Dart function as a callback function is Pointer # fromFunction .. You can use this method to convert a Dart function to a C function pointer. As for the arguments, those that are obvious will be marshalled automatically.

The code that works as the above sample Flutter app is below.

in conclusion

Other language bindings have a lot of type conversions and are inevitably long in notation, If you read it calmly, it is essentially the same as handling function pointers in C language.

Of course, when actually using it in development, error handling, resource management, prevention of subtle type mistakes, Ingenuity to reduce the number of copies, caching of library handlers and symbols, etc. There are many other things to think about, If you are not confused by type conversion, I think that you can think of it in the same way as Native development.

reference

-FFI in Flutter --There is no mention of callbacks, but it was very helpful for me to summarize the history of FFI in an easy-to-understand manner.

Recommended Posts

Call the C function with dart: ffi and call the Dart function callback
Call the API with python3.
What is the Callback function?
Call C from Python with DragonFFI
Function pointer and objdump ~ C language ~
Output the call graph with PyCallGraph
Compute the partition function with the sum-product algorithm
Code-Server x Dart with ffi x Go x Clang
RaspberryPi L Chika with Python and C #
[C language] Be careful of the combination of buffering API and non-buffering system call
Scraping the holojour and displaying it with CLI
I tried function synthesis and curry with python
Solving the Lorenz 96 model with Julia and Python
Archive and compress the entire directory with python
Describe ec2 with boto3 and retrieve the value
In Python3.8 and later, the inverse mod can be calculated with the built-in function pow.