目次

dart:ffi を使用した C 相互運用

Dart Native プラットフォームで実行される Dart モバイル、コマンドライン、およびサーバーアプリは、dart:ffi ライブラリを使用してネイティブ C API を呼び出し、ネイティブメモリの読み取り、書き込み、割り当て、および割り当て解除を行うことができます。FFI外部関数インターフェース の略です。同様の機能の他の用語には、ネイティブインターフェース および 言語バインディング があります。

API ドキュメントは、dart:ffi API リファレンスで入手できます。

サンプルファイルのダウンロード

#

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

説明
hello_world引数と戻り値のない C 関数を呼び出す方法。
primitivesint またはポインタである引数と戻り値を持つ 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
    var libraryPath = path.join(Directory.current.path, 'hello_library',
        'libhello.so');
    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');
    }
  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 ライブラリをバンドル/パッケージ化/配布してロードする方法は、プラットフォームとライブラリの種類によって異なります。

方法については、次のページと例を参照してください。

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

#

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

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

#

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

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

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

#

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

Dart 型説明
BoolC のネイティブ bool を表します。
DoubleC のネイティブ 64 ビット double を表します。
FloatC のネイティブ 32 ビット float を表します。
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バインディングを作成するには時間がかかることがあります。DartにCヘッダーファイルからFFIラッパーを作成させるには、package:ffigenバインディングジェネレーターを使用します。

ネイティブアセットのビルドとバンドル

#

ネイティブアセット機能は、ネイティブコードに依存するDartパッケージの配布に関連する多くの問題を解決するはずです。これは、FlutterおよびスタンドアロンのDartアプリケーションのビルドに関与するさまざまなビルドシステムと統合するための統一されたフックを提供することにより実現します。

この機能は、Dartパッケージがネイティブコードに依存して使用する方法を簡素化するはずです。ネイティブアセットは、次のメリットを提供する必要があります。

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

ネイティブ実験にオプトインすると、flutter (run|build)およびdart (run|build)コマンドは、Dartコードとともにネイティブコードをビルドおよびバンドルします。

native_add_libraryの例を確認する

#

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

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

ソースファイル説明
src/native_add_library.caddのコードを含むCファイル。
lib/native_add_library.dartFFIを介してアセットpackage:native_add_library/native_add_library.dartでC関数addを呼び出すDartファイル。(アセットIDはデフォルトでライブラリURIになることに注意してください。)
test/native_add_library_test.dartネイティブコードを使用するDartテスト。
hook/build.dartsrc/native_add_library.cをコンパイルし、IDpackage:native_add_library/native_add_library.dartを持つコンパイル済みアセットを宣言するためのビルドフック。

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

ネイティブアセット API ドキュメントを確認

#

次のパッケージのAPIドキュメントを見つけることができます

実験にオプトイン

#

実験を有効にしてフィードバックを提供する方法については、次のトラッキングイシューを参照してください。