这是我参与11月更文挑战的第3天,活动详情查看:2021最后一次更文挑战
我们知道Dart语言与C语言基础数据类型是不一样的,如何在双方互调的时候无缝交换数据那么一定就涉及到双方数据结构的映射。上一篇文章提到了NativeType
,它的作用是提供一系列Dart与C的映射关系。
Dart FFI与C基础数据类型映射表
Dart 中定义的NativeType | C语言中的类型 | 说明 |
---|---|---|
Opaque | opaque | 不暴露其成员类型,一般用于表示C++中的类 |
Int8 | int8_t 或 char | 有符号8位整数 |
Int16 | int16_t 或 short | 有符号16位整数 |
Int32 | int32_t 或 int | 有符号32位整数 |
Int64 | int64_t 或 long long | 有符号64位整数 |
Uint8 | uint8_t 或 unsigned char | 无符号8位整数 |
Uint16 | uint16_t 或 unsigned short | 无符号16位整数 |
Uint32 | int32_t 或 unsigned int | 无符号32位整数 |
Uint64 | uint64_t 或 unsigned long long | 无符号64位整数 |
IntPtr | int* | 整数类型指针 |
Float | float | 单精度浮点类型 |
Double | double | 双精度浮点类型 |
Void | void | void类型 |
Handle | Dart_Handle Dart | 句柄在C中的表示形式 |
NativeFunction | 函数 | 函数类型 |
Struct | struct | 结构体类型 |
Union | union | 共同体类型 |
Pointer | * | 指针类型 |
nullptr | NULL | 空指针 |
dynamic | Dart_CObject | Dart对象在C中的表现形式 |
示例
sample.c
#include <stdint.h>
// 基础数据类型
int8_t int8 = -108;
int16_t int16 = -16;
int32_t int32 = -32;
int64_t int64 = -64;
uint8_t uint8 = 208;
uint16_t uint16 = 16;
uint32_t uint32 = 32;
uint64_t uint64 = 64;
float float32 = 0.32;
double double64 = 0.64;
main.dart
late final ffi.Pointer<ffi.Int8> _int8 = _lookup<ffi.Int8>('int8');
int get int8 => _int8.value;
set int8(int value) => _int8.value = value;
late final ffi.Pointer<ffi.Int16> _int16 = _lookup<ffi.Int16>('int16');
int get int16 => _int16.value;
set int16(int value) => _int16.value = value;
late final ffi.Pointer<ffi.Int32> _int32 = _lookup<ffi.Int32>('int32');
int get int32 => _int32.value;
set int32(int value) => _int32.value = value;
late final ffi.Pointer<ffi.Int64> _int64 = _lookup<ffi.Int64>('int64');
int get int64 => _int64.value;
set int64(int value) => _int64.value = value;
late final ffi.Pointer<ffi.Uint8> _uint8 = _lookup<ffi.Uint8>('uint8');
int get uint8 => _uint8.value;
set uint8(int value) => _uint8.value = value;
late final ffi.Pointer<ffi.Uint16> _uint16 = _lookup<ffi.Uint16>('uint16');
int get uint16 => _uint16.value;
set uint16(int value) => _uint16.value = value;
late final ffi.Pointer<ffi.Uint32> _uint32 = _lookup<ffi.Uint32>('uint32');
int get uint32 => _uint32.value;
set uint32(int value) => _uint32.value = value;
late final ffi.Pointer<ffi.Uint64> _uint64 = _lookup<ffi.Uint64>('uint64');
int get uint64 => _uint64.value;
set uint64(int value) => _uint64.value = value;
late final ffi.Pointer<ffi.Float> _float32 = _lookup<ffi.Float>('float32');
double get float32 => _float32.value;
set float32(double value) => _float32.value = value;
late final ffi.Pointer<ffi.Double> _double64 =
_lookup<ffi.Double>('double64');
double get double64 => _double64.value;
set double64(double value) => _double64.value = value;
late final ffi.Pointer<ffi.Pointer<ffi.Int8>> _str1 =
_lookup<ffi.Pointer<ffi.Int8>>('str1');
ffi.Pointer<ffi.Int8> get str1 => _str1.value;
set str1(ffi.Pointer<ffi.Int8> value) => _str1.value = value;
print('\n*************** 1. 基础数据类型 **************\n');
print("int8=${nativeLibrary.int8}");
print("int16=${nativeLibrary.int16}");
print("int32=${nativeLibrary.int32}");
print("int64=${nativeLibrary.int64}");
print("uint8=${nativeLibrary.uint8}");
print("uint16=${nativeLibrary.uint16}");
print("uint32=${nativeLibrary.uint32}");
print("uint64=${nativeLibrary.uint64}");
print("float32=${nativeLibrary.float32}");
print("double64=${nativeLibrary.double64}");
print("string=${nativeLibrary.str1.cast<Utf8>().toDartString()}");
nativeLibrary.int8++;
nativeLibrary.int16++;
nativeLibrary.int32++;
nativeLibrary.int64++;
nativeLibrary.uint8++;
nativeLibrary.uint16++;
nativeLibrary.uint32++;
nativeLibrary.uint64++;
nativeLibrary.float32++;
nativeLibrary.double64++;
nativeLibrary.str1 = "修改一下".toNativeUtf8().cast();
print("修改后:");
print("int8=${nativeLibrary.int8}");
print("int16=${nativeLibrary.int16}");
print("int32=${nativeLibrary.int32}");
print("int64=${nativeLibrary.int64}");
print("uint8=${nativeLibrary.uint8}");
print("uint16=${nativeLibrary.uint16}");
print("uint32=${nativeLibrary.uint32}");
print("uint64=${nativeLibrary.uint64}");
print("float32=${nativeLibrary.float32}");
print("double64=${nativeLibrary.double64}");
print("string=${nativeLibrary.str1.cast<Utf8>().toDartString()}");
结果输出
*************** 1. 基础数据类型 **************
int8=-108
int16=-16
int32=-32
int64=-64
uint8=208
uint16=16
uint32=32
uint64=64
float32=0.11999999731779099
double64=0.64
string=Dart FFI SAMPLE
修改后:
int8=-107
int16=-15
int32=-31
int64=-63
uint8=209
uint16=17
uint32=33
uint64=65
float32=1.1200000047683716
double64=1.6400000000000001
string=修改一下
由于我想让程序更有封装性,我对每个函数添加了get
,set
方法。 上面的示例基本上只展示了数字类型转换,基本上还算简单,按照上表数据结构对应转换就不会出错。
细心的朋友可能已经发现了,上面的字符串是比较特殊,需要一层转换。C语言中的char*
需要用ffi.Pointer<ffi.Int8>
去接收,我们可以拿到这个指针,然后转换成Utf8
格式,需要说明的是Utf8
是ffi
库下的一个类型(ffi
包含dart sdk提供的类与方法和ffi库的方法)。
Utf8
是一个UTF-8
数据的列表(Array),我们拿到Utf8
的指针后,可以通过它提供的方法toDartString
来将其转换成Dart的String类型。
late final ffi.Pointer<ffi.Pointer<ffi.Int8>> _str1 =
_lookup<ffi.Pointer<ffi.Int8>>('str1');
String value = _str1.value.cast<Utf8>().toDartString()
我们还可以通过 '这是Dart字符串'.toNativeUtf8().cast<ffi.Int8>()
将Dart字符串转换成C的char*
。
上面代码我都放到了Github上,Github仓库