メインコンテンツにスキップ

Dart:ffi を使用した C の相互運用

Dart のモバイル、コマンドライン、およびサーバーアプリは、Dart Native プラットフォームで実行される場合、dart:ffi ライブラリを使用してネイティブ C API を呼び出したり、ネイティブメモリを読み書き、割り当て、解放したりできます。FFIforeign function interface の略です。類似の機能には、native interfacelanguage bindings という用語もあります。

API ドキュメントは、dart:ffi API リファレンスで利用可能です。

サンプルファイルをダウンロードする

#

このガイドのサンプルを操作するには、完全な ffi サンプルディレクトリをダウンロードしてください。これには、dart:ffi ライブラリの使用方法を示す以下のサンプルが含まれています。

説明
hello_world引数も戻り値もない C 関数を呼び出す方法。
primitives整数またはポインタの引数と戻り値を持つ C 関数を呼び出す方法。
structs構造体を使用して、文字列を C との間でやり取りしたり、単純なおよび複雑な C 構造体を処理したりする方法。
test_utilsこれらのすべてのサンプルの一般的なテストユーティリティ。

hello_world サンプルを確認する

#

hello_world サンプルには、C ライブラリを呼び出すために必要な最小限のコードが含まれています。このサンプルは、前のセクションでダウンロードした samples/ffi にあります。

ファイル

#

hello_world サンプルには、次のファイルが含まれています。

ソースファイル説明
hello.dartC ライブラリの hello_world() 関数を使用する Dart ファイル。
pubspec.yamlSDK の下限が 3.4 である Dart pubspec ファイル。
hello_library/hello.hhello_world() 関数を宣言します。
hello_library/hello.chello.h をインポートし、hello_world() 関数を定義する C ファイル。
hello_library/hello.defDLL をビルドする際に使用される情報を指定するモジュール定義ファイル。
hello_library/CMakeLists.txtC コードを動的ライブラリにコンパイルするための CMake ビルドファイル。

C ライブラリのビルドにより、libhello.dylib (macOS)、libhello.dll (Windows)、または libhello.so (Linux) という名前の動的ライブラリファイルを含むいくつかのファイルが作成されます。

ビルドと実行

#

動的ライブラリをビルドして Dart アプリを実行するためのコマンドは、次のシリーズに似ています。

cd hello_library
cmake .
...
make
...
cd ..
dart pub get
dart run hello.dart
Hello World

dart:ffi を活用する

#

dart:ffi ライブラリを使用して C 関数を呼び出す方法を学ぶには、hello.dart ファイルを確認してください。このセクションでは、このファイルの内容を説明します。

  1. dart:ffi をインポートします。

    dart
    import 'dart:ffi' as ffi;
  2. 動的ライブラリのパスを格納するために使用する path ライブラリをインポートします。

    dart
    import 'dart:io' show Platform, Directory;
    import 'package:path/path.dart' as path;
  3. C 関数の FFI 型シグネチャを持つ typedef を作成します。
    dart:ffi ライブラリで最も使用される型については、ネイティブ型とのインターフェイスを参照してください。

    dart
    typedef hello_world_func = ffi.Void Function();
  4. C 関数を呼び出す際に使用する変数の typedef を作成します。

    dart
    typedef HelloWorld = void Function();
  5. 動的ライブラリのパスを格納する変数を格納します。

    dart
    final String libraryPath;
    if (Platform.isMacOS) {
      libraryPath = path.join(
        Directory.current.path,
        'hello_library',
        'libhello.dylib',
      );
    } else if (Platform.isWindows) {
      libraryPath = path.join(
        Directory.current.path,
        'hello_library',
        'Debug',
        'hello.dll',
      );
    } else {
      libraryPath = path.join(
        Directory.current.path,
        'hello_library',
        'libhello.so',
      );
    }
  6. C 関数を含む動的ライブラリを開きます。

    dart
    final dylib = ffi.DynamicLibrary.open(libraryPath);
  7. C 関数への参照を取得し、変数に格納します。このコードは、ステップ 2 と 3 の typedefs、およびステップ 4 の動的ライブラリ変数を組み合わせて使用します。

    dart
    final HelloWorld hello = dylib
        .lookup<ffi.NativeFunction<hello_world_func>>('hello_world')
        .asFunction();
  8. C 関数を呼び出します。

    dart
    hello();

hello_world サンプルを理解したら、他の dart:ffi サンプルを参照してください。

C ライブラリをバンドルしてロードする

#

ネイティブ C ライブラリをバンドル/パッケージ/配布してからロードする方法は、プラットフォームとライブラリの種類によって異なります。

方法については、以下のページとサンプルを参照してください。

  • Android アプリ向けの Flutter dart:ffi
  • iOS アプリ向けの Flutter dart:ffi
  • macOS アプリ向けの Flutter dart:ffi
  • dart:ffi サンプル

ネイティブ型とのインターフェイス

#

dart:ffi ライブラリは、NativeType を実装し、C のネイティブ型を表す複数の型を提供します。一部のネイティブ型はインスタンス化できます。その他のネイティブ型は、型シグネチャのマーカーとしてのみ使用できます。

これらの型シグネチャマーカーをインスタンス化できます

#

次のネイティブ型は、型シグネチャのマーカーとして使用できます。これらまたはそのサブタイプは、Dart コードでインスタンス化できます

Dart 型説明
Array固定サイズのアイテム配列。型固有の配列のスーパータイプ。
Pointerネイティブ C メモリへのポインタを表します。
Structすべての FFI 構造体型のスーパータイプ。
Unionすべての FFI 共用体型のスーパータイプ。

型シグネチャマーカーとしてのみ機能します

#

以下のリストは、型シグネチャのマーカーとして機能するプラットフォームに依存しないネイティブ型を示しています。これらは Dart コードでインスタンス化できません

Dart 型説明
BoolC のネイティブブール値を表します。
DoubleC のネイティブ 64 ビット浮動小数点数を表します。
FloatC のネイティブ 32 ビット浮動小数点数を表します。
Int8C のネイティブ符号付き 8 ビット整数を表します。
Int16C のネイティブ符号付き 16 ビット整数を表します。
Int32C のネイティブ符号付き 32 ビット整数を表します。
Int64C のネイティブ符号付き 64 ビット整数を表します。
NativeFunctionC の関数型を表します。
OpaqueC のすべての不透明型のスーパータイプ。
Uint8C のネイティブ符号なし 8 ビット整数を表します。
Uint16C のネイティブ符号なし 16 ビット整数を表します。
Uint32C のネイティブ符号なし 32 ビット整数を表します。
Uint64C のネイティブ符号なし 64 ビット整数を表します。
VoidC の void 型を表します。

また、ABI 固有のマーカーネイティブ型も多数あり、これらは AbiSpecificInteger を継承しています。これらの型が特定のプラットフォームでどのようにマッピングされるかについては、次の表でリンクされている API ドキュメントを参照してください。

Dart 型説明
AbiSpecificIntegerすべての ABI 固有の整数型のスーパータイプ。
IntC の int 型を表します。
IntPtrC の intptr_t 型を表します。
LongC の long int (long) 型を表します。
LongLongC の long long 型を表します。
ShortC の short 型を表します。
SignedCharC の signed char 型を表します。
SizeC の size_t 型を表します。
UintPtrC の uintptr_t 型を表します。
UnsignedCharC の unsigned char 型を表します。
UnsignedIntC の unsigned int 型を表します。
UnsignedLongC の unsigned long int 型を表します。
UnsignedLongLongC の unsigned long long 型を表します。
UnsignedShortC の unsigned short 型を表します。
WCharC の wchar_t 型を表します。

package:ffigen で FFI バインディングを生成する

#

大規模な API サーフェスでは、C コードと統合する Dart バインディングを作成するのは時間がかかる場合があります。C ヘッダーファイルから Dart が FFI ラッパーを作成するようにするには、package:ffigen バインディングジェネレーターを使用します。

ネイティブコードをビルドしてバンドルする

#

Dart のビルドフック (旧称ネイティブアセット) を使用すると、パッケージに Dart ソースコード以上のものを含めることができます。パッケージには、実行時に透過的にビルド、バンドル、および利用可能になるネイティブコードアセットを含めることができるようになりました。

この機能により、Dart パッケージがネイティブコードに依存し、使用する方法が簡素化されます。

  • hook/build.dart のパッケージのビルドフックを使用して、ネイティブコードをビルドするかバイナリを取得します。
  • Dart と Flutter は、ビルドフックが報告する CodeAsset をバンドルします。
  • 実行時に、assetId を使用して、宣言的な @Native<>() extern 関数を通じてコードアセットにアクセスします。

Flutter とスタンドアロン Dart は、アプリが使用するすべてのパッケージのネイティブコードを自動的にバンドルし、実行時に利用可能にします。これは、flutter (run|build)dart (run|build) の両方で機能します。

native_add_library サンプルを確認する

#

native_add_library サンプルには、Dart パッケージで C コードをビルドしてバンドルするための最小限のコードが含まれています。

このサンプルには、次のファイルが含まれています。

ソースファイル説明
src/native_add_library.cadd のコードが含まれる C ファイル。
lib/native_add_library.dartアセット package:native_add_library/native_add_library.dart の C 関数 add を FFI 経由で呼び出す Dart ファイル。(アセット ID はデフォルトでライブラリ URI になります。)
test/native_add_library_test.dartネイティブコードを使用する Dart テスト。
hook/build.dartsrc/native_add_library.c をコンパイルし、アセットを package:native_add_library/native_add_library.dart という ID で宣言するためのビルドフック。

Dart または Flutter プロジェクトが package:native_add_library に依存している場合、runbuild、および test コマンドを実行すると、hook/build.dart ビルドフックが呼び出されます。native_add_app サンプルは、native_add_library の使用例を示しています。

ビルドフック API ドキュメントを確認する

#

API ドキュメントは、次のパッケージで入手できます。

  • Dart FFI でのコードアセットのサポートについて学ぶには、Native および DefaultAssetdart:ffi API リファレンスを参照してください。
  • hook/build.dart ビルドフックについて学ぶには、package:hooks API リファレンスを参照してください。

フィードバックを提供する

#

フィードバックを提供する場合は、これらのトラッキング課題を参照してください。