使用 dart:ffi 與 C 進行互動
Dart 的移動端、命令列和伺服器端應用所執行的 Dart 原生平台,均可以使用 dart:ffi
庫呼叫原生的 C 語言 API,用於讀、寫、分配和銷燬原生記憶體。
FFI 指的是 外部函式介面。類似的術語包括 原生介面 和 語言繫結。
相關的 API 文件可在
dart:ffi
API 文件
檢視。
範例
以下的範例將展示如何使用 dart:ffi
庫:
hello_world | |
primitives | |
structs | |
sqlite |
快速上手的 hello_world
hello_world 範例 展示瞭如何用最少的程式碼呼叫 C 語言庫。
檔案
hello_world 範例包含了以下檔案:
hello.dart | hello_world() 函式的檔案。 |
pubspec.yaml | |
hello_library/hello.h | hello_world() 函式。 |
hello_library/hello.c | hello.h 並實現了 hello_world() 函式。 |
hello_library/hello.def | |
hello_library/CMakeLists.txt |
建構 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
hello.dart
檔案
闡述了使用 dart:ffi 呼叫 C 函式的步驟:
-
匯入
dart:ffi
。 -
匯入
path
庫用於合成動態庫的路徑。 -
為 C 函式的 FFI 型別簽名的定義一個類別型。
-
為呼叫 C 函式的變數定義一個類別型。
-
利用一個變數儲存動態庫的路徑。
-
載入包含 C 函式的動態庫。
-
建立該 C 函式的參考,接著將其賦予變數。
-
呼叫 C 函式。
以下是每一個步驟對應的程式碼。
-
匯入 dart:ffi。
import 'dart:ffi' as ffi;
-
匯入
path
庫用於合成動態庫的路徑。
import 'dart:io' show Platform, Directory;
import 'package:path/path.dart' as path;
-
為 C 函式的 FFI 型別簽名的定義一個類別型。
參閱 定義原生型別的介面 瞭解dart:ffi
庫中定義的常用型別。
typedef hello_world_func = ffi.Void Function();
-
為呼叫 C 函式的變數定義一個類別型。
typedef HelloWorld = void Function();
-
利用一個變數儲存動態庫的路徑。
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');
}
-
載入包含 C 函式的動態庫。
final dylib = ffi.DynamicLibrary.open(libraryPath);
-
建立該 C 函式的參考,接著將其賦予變數。這段程式碼使用了步驟 2 和 3 定義的型別,以及步驟 4 建立的動態庫變數。
final HelloWorld hello = dylib
.lookup<ffi.NativeFunction<hello_world_func>>('hello_world')
.asFunction();
-
呼叫 C 函式。
hello();
當你理解 hello_world 範例的內容後,可以進一步學習 其他的 dart:ffi
範例。
整合並載入 C 庫
根據平臺和庫的型別的不同,捆綁(或 打包 和 分發) C 庫到 package 或應用並進行載入的方式,有所不同。
Interfacing with native types
The dart:ffi
library provides multiple types
that implement NativeType
and represent native types in C.
Some native types are only used as markers in type signatures while others (or their subtypes) can be instantiated.
Instantiable native types
The following native types can be used as markers in type signatures and they (or their subtypes) can be instantiated in Dart code:
Dart type | Description |
---|---|
Array | A fixed-sized array of items. Supertype of type specific arrays. |
Pointer | Represents a pointer into native C memory. |
Struct | The supertype of all FFI struct types. |
Union | The supertype of all FFI union types. |
Purely marker native types
The following are platform-agnostic native types that are used only as markers in type signatures, and can’t be instantiated in Dart code:
Dart type | Description |
---|---|
Bool | Represents a native bool in C. |
Double | Represents a native 64 bit double in C. |
Float | Represents a native 32 bit float in C. |
Int8 | Represents a native signed 8 bit integer in C. |
Int16 | Represents a native signed 16 bit integer in C. |
Int32 | Represents a native signed 32 bit integer in C. |
Int64 | Represents a native signed 64 bit integer in C. |
NativeFunction | Represents a function type in C. |
Opaque | The supertype of all opaque types in C. |
Uint8 | Represents a native unsigned 8 bit integer in C. |
Uint16 | Represents a native unsigned 16 bit integer in C. |
Uint32 | Represents a native unsigned 32 bit integer in C. |
Uint64 | Represents a native unsigned 64 bit integer in C. |
Void | Represents the void type in C. |
There are also many ABI specific marker native types that extend AbiSpecificInteger. Refer to their linked API documentation for more information and a guideline on what types they map to on specific platforms:
Dart type | Description |
---|---|
AbiSpecificInteger | The supertype of all ABI-specific integer types. |
Int | Represents the int type in C. |
IntPtr | Represents the intptr_t type in C. |
Long | Represents the long int (long ) type in C. |
LongLong | Represents the long long type in C. |
Short | Represents the short type in C. |
SignedChar | Represents the signed char type in C. |
Size | Represents the size_t type in C. |
UintPtr | Represents the uintptr_t type in C. |
UnsignedChar | Represents the unsigned char type in C. |
UnsignedInt | Represents the unsigned int type in C. |
UnsignedLong | Represents the unsigned long int (unsigned long ) type in C. |
UnsignedLongLong | Represents the unsigned long long type in C. |
UnsignedShort | Represents the unsigned short type in C. |
WChar | Represents the wchar_t type in C. |
package:ffigen
產生 FFI 的繫結
使用 為大量的 API 編寫繫結可能要花費你的大量時間。你可以使用 package:ffigen
繫結產生器,自動地從 C 標頭檔案產生 FFI 包裝,從而減少時間消耗。