Dart FFI 探索(三)| 新思路使用 FFI

在 7 月 28 日 ,dart在pub上發佈了ffigen,用於解析c/cpp頭文件,來自動生成能夠對應調用的dart函數。java

前言

因爲 dart 對於底層的 c 語言的有些函數還不支持,好比我遇到的,對文件描述符的操做,還有諸多須要用ffi的地方,例如視頻的解碼等,拿在 Flutter 上使用 dart 的狀況,對於視頻解碼類,咱們通常是經過原生通道,讓java/kotlinswift去調c/cpp,我以前的文章也自實現了dart ffi調用ffmpeg解碼來實現視屏播放。linux

而使用原生通道這個思路來調一些c/cpp其實就畫蛇添足了,並且增長了很多的平臺代碼,不方便移植與適配,除非目前的視頻播放器類,還不能徹底離開平臺的一些支持,因此其餘能脫離平臺的,咱們無需再經過平臺插件。android

在 Flutter 官方放出 ffi 在Flutter上的使用,我就比較關注,並藉此完成了一個殘廢的終端模擬器 😄 。ios

原始 FFI 思路

  • 將已有的 c/cpp 經過交叉編譯各個平臺,windowsdllunix系統的so庫,而後經過DynamicLibraryopen函數去打開這個庫,來得到其中函數的動態連接。隨即按套路編寫dart調用ffi的代碼便可。

ffgen 提供思路

每一個平臺都有一些已經有的so庫,若是咱們的 c/cpp 語言實現的都是一些全部unix設備或者windows設備底層自帶so、dll中的函數,咱們不妨將裏面每個函數都轉換成dart端能夠對應調用的函數,也就是生成一個dart可用的 ".h" 頭,固然這個擴展名仍是 ".dart" 。macos

這樣一來,咱們無需再爲了 windows、macos、linux、android、ios 這5個平臺分別交叉編譯一份動態庫,咱們只須要寫一份通用的dart代碼便可。ubuntu

更多的是在linux、macos、android上。 這種思路一些開發者也早就想到過,包括 Flutter 前不久放出了一個在 Flutter平臺調用一個win32 api 的例子swift

有興趣看這個包---> win32windows

一個使用FFI包裝一些最多見的Win32 API調用的程序包,使它們可使用Dart代碼進行訪問,而無需C編譯器或Windows SDK。api

使用

環境

ubuntu/linux

安裝 libclangdev - sudo apt-get install libclang-dev.數組

Windows

安裝 Visual Studio with C++ development support.

安裝 LLVM or winget install -e --id LLVM.LLVM.

MacOS

安裝 Xcode。

安裝 LLVM - brew install llvm

新建文件,並編寫 pubspec.yaml

# Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
# for details. All rights reserved. Use of this source code is governed by a
# BSD-style license that can be found in the LICENSE file.

name: dart_ffi

environment:
  sdk: '>=2.8.1 <3.0.0'

dev_dependencies:
  ffigen: ^0.1.5
  ffi: ^0.1.3
  
# ffigen:
# name: Dirent
# description: dirent.h 頭文件在dart的移植.
# output: 'dirent.dart'
# headers:
# entry-points:
# - '/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/dirent.h'

ffigen:
  name: Stdio
  description: stdio.h 頭文件在dart的移植.
  output: 'stdio.dart'
  headers:
    entry-points:
      - '/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/stdio.h'
複製代碼

而後執行pub get

我想經過一個項目來解析多個頭文件,採用了比較 low 的註釋來實現,須要解析哪一個,就單獨放開哪部分的註釋。

更多請移步ffigen

轉換

執行pub run ffigen 好像不太順利,其中的警告就是,c 語言的有些變量是 "_" 開頭的,被轉換後在 dart 會限制爲私有。

還有一些 error 直接被我註釋掉了,是一些不會用到的函數或變量。

使用

我新建了一個 test.dart 文件以下

import 'dart:convert';
import 'dart:ffi';
import 'package:ffi/ffi.dart';
import 'stdio.dart';

void main() {
  Stdio stdio = Stdio(DynamicLibrary.process());
  String str = '123\n';
  List<int> list = utf8.encode(str);
  print(list);
  Pointer<Int8> data = allocate<Int8>(count: 4);
  for (int i = 0; i < list.length; i++) {
    data[i] = list[i];
  }
  stdio.printf(data);
  str = '456\n';
  data = Utf8.toUtf8(str).cast<Int8>();
  stdio.printf(data);
  free(data);
}
複製代碼

printf函數的參數爲Pointer<Int8>,能夠理解成一維數組,跟c語言的charint[]都是差很少的,而c語言的chardart中對應類型是Pointer<Utf8>,其實都是8位的數組,因此我使用了兩種傳參方式,第二種要更方便。

還有須要注意,若是你的程序不是立馬就結束的,好比一個 Flutter app ,請在任何使用完allocate函數後對它及時的釋放,例如Utf8.toUtf8這個函數,內部也使用的allocate函數。

運行結果

這樣就在dart完成了c語言printf函數的調用。

其實按照c語言的預編譯思想,被調用到函數的實如今編譯期間會copy到當前文件,因此你能夠根據本身使用到的去轉換後的dart刪減來節省編譯後的內存。

結語

  • 因爲我瞭解到這個,也是經過一位也在使用 Flutter 開發終端模擬器的大佬那兒得知,而他是打算重構完整的終端模擬器,重構全部的終端序列。
  • 但對於這種忽然發佈的 dart 包,大部分人是不會知道它的,除非被正式列入了一些官網,可是的確頗有用,因此,推薦給你們,這種方案,我的感受真的很不錯。
  • 放假開始兼職,學習時間真的少太多,不過實戰經驗的確是增長了。
相關文章
相關標籤/搜索