🐷

dart:ffiを使ってDartでC++を動かす。🤖

2024/03/13に公開

なぜやるか

OpenCVを利用するFlutterアプリを実装するために、技術調査としてDartでC++を動かしてみました。

ゴール

C++で書いた関数をDartから呼び出します。ios/androidどちらもやりますよ。

方法

dart:ffiを使用します。
https://dart.dev/interop/c-interop

dart:ffiってなに?

「ffi」はforeign function interface (外部関数インターフェース)の略です。

  • C/C++でネイティブ言語のライブラリを使用できる
  • メモリ上の特定アドレス(ポインタ)に直接書き込み、読み込み、割り当て機能がある
    こういった機能を持つプラグインです。

実装

ios→androidの順で実装します。

iOS編

①C++ファイルの準備

まずはiosフォルダ内にios>Classes>native_add.cppのような構成でC++ファイルを準備します。

native_add.cpp
// 🔽 dartから呼び出したいC++関数で、引数を足して返す単純な関数です。
// 先に言っておくと、iosフォルダの中のファイルをandroidでも参照する構成にしていきます。
#include <stdint.h>
   
extern "C" __attribute__((visibility("default"))) __attribute__((used))
int32_t native_add(int32_t x, int32_t y) {
return x + y;
}

②Dartから呼び出す処理の準備

次に、libフォルダ内でdartファイルを作成します。
例ではlib>native_add.dartのように配置していきます。

native_add.dart
import 'dart:ffi'; // For FFI
import 'dart:io'; // For Platform.isX
   
// ネイティブライブラリをロードするためのDynamicLibraryオブジェクトを作成
final DynamicLibrary nativeAddLib = Platform.isAndroid
      ? DynamicLibrary.open('libnative_add.so')
      : DynamicLibrary.process();
   
// DynamicLibraryオブジェクトから検索された関数をDartの関数型に変換
final int Function(int x, int y) nativeAdd = nativeAddLib
      .lookup<NativeFunction<Int32 Function(Int32, Int32)>>('native_add')
      .asFunction();

③Xcode上からシンボル(関数や変数)のビルド設定を追加

①で作成したC++ファイルをXcodeからCompile Sourcesに設定します。ios/Classesフォルダの中にあるので、外部フォルダから参照させましょう。

native_add.dartで変換した関数を呼び出す

シンプル✅

main.dart
Text('1 + 2 == ${nativeAdd(1, 2)}')

Android編

①CMakeListsの作成

AndroidではCMakeLists.txtに共有ライブラリ(先程作ったios/Classes/native_add.cpp)をビルドするための設定を記述していきます。
フォルダ構成はandroid/app/CMakeLists.txtとします。

CMakeLists.txt
cmake_minimum_required(VERSION 3.4.1)
    
add_library(native_add 
              SHARED 
              ../../ios/Classes/native_add.cpp)

②build.gradleでCMakeListsのビルド設定

build.gradle
android {
        <..上記省略>
        sourceSets {
            main.java.srcDirs += 'src/main/kotlin'
        }
        
        // ⭐外部ネイティブビルド設定を追加️
        externalNativeBuild {
            // CMakeLists.txtからcmakeを実行
            cmake{
                path "CMakeLists.txt"
            }
        }
    
        defaultConfig {
         <以下省略..>

③ビルド

iOS編②と④で既にDartから呼び出す処理の準備を行なっているので、そのままビルドできるはずです✅
iOS編②と④をまだ作業していない場合はそれぞれファイルを追加してあげてください。

まとめ

今回は共通ライブラリを想定してフォルダを構成・実装していますが、ios/androidどちらかしか実装の必要がない、または個別に外部ライブラリを分ける場合はライブラリをios配下に構築する必要はありません。

この記事では「DartでC++関数を呼び出す」までをゴールとしていますが、この後に続いた「FlutterアプリでOpenCVを扱う」まで記事にしたいと思います。

参考とさせていただいたもの

・Flutterプラグインでdart:ffiを使ってみる
https://speakerdeck.com/espresso3389/flutterhurakuintedart-ffiwoshi-tutemiru
・dart:ffi 公式
https://dart.dev/interop/c-interop

Discussion