Dart中FFI学习

news2024/9/27 5:24:05

Flutter中FFI学习

  • Dart FFI编程
    • 概述
    • NativeType(类型映射)
    • Window安装GCC
    • Dart调用C的函数
    • 数组
    • 字符串
    • 结构体

Dart FFI编程

概述

dart:ffi库可以使用Dart语言调用本地C语言API
,并读取、写入、分配和删除本地内存。FFI是指外部函数接口(Foregin function interface)。其他类似功能的术语包括本地接口和语言绑定。

在这里插入图片描述
具体关于如何将C语言指针映射为Dart中的指针类型的操作, 可以查看Dart FFI API参考文档

关于Dart语言的FFI使用, 可总结以下6个步骤:

  1. 导入ffi
  2. 为被调用的C函数创建Dart Native签名(FFI类型前面,Navtive Type)
  3. 为被调用的C函数创建Dart函数前面(与第2不对应)
  4. 加载动态库
  5. 查找C函数,并将C函数指针映射为Dart函数(即将第2、3步创建的签名映射起来)
  6. 调用函数

NativeType(类型映射)

NativeType是在Dart中表示C语言中的数据结构,它不可在Dart中实例化,只能由Native返回。

Dart FFI与C基础数据类型映射表如下:
在这里插入图片描述

Window安装GCC

window平台安装GCC

把C/C++文件编译成.dll动态库

gcc -shared -o libexample.dll libexample.c

Dart调用C的函数

//C语言函数
int calu(char *expression) {
    
    printf("开始执行方法============");
    for (size_t i = 0; i < 5; i++)
    {
        /* code */
        printf("传入的值是===========%c\n", expression[i]);
    }
   
    return 8;
}

验证代码:

import 'dart:ffi';
import 'package:ffi/ffi.dart' as ffi;

///
/// 被Dart调用的C语言原型
/// int calu(char *expression)
///
///

/// int -> int32 (在c语言中int 是占32位)
/// char *  -> Pointer<ffi.Utf8>  在C语言中char * 代表一个指针, Char 实际上int8,但是dart:ffi中没有对应的,
/// 所以我们需要使用到第三方那个库中的API, 该库中char*对应是utf8
/// ffi类型签名(native签名)把c的函数映射成native函数
typedef CaluNavtive = Int32 Function(Pointer<ffi.Utf8>);

/// Dart函数签名
/// 首先dart函数签名,应该使用dart中的签名, 如果是基础数据类型,我们可以直接使用dart中的基础数据类型
/// 但是如果是对象类型, 比如下面函数中我们就不能使用String,如果这里写String,是无法和Native的类型进行转换的
/// 像Dart中特有的非基础数据类型, 那么只能使用Navtive类型, 所以这里直接使用Pointer<ffi.Utf8>
typedef Calc = int Function(Pointer<ffi.Utf8>);

void main(List<String> arguments) {
  // 加载动态库
  var dyn = DynamicLibrary.open('./bin/libtest.dll');

  //查找C函数
  /// external F lookupFunction<T extends Function, F extends Function>(
  Calc calc = dyn.lookupFunction<CaluNavtive, Calc>('calu');

  // 首先我们方法需要的是一个pointer<ffi.ut8>类型的参数, 那么我们需要把dart类型的字符串转成这个类型传递进去
  final exp = "1234567".toNativeUtf8();

  //调用函数
  var result = calc(exp);
  print("result======$result");
}

输出结果:
在这里插入图片描述

数组

Dart与C传递数组, 只能使用堆内存, 也就是动态内存, 当我们需要再FFI中使用动态内存分配时,需要依赖一个官方开发的外部包ffi,注意与Dart内部核心包ffi进行区分
在这里插入图片描述
外部包package:ffi主要提供了动态内存分配与字符串的处理,是FFI开发中必不可少的依赖,因此不要忘记在pubspec.yaml中配置依赖

  • C语言代码:
void test_array(int *arr, int len) {
    for (size_t i = 0; i < len; i++)
    {
        /* code */
        printf("%d\n", arr[i]);
    }
    
}
  • Dart代码:
/// C语言函数
/// void test_array(int *arr, int len)

/// ffi函数类型(Navtive)
typedef testArrayNavtive = Void Function(Pointer<Int32>, Int32);

/// Dart函数
typedef testArray = void Function(Pointer<Int32>, int);

/// Dart层创建一个整形数组, 传递给C语言, 然后C语言循环打印
void test_array(List list) {
  // 需要动态分配一个内存
  Pointer<Int32> pArr = ffi.malloc.allocate(sizeOf<Int32>() * list.length);

  //循环list把内容都加入到数组中
  for (var i = 0; i < list.length; i++) {
    // elementAt是做一个便宜指针操作
    pArr.elementAt(i).value = list[i];
  }

  // 加载动态库
  var dyn = DynamicLibrary.open('./bin/libtest.dll');

  //查找C函数
  /// external F lookupFunction<T extends Function, F extends Function>(
  testArray testarrayTemp =
      dyn.lookupFunction<testArrayNavtive, testArray>('test_array');

  testarrayTemp(pArr, list.length);

  // 释放数组
  ffi.malloc.free(pArr);
}

  • 调用:
void main(List<String> arguments) {
  // 调用打印数组
  List list = [1, 2, 67, 8, 23, 88, 90, 32];
  test_array(list);
}

  • 结果:
    在这里插入图片描述

字符串

字符串的本质就是字节数组, 因此传递字符串就是传递数组。

  • C语言函数:
// 测试字符串的传递
void test_string(char *str, int len) {
    printf("便利dart传递进来的字符串======%s", str);
    for (size_t i = 0; i < len; i++)
    {
        /* code */
        printf("%d\n", str[i]);
    }
}
  • Dart函数
/// ffi函数
typedef testStringNavtive = Void Function(Pointer<Int8>, Int32);

/// Dart函数
typedef testString = void Function(Pointer<Int8>, int);
void test_string(String message) {
  // 如果传递的字符串是常见, 就不用动态分配内存, 但是如果是一个变量,那么我们需要动态分配内存
  Pointer<Int8> charPointer =
      ffi.malloc.allocate(sizeOf<Int8>() * message.length);

  charPointer = message.toNativeUtf8().cast<Int8>();

  // 加载动态库
  var dyn = DynamicLibrary.open('./bin/libtest.dll');

  //查找C函数
  /// external F lookupFunction<T extends Function, F extends Function>(
  testString tesstringTemp =
      dyn.lookupFunction<testStringNavtive, testString>('test_string');

  tesstringTemp(charPointer, message.length);

  ffi.malloc.free(charPointer);
}
  • 结果:
    在这里插入图片描述

上述Dart中传递一个字符串给C语言(char *), 我们使用了Pointer来传递的, 但是我看了很多其他的资料, 都是Dart中的Pointer<ffi.utf8>就对饮C语言的char *,但是当我使用时,报如下错误:
在这里插入图片描述
不知道如何解决,后续待研究

结构体

目前,FFI中,结构体不能直接 作为函数传递,但可以作为返回值。

  • C语言中代码:
#include <stdio.h>
#include <stdlib.h>

// 定义一个结构体
typedef struct struct_ts
{
    /* data */
    char * name;
    int age;
} Person;

// 创建一个Person的对象
Person create_person(char *name, int age) {
    Person p = {};
    p.name = name;
    p.age = age;

    return p;
}

// 创建一个Person的指针对象
Person * get_person(char *name, int age) {
    Person *p = (Person *)malloc(sizeof(Person));
    p->name = name;
    p->age = age;

    return p;
}
  • Dart中创建和C语言中对应的结构体代码:
class Person extends Struct {
  external Pointer<ffi.Utf8> name;

  @Int32()
  external int age;
}
  • Dart中的结构体只能用于和C语言中结构体映射,FFI 会自动生成 setter/getter 方法,用于中访问内存中 Native 结构体的字段值;
  • 如果字段的数据类型不是 NativeType,则需要使用 NativeType 进行修饰(如:int, double);否则则不需要(如:Pointer类型则不需要);
  • Struct 中的所有字段都必须使用 external 关键词进行修饰;

注意:不能实例化该 Dart 类,仅用于指向 Native 内存(即结构体是由C分配的,Dart只是持有一个引用而已),如果要实例化该类,则应该由 C 语言提供对应的创建/销毁方法,由Dart调用。

  • Dart调用代码:
/// Person create_person(char *name, int age)
/// Person * get_person(char *name, int age)
typedef createPersonNavtive = Person Function(Pointer<ffi.Utf8>, Int32);
typedef createPerson = Person Function(Pointer<ffi.Utf8>, int);

typedef getPersonNavtive = Pointer<Person> Function(Pointer<ffi.Utf8>, Int32);
typedef getPerson = Pointer<Person> Function(Pointer<ffi.Utf8>, int);

void test_struct() {
// 加载动态库
  var dyn = DynamicLibrary.open('./bin/libstruct_ts.dll');

  //查找C函数
  /// external F lookupFunction<T extends Function, F extends Function>(
  final create_person =
      dyn.lookupFunction<createPersonNavtive, createPerson>('create_person');
  var get_person =
      dyn.lookupFunction<getPersonNavtive, getPerson>('get_person');

  // 创建一个对象
  var person = create_person('张三'.toNativeUtf8(), 28);
  print('${person.name.toDartString()}-----${person.age}');

  //创建一个person的指针对象
  var p = get_person('李四'.toNativeUtf8(), 18);

  print("${p.ref.name.toDartString()} -----${p.ref.age}");

  // 修改指针对象

  p.ref.name = '王五'.toNativeUtf8();
  p.ref.age = 20;
  print("修改指针对象后====${p.ref.name.toDartString()} -----${p.ref.age}");

// 这里需要释放指针对象,这里建议C语言应该还需要提供一个释放的方法,供Dart调用。而不是直接在dart中直接释放
  ffi.malloc.free(p);
}
  • 结果:
    在这里插入图片描述
  • create_person方法返回了一个Dart的Person对象,这个是传值而非传引用,所以这个perosn对象相当于C语言中person对象的副本, 是分开的, 修改不会相互影响的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2168970.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

JS设计模式之组合模式:打造灵活高效的对象层次结构

引言 当我们构建复杂的应用程序时&#xff0c;经常会遇到处理对象层次结构的情况。这些层次结构通常是树形结构&#xff0c;由组合节点和叶子节点组成。在这样的情况下&#xff0c;JavaScript 设计模式之一的组合模式就能派上用场。 组合模式是一种结构型设计模式&#xff0c…

Gitlab学习(006 gitlab操作)

尚硅谷2024最新Git企业实战教程&#xff0c;全方位学习git与gitlab 总时长 5:42:00 共40P 此文章包含第21p-第24p的内容 文章目录 git登录修改root密码 设置修改语言取消相对时间勾选 团队管理创建用户创建一个管理员登录管理员账号创建一个普通用户登录普通用户账号 群组管理…

工业交换机一键重启的好处

在当今高度自动化和智能化的工业环境中&#xff0c;工业交换机作为网络系统中至关重要的一环&#xff0c;其稳定性和可靠性直接影响到整个生产过程的顺利进行。为了更好地维护这些设备的健康运行&#xff0c;一键重启功能应运而生&#xff0c;并呈现出诸多显著的好处。 首先&am…

助力降本增效,ByteHouse打造新一代云原生数据仓库

随着数据量的爆炸式增长、企业上云速度加快以及数据实时性需求加强&#xff0c;云原生数仓市场迎来了快速发展机遇。 据 IDC、Gartner 研究机构数据显示&#xff0c;到 2025 年&#xff0c;企业 50% 数据预计为云存储&#xff0c;75% 数据库都将运行在云上&#xff0c;全球数据…

Swagger配置且添加小锁(asp.net)(笔记)

此博客是基于 asp.net core web api(.net core3.1)框架进行操作的。 一、安装Swagger包 在 NuGet程序包管理中安装下面的两个包&#xff1a; swagger包&#xff1a;Swashbuckle.AspNetCore swagger包过滤器&#xff1a;Swashbuckle.AspNetCore.Filters 二、swagger注册 在…

数据结构——初始树和二叉树

线性结构是一对一的关系&#xff0c;意思就是只有唯一的前驱和唯一的后继&#xff1b; 非线性结构&#xff0c;如树形结构&#xff0c;它可以有多个后继&#xff0c;但只有一个前驱&#xff1b;图形结构&#xff0c;它可以有多个前驱&#xff0c;也可以有多个后继。 树的定义…

进阶:反转二叉树的奇数层

目录标题 题目描述示例解题思路代码实现详细步骤解释复杂度分析 题目描述 给定一棵完美二叉树的根节点 root&#xff0c;请反转这棵树中每个奇数层的节点值。完美二叉树是指所有叶子节点都在同一层&#xff0c;并且每个非叶子节点都有两个子节点。 示例 示例 1&#xff1a; …

Harmony商城项目

目录&#xff1a; 1、启动项目看效果图2、代码分析 1、启动项目看效果图 2、代码分析 import CommonConstants from ../constants/CommonConstants; import WomanPage from ./components/WomanPage import ManPage from ./components/ManPage import HomePage from ./component…

Teams集成-会议侧边栏应用开发-实时转写

Teams虽然提供了转写的接口&#xff0c;但是不是实时的&#xff0c;即便使用订阅事件也不是实时的&#xff0c;为了达到实时转写的效果&#xff0c;使用recall.ai的转录和assembly_ai的转写实现。 前提&#xff1a;除Teams会议侧边栏应用开发-会议转写-CSDN博客的基本要求外&a…

实战教程!Zabbix 监控 Spark 中间件配置教程

本文将介绍以JMX方式监控Spark中间件。JMX具有跨平台、灵活性强、监控能力强、易于集成与扩展、图形化界面支持以及安全性与可配置性等多方面的优势&#xff0c;是监控Spark等复杂Java应用程序的重要工具之一。 Apache Spark 是一个开源的大数据处理框架&#xff0c;它提供了快…

【深度学习】ubuntu系统下docker部署cvat的自动标注功能(yolov8 segmentation)

cvat部署自动标注教程 前言step1. 拷贝yolov8项目step2. 创建yolov8的本地镜像step3. 在cvat中构建我们的工作空间 前言 安装docker和cvat的流程我这里就不赘述了&#xff0c;这样的教程还是挺多的&#xff0c;但是对于使用docker在cvat上部署自动标注算法的整个详细流程&#…

【MySQL】MVCC及其实现原理

目录 1. 概念介绍 什么是MVCC 什么是当前读和快照读 MVCC的好处 2. MVCC实现原理 隐藏字段 Read View undo-log 数据可见性算法 3. RC和RR隔离级别下MVCC的差异 4. MVCC&#xff0b;Next-key-Lock 防止幻读 1. 概念介绍 什么是MVCC Multi-Version Concurrency Cont…

通信工程学习:什么是FDD频分双工

FDD:频分双工 FDD(频分双工,Frequency Division Duplexing)是一种无线通信技术,它通过将频谱划分为上行和下行两个不重叠的频段来实现同时双向通信。以下是FDD频分双工的详细解释: 一、定义与原理 定义: FDD是一种无线通信系统的工作模式,其中上行链路(从移动…

以Flask为基础的虾皮Shopee“曲线滑块验证码”识别系统部署

以Flask为基础的虾皮Shopee“曲线滑块验证码”识别系统部署 一、验证码类型二、简介三、Flask应用 一、验证码类型 验证码类型&#xff1a;此类验证码存在两个难点&#xff0c;一是有右侧有两个凹槽&#xff0c;二是滑块的运动轨迹不是直线的&#xff0c;而是沿着曲线走的&…

您的业​​务端点是否完全安全?

根据 2023 年数据泄露调查报告&#xff0c;52% 的数据泄露涉及凭证泄露。这令人担忧&#xff0c;不是吗&#xff1f; 在当今的数字世界中&#xff0c;企业严重依赖技术&#xff0c;保护您的设备&#xff08;端点&#xff09;至关重要。这些设备&#xff08;包括计算机、笔记本…

MySQL从入门到精通 - 基础篇

一、MySQL概述 1. 数据库相关概念 二、SQL &#xff08;1&#xff09;SQL通用语法 &#xff08;2&#xff09;SQL分类 &#xff08;3&#xff09;数据定义语言DDL 数据库操作 表操作 数据类型 1. 数值类型 2. 字符串类型 二进制数据&#xff1a;以二进制格式&#xff08;0和…

uniapp 知识点

自定义导航 在page.json navigationstyle":"custom"navigateTo传参 页面传参只能onLoad(option)里面拿 px和upx的关系 在750设计图中&#xff0c;1px1upx 路由 navigateBack返回上一页 重定向 其实就是把当前页面干掉了 公共组件和页面共同点 computed,watc…

基于微信小程序的智能汽车充电站系设计与实现(源码+定制+文档)

博主介绍&#xff1a; ✌我是阿龙&#xff0c;一名专注于Java技术领域的程序员&#xff0c;全网拥有10W粉丝。作为CSDN特邀作者、博客专家、新星计划导师&#xff0c;我在计算机毕业设计开发方面积累了丰富的经验。同时&#xff0c;我也是掘金、华为云、阿里云、InfoQ等平台…

Spring Boot技术:构建高效网上购物平台

第3章 系统分析 3.1 可行性分析 在系统开发之初要进行系统可行分析&#xff0c;这样做的目的就是使用最小成本解决最大问题&#xff0c;一旦程序开发满足用户需要&#xff0c;带来的好处也是很多的。下面我们将从技术上、操作上、经济上等方面来考虑这个系统到底值不值得开发。…

【Vue】Vue3 的初始化过程

核心流程是patch&#xff0c;然后Patch有一个分支&#xff0c;分别处理组件和浏览器原生标签。分别对应processElement和processComponent&#xff0c;从上到下插入&#xff0c;知道处理完成&#xff0c;才把顶层div插入到浏览器。“一次性渲染&#xff0c;而不是一个个一个渲染…