Dart:ffi を使用した C の相互運用
- サンプルファイルをダウンロードする
- C ライブラリをバンドルしてロードする
- ネイティブ型とのインターフェイス
- package:ffigen で FFI バインディングを生成する
- ネイティブコードをビルドしてバンドルする
Dart のモバイル、コマンドライン、およびサーバーアプリは、Dart Native プラットフォームで実行される場合、dart:ffi ライブラリを使用してネイティブ C API を呼び出したり、ネイティブメモリを読み書き、割り当て、解放したりできます。FFI は foreign function interface の略です。類似の機能には、native interface や language 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.dart | C ライブラリの hello_world() 関数を使用する Dart ファイル。 |
pubspec.yaml | SDK の下限が 3.4 である Dart pubspec ファイル。 |
hello_library/hello.h | hello_world() 関数を宣言します。 |
hello_library/hello.c | hello.h をインポートし、hello_world() 関数を定義する C ファイル。 |
hello_library/hello.def | DLL をビルドする際に使用される情報を指定するモジュール定義ファイル。 |
hello_library/CMakeLists.txt | C コードを動的ライブラリにコンパイルするための 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 Worlddart:ffi を活用する
#dart:ffi ライブラリを使用して C 関数を呼び出す方法を学ぶには、hello.dart ファイルを確認してください。このセクションでは、このファイルの内容を説明します。
dart:ffiをインポートします。dartimport 'dart:ffi' as ffi;動的ライブラリのパスを格納するために使用する path ライブラリをインポートします。
dartimport 'dart:io' show Platform, Directory; import 'package:path/path.dart' as path;C 関数の FFI 型シグネチャを持つ typedef を作成します。
dart:ffiライブラリで最も使用される型については、ネイティブ型とのインターフェイスを参照してください。darttypedef hello_world_func = ffi.Void Function();C 関数を呼び出す際に使用する変数の
typedefを作成します。darttypedef HelloWorld = void Function();動的ライブラリのパスを格納する変数を格納します。
dartfinal 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', ); }C 関数を含む動的ライブラリを開きます。
dartfinal dylib = ffi.DynamicLibrary.open(libraryPath);C 関数への参照を取得し、変数に格納します。このコードは、ステップ 2 と 3 の
typedefs、およびステップ 4 の動的ライブラリ変数を組み合わせて使用します。dartfinal HelloWorld hello = dylib .lookup<ffi.NativeFunction<hello_world_func>>('hello_world') .asFunction();C 関数を呼び出します。
darthello();
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 型 | 説明 |
|---|---|
| Bool | C のネイティブブール値を表します。 |
| Double | C のネイティブ 64 ビット浮動小数点数を表します。 |
| Float | C のネイティブ 32 ビット浮動小数点数を表します。 |
| Int8 | C のネイティブ符号付き 8 ビット整数を表します。 |
| Int16 | C のネイティブ符号付き 16 ビット整数を表します。 |
| Int32 | C のネイティブ符号付き 32 ビット整数を表します。 |
| Int64 | C のネイティブ符号付き 64 ビット整数を表します。 |
| NativeFunction | C の関数型を表します。 |
| Opaque | C のすべての不透明型のスーパータイプ。 |
| Uint8 | C のネイティブ符号なし 8 ビット整数を表します。 |
| Uint16 | C のネイティブ符号なし 16 ビット整数を表します。 |
| Uint32 | C のネイティブ符号なし 32 ビット整数を表します。 |
| Uint64 | C のネイティブ符号なし 64 ビット整数を表します。 |
| Void | C の void 型を表します。 |
また、ABI 固有のマーカーネイティブ型も多数あり、これらは AbiSpecificInteger を継承しています。これらの型が特定のプラットフォームでどのようにマッピングされるかについては、次の表でリンクされている API ドキュメントを参照してください。
| Dart 型 | 説明 |
|---|---|
| AbiSpecificInteger | すべての ABI 固有の整数型のスーパータイプ。 |
| Int | C の int 型を表します。 |
| IntPtr | C の intptr_t 型を表します。 |
| Long | C の long int (long) 型を表します。 |
| LongLong | C の long long 型を表します。 |
| Short | C の short 型を表します。 |
| SignedChar | C の signed char 型を表します。 |
| Size | C の size_t 型を表します。 |
| UintPtr | C の uintptr_t 型を表します。 |
| UnsignedChar | C の unsigned char 型を表します。 |
| UnsignedInt | C の unsigned int 型を表します。 |
| UnsignedLong | C の unsigned long int 型を表します。 |
| UnsignedLongLong | C の unsigned long long 型を表します。 |
| UnsignedShort | C の unsigned short 型を表します。 |
| WChar | C の 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.c | add のコードが含まれる 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.dart | src/native_add_library.c をコンパイルし、アセットを package:native_add_library/native_add_library.dart という ID で宣言するためのビルドフック。 |
Dart または Flutter プロジェクトが package:native_add_library に依存している場合、run、build、および test コマンドを実行すると、hook/build.dart ビルドフックが呼び出されます。native_add_app サンプルは、native_add_library の使用例を示しています。
ビルドフック API ドキュメントを確認する
#API ドキュメントは、次のパッケージで入手できます。
- Dart FFI でのコードアセットのサポートについて学ぶには、
NativeおよびDefaultAssetのdart:ffiAPI リファレンスを参照してください。 hook/build.dartビルドフックについて学ぶには、package:hooksAPI リファレンスを参照してください。
フィードバックを提供する
#フィードバックを提供する場合は、これらのトラッキング課題を参照してください。