Reputation: 11590
The official flutter tutorial on C/C++ interop through ffi only touches on calling a C++ function and getting a single return value.
What if I have a data buffer created on C/C++ side, but want to deliver to dart/flutter-side to show?
With @MilesBudnek 's tip, I'm testing Dart's FFI by trying to have safe memory deallocation from Dart to C/C++. The test reuses the official struct sample .
I could get the Array as a dart Pointer
, but it's unclear to me how to iterate the array as a collection easily.
I'm implementing a Dart-side C array binding like this:
In struct.h
struct Array
{
int* array;
int len;
};
and a pair of simple allocation/deallocation test functions:
struct Array* get_array();
int del_array(struct Array* arr);
Then on Dart side in structs.dart
:
typedef get_array_func = Pointer<Array> Function();
typedef del_array_func = void Function(int arrAddress);
...
final getArrayPointer = dylib.lookup<NativeFunction<get_array_func>>('get_array');
final getArray = getArrayPointer.asFunction<get_array_func>();
final arrayPointer = getArray();
final array = arrayPointer.ref.array;
print('array.array: $array');
This gives me the print out
array.array: Pointer<Int32>: address=0x7fb0a5900000
Can I convert the array pointer to a List
easily? Something like:
final array = arrayPointer.ref.array.toList();
array.forEach(index, elem) => print("array[$idx]: $elem");
It's unclear to me how to retrieve this kind of vector data from C/C++ by dart/flutter.
More importantly, how to push data from C++ side from various threads? If there is no builtin support, off the top of my head I'd need to implement some communication schemes.
I could do network through TCP sockets. But I'm reluctant to go there if there are easier solutions.
Write data to file with C/C++, and let dart/flutter poll on the file and stream data over. This is not realtime friendly.
So, are there better options?
Upvotes: 7
Views: 7186
Reputation: 4109
asTypedList
will only work with pointers that relate to TypedData
.
there are other cases where, for example, you want to convert an Pointer<UnsignedChar>
to a Uint8List
, in this case you can:
Pointer<UnsignedChar
to a Pointer<Uint8>
and then use asTypedList
. In this case you have to make sure the pointer is not freed while the Uint8List
is still referenced.extension UnsignedCharPointerExtension on Pointer<UnsignedChar> {
Uint8List? toUint8List(int length) {
if (this == nullptr) {
return null;
}
return cast<Uint8>().asTypedList(length);
}
}
Uint8List
extension UnsignedCharPointerExtension on Pointer<UnsignedChar> {
Uint8List? toUint8List(int length) {
if (this == nullptr) {
return null;
}
final Uint8List list = Uint8List(length);
for (int i = 0; i < length; i++) {
list[i] = this[i];
}
return list;
}
}
Upvotes: 1
Reputation: 11590
Solved it.
According to this issue, the API asTypedList is the way to go.
Here is the code that works for me
final getArrayPointer = dylib.lookup<NativeFunction<get_array_func>>('get_array');
final getArray = getArrayPointer.asFunction<get_array_func>();
final arrayPointer = getArray();
final arr = arrayPointer.ref.arr;
print('array.array: $arr');
final arrReal = arr.asTypedList(10);
final arrType = arrReal.runtimeType;
print('arrReal: $arrReal, $arrType');
arrReal.forEach((elem) => print("array: $elem"));
This gives me:
array.array: Pointer<Int32>: address=0x7f9eebb02870
arrReal: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], Int32List
array: 0
array: 1
array: 2
array: 3
array: 4
array: 5
array: 6
array: 7
array: 8
array: 9
Upvotes: 11