失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > 06 Flutter FFI 类

06 Flutter FFI 类

时间:2023-03-23 10:51:01

相关推荐

06 Flutter FFI 类

Flutter FFI 学习笔记系列

《Flutter FFI 最简示例》《Flutter FFI 基础数据类型》《Flutter FFI 函数》《Flutter FFI 字符串》《Flutter FFI 结构体》《Flutter FFI 类》《Flutter FFI 数组》《Flutter FFI 内存管理》《Flutter FFI Dart Native API》

在前面的章节中,介绍结构体在 C 和 Dart 中的相互调用。接下来将介绍类在 C 和 Dart 中的相互调用。

由于 Dart 只能调用 C 风格的符号,并不能调用 C++ 风格的符号,而 class 是 C++ 才有的,因此想要在 Dart 调用 C++ 的类,需要做一些额外的工作才行。

1、基本思路

基本思路如下:

类的映射:写一个类,继承Opaque,用于表示 C++ 中的类;成员变量的映射:通过全局方法实现;成员方法的映射:通过全局方法实现;

2、示例

下面的示例中,演示了如何将 C++ 中的类映射给 Dart 使用。

首先,在 C/C++ 中定义类,然后定义一些全局函数,如下:

#include <malloc.h>#include <cstring>#define DART_API extern "C" __attribute__((visibility("default"))) __attribute__((used))//定义一个类表示怪物class Monster {public:char *name{}; //名称int32_t hp = 255; //血量int32_t atk = 10; //攻击力public://攻击目标void attack(Monster *target) const {target->hp -= atk;}};//创建一个怪物DART_API Monster *createMonster(char *name, int32_t hp, int32_t atk) {auto *monster = (Monster *) malloc(sizeof(Monster));monster->hp = hp;monster->atk = atk;monster->name = name;return monster;}DART_API const char *Monster_getName(Monster *monster) {return monster->name;}DART_API int32_t Monster_getHP(Monster *monster) {return monster->hp;}DART_API void Monster_setHP(Monster *monster, int32_t hp) {monster->hp = hp;}DART_API int32_t Monster_getATK(Monster *monster) {return monster->atk;}DART_API void Monster_setATK(Monster *monster, int32_t atk) {monster->atk = atk;}DART_API void Monster_attack(Monster *monster, Monster *target) {monster->attack(target);}

代码说明

上面的 C 代码中,定义了一个名为Monster的类,包括name,hp,atk三个属性,和一个attack()方法;对于 类中的成员变量,需要定义对应的 C 风格的函数,如:Monster_getNameMonster_getHPMonster_setHP等;对应 类中的成员方法,同样需要定义对应的 C 风格的函数,如:Monster_attack()

接着,在 Dart 代码定义相对应的函数类型 和Opaque类型 如下:

//-------下面是函数定义-------typedef Native_createMonster = Pointer<Monster> Function(Pointer<Utf8> name, Int32 hp, Int32 atk);typedef FFI_createMonster = Pointer<Monster> Function(Pointer<Utf8> name, int hp, int atk);typedef Native_getName = Pointer<Utf8> Function(Pointer<Monster> monster);typedef FFI_getName = Pointer<Utf8> Function(Pointer<Monster> monster);typedef Native_setHP = Void Function(Pointer<Monster> monster, Int32 hp);typedef FFI_setHP = void Function(Pointer<Monster> monster, int hp);typedef Native_getHP = Int32 Function(Pointer<Monster> monster);typedef FFI_getHP = int Function(Pointer<Monster> monster);typedef Native_setATK = Void Function(Pointer<Monster> monster, Int32 atk);typedef FFI_setATK = void Function(Pointer<Monster> monster, int atk);typedef Native_getATK = Int32 Function(Pointer<Monster> monster);typedef FFI_getATK = int Function(Pointer<Monster> monster);typedef Native_attack = Int32 Function(Pointer<Monster> monster, Pointer<Monster> target);typedef FFI_attack = int Function(Pointer<Monster> monster, Pointer<Monster> target);// ----------- 下面是类的定义 -------------//一个Monster类,对应于C中的Monsterclass Monster extends Opaque {static FFI_createMonster? createFunc;static FFI_getName? nameFunc;static FFI_setHP? setHPFunc;static FFI_getHP? getHPFunc;static FFI_setATK? setATKFunc;static FFI_getATK? getATKFunc;static FFI_attack? attackFunc;static init(DynamicLibrary dl) {createFunc = dl.lookupFunction<Native_createMonster, FFI_createMonster>("createMonster");nameFunc = dl.lookupFunction<Native_getName, FFI_getName>("Monster_getName");setHPFunc = dl.lookupFunction<Native_setHP, FFI_setHP>("Monster_setHP");getHPFunc = dl.lookupFunction<Native_getHP, FFI_getHP>("Monster_getHP");setATKFunc = dl.lookupFunction<Native_setATK, FFI_setATK>("Monster_setATK");getATKFunc = dl.lookupFunction<Native_getATK, FFI_getATK>("Monster_getATK");attackFunc = dl.lookupFunction<Native_attack, FFI_attack>("Monster_attack");}//保存由C返回的实例的指针late Pointer<Monster> _thiz;late Pointer<Utf8> nativeNameValue;Monster(String name, int hp, int atk) {nativeNameValue = name.toNativeUtf8();//创建Monster实例,并保存实例指针_thiz = createFunc!(nativeNameValue, hp, atk); }String get name => nameFunc!(_thiz).toDartString();int get hp => getHPFunc!(_thiz);set hp(value) => setHPFunc!(_thiz, value);int get atk => getATKFunc!(_thiz);set atk(value) => setATKFunc!(_thiz, value);void attack(Monster target) {attackFunc!(_thiz, target._thiz);}void free() {calloc.free(nativeNameValue);calloc.free(_thiz);}String toDebugString() {return "{name=$name, hp=$hp, atk=$atk}";}}

说明

在 Dart 中,我们定义了一个Monster类,继承于Opaque,Opaque的意思是不透明,即其成员是不暴露的;init()方法是一个初始化方法,可以提前把我们需要用到的函数提前映射好,方便后续使用;在Monster类中,我们通过定义setter/getter来表示成员变量。它们的实现是调用 C 中的全局方法,把实例(Monster指针)传给这些全局方法,这样这些方法就知道对哪个实例调用相应的方法了;在Monster类中,我们定义了与 C 一致的成员方法。它们的实现也是调用 C 中的全局方法;最后,在Monster实例不使用的时候,可以调用free()方法进行内存释放。

最后,我们就可以在 Dart 中使用该Monster类了,使用方法如下:

//加载符号DynamicLibrary nativeApi = Platform.isAndroid? DynamicLibrary.open("libnative_ffi.so"): DynamicLibrary.process();//初始化相关函数Monster.init(nativeApi);//创建两个MonsterMonster alice = Monster("Alice", 255, 10);Monster nero = Monster("Nero", 200, 12);print("before fighting, ${alice.toDebugString()}, ${nero.toDebugString()}");//让两个Monster相互攻击for (int i = 0; i < 10; i++) {if (i.isEven) {alice.attack(nero);print("Alice =>>>>> Nero, ${nero.toDebugString()}");} else {nero.attack(alice);print("Alice <<<<<= Nero, ${alice.toDebugString()}");}}print("after fighting, ${alice.toDebugString()}, ${nero.toDebugString()}");//最后不要忘记释放内存alice.free();nero.free();

代码说明

Dart 调用createMonster创建了Monster实例之后,需要将实例的指针Pointer<Monster>保存起来,以便后续使用;调用Monster_setHPMonster_attack等方法时,需要传递Pointer<Monster>指针;最后,由于是在 C 分配的内存,因此 Dart 需要在不使用的时候调用calloc.free()释放内存,避免内存泄漏;

3、扩展知识

纯手工编写上面的一个类可能不算什么,但是如果有非常多的 C/C++ 代码需要映射到 Dart 使用时,可能就需要使用一些工具来自动生成代码了。

官方推荐的一个代码自动生成工具:ffigen,地址:https://pub.dev/packages/ffigen.

4、总结

上面介绍了如何把 C++ 中的类映射给 Dart 使用。后面的章节中,将会介绍数组、内存管理等知识,欢迎关注。

如果觉得《06 Flutter FFI 类》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。