dart-native-interop-ffi
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
Chinesedart-c-interop
dart-c-interop
Goal
目标
Integrates native C libraries into Dart applications using the library. Automates FFI binding generation for large API surfaces, safely maps native types across platforms, and strictly manages native memory lifecycles using manual allocation and finalizers. Assumes the user has a configured Dart or Flutter environment and access to the target C headers and compiled dynamic libraries (, , or ).
dart:ffi.so.dylib.dll使用库将原生C库集成到Dart应用中。针对大型API自动生成FFI绑定,跨平台安全映射原生类型,并通过手动分配和终结器严格管理原生内存生命周期。假设用户已配置好Dart或Flutter环境,且可访问目标C头文件和编译后的动态库(、或)。
dart:ffi.so.dylib.dllDecision Logic
决策逻辑
When implementing C interop, evaluate the API surface and memory requirements to determine the correct path:
- API Surface Size:
- If the C API is large or complex: Use to automate binding generation.
package:ffigen - If the C API is small (1-5 functions): Manually define signatures and lookups.
typedef
- If the C API is large or complex: Use
- Memory Ownership:
- If Dart creates native structures or strings: Manually allocate using or
callocfrommalloc.package:ffi - If Dart holds pointers to C-allocated memory that must be freed when the Dart object is garbage collected: Implement and attach a
Finalizable.NativeFinalizer
- If Dart creates native structures or strings: Manually allocate using
- Cross-Platform Types:
- If mapping standard C integer types (,
int,long): PREFERsize_tsubtypes (e.g.,abiSpecificInteger,Int,Long) over fixed-width types (Size,Int32) to ensure cross-platform ABI compatibility.Int64
- If mapping standard C integer types (
在实现C互操作时,需评估API规模和内存需求以确定正确方案:
- API规模:
- 若C API庞大或复杂:使用自动生成绑定。
package:ffigen - 若C API规模较小(1-5个函数):手动定义签名并查找函数。
typedef
- 若C API庞大或复杂:使用
- 内存所有权:
- 若Dart创建原生结构体或字符串:使用中的
package:ffi或calloc手动分配内存。malloc - 若Dart持有C分配的内存指针,且需在Dart对象被垃圾回收时释放:实现并附加
Finalizable。NativeFinalizer
- 若Dart创建原生结构体或字符串:使用
- 跨平台类型:
- 若映射标准C整数类型(、
int、long):优先使用size_t子类型(如abiSpecificInteger、Int、Long)而非固定宽度类型(Size、Int32),以确保跨平台ABI兼容性。Int64
- 若映射标准C整数类型(
Instructions
操作步骤
1. Determine Environment and Assets
1. 确认环境与资源
STOP AND ASK THE USER:
- "What are the target platforms (macOS, Windows, Linux, Android, iOS)?"
- "Where are the C header files () and compiled dynamic libraries located in your project?"
.h
请询问用户以下问题:
- "目标平台是什么(macOS、Windows、Linux、Android、iOS)?"
- "C头文件()和编译后的动态库在项目中的哪个位置?"
.h
2. Configure Binding Generation (If using ffigen
)
ffigen2. 配置绑定生成(若使用ffigen
)
ffigenFor large APIs, configure to parse C headers and generate Dart bindings.
Create or update :
package:ffigenffigen.yamlyaml
name: NativeLibrary
description: Bindings for native C library.
output: 'lib/src/generated_bindings.dart'
headers:
entry-points:
- 'src/native_lib.h'
include-directives:
- '**native_lib.h'
ffi-native:
asset: 'native_lib'Run the generator:
bash
dart run ffigen --config ffigen.yaml针对大型API,配置来解析C头文件并生成Dart绑定。创建或更新:
package:ffigenffigen.yamlyaml
name: NativeLibrary
description: Bindings for native C library.
output: 'lib/src/generated_bindings.dart'
headers:
entry-points:
- 'src/native_lib.h'
include-directives:
- '**native_lib.h'
ffi-native:
asset: 'native_lib'运行生成器:
bash
dart run ffigen --config ffigen.yaml3. Load the Dynamic Library
3. 加载动态库
Implement platform-specific routing to load the dynamic library.
dart
import 'dart:ffi' as ffi;
import 'dart:io' show Platform;
import 'package:path/path.dart' as path;
ffi.DynamicLibrary loadNativeLibrary(String libraryName) {
if (Platform.isMacOS || Platform.isIOS) {
return ffi.DynamicLibrary.open('lib$libraryName.dylib');
} else if (Platform.isWindows) {
return ffi.DynamicLibrary.open('$libraryName.dll');
} else {
return ffi.DynamicLibrary.open('lib$libraryName.so');
}
}
final dylib = loadNativeLibrary('hello');实现平台特定的路由来加载动态库。
dart
import 'dart:ffi' as ffi;
import 'dart:io' show Platform;
import 'package:path/path.dart' as path;
ffi.DynamicLibrary loadNativeLibrary(String libraryName) {
if (Platform.isMacOS || Platform.isIOS) {
return ffi.DynamicLibrary.open('lib$libraryName.dylib');
} else if (Platform.isWindows) {
return ffi.DynamicLibrary.open('$libraryName.dll');
} else {
return ffi.DynamicLibrary.open('lib$libraryName.so');
}
}
final dylib = loadNativeLibrary('hello');4. Manually Map Types and Look Up Functions (If not using ffigen
)
ffigen4. 手动映射类型与查找函数(若不使用ffigen
)
ffigenDefine the C signature and the Dart signature. Prefer types for standard C types.
AbiSpecificIntegerdart
import 'dart:ffi' as ffi;
// C signature: void hello_world();
typedef hello_world_func = ffi.Void Function();
// Dart signature
typedef HelloWorld = void Function();
// C signature: long process_data(size_t size);
// PREFER AbiSpecificInteger (Long, Size) over Int64/Int32
typedef process_data_func = ffi.Long Function(ffi.Size size);
typedef ProcessData = int Function(int size);
final HelloWorld hello = dylib
.lookup<ffi.NativeFunction<hello_world_func>>('hello_world')
.asFunction();定义C签名和Dart签名。对于标准C类型,优先使用类型。
AbiSpecificIntegerdart
import 'dart:ffi' as ffi;
// C signature: void hello_world();
typedef hello_world_func = ffi.Void Function();
// Dart signature
typedef HelloWorld = void Function();
// C signature: long process_data(size_t size);
// PREFER AbiSpecificInteger (Long, Size) over Int64/Int32
typedef process_data_func = ffi.Long Function(ffi.Size size);
typedef ProcessData = int Function(int size);
final HelloWorld hello = dylib
.lookup<ffi.NativeFunction<hello_world_func>>('hello_world')
.asFunction();5. Manage Native Memory and Resources
5. 管理原生内存与资源
When allocating memory in Dart to pass to C, use . When wrapping C-allocated memory, use and to prevent memory leaks.
callocFinalizableNativeFinalizerdart
import 'dart:ffi' as ffi;
import 'package:ffi/ffi.dart'; // Provides calloc, malloc, Utf8
// 1. Manual Allocation
ffi.Pointer<Utf8> allocateString(String dartString) {
// DO manually allocate memory using calloc
final ffi.Pointer<Utf8> cString = dartString.toNativeUtf8(allocator: calloc);
return cString;
}
void freeMemory(ffi.Pointer pointer) {
// DO manually free memory
calloc.free(pointer);
}
// 2. Native Finalizer for C-allocated memory
// Assume C provides: void free_resource(Resource* res);
final ffi.Pointer<ffi.NativeFunction<ffi.Void Function(ffi.Pointer)>> freeResourcePtr =
dylib.lookup('free_resource');
final ffi.NativeFinalizer _finalizer = ffi.NativeFinalizer(freeResourcePtr.cast());
class NativeResourceWrapper implements ffi.Finalizable {
final ffi.Pointer<ffi.Void> _cResource;
NativeResourceWrapper(this._cResource) {
// DO use Finalizable and NativeFinalizer to ensure cleanup
_finalizer.attach(this, _cResource.cast(), detach: this);
}
void dispose() {
_finalizer.detach(this);
// Manually call the free function if disposed early
final freeFunc = freeResourcePtr.asFunction<void Function(ffi.Pointer)>();
freeFunc(_cResource);
}
}当在Dart中分配内存以传递给C时,使用。当包装C分配的内存时,使用和来防止内存泄漏。
callocFinalizableNativeFinalizerdart
import 'dart:ffi' as ffi;
import 'package:ffi/ffi.dart'; // Provides calloc, malloc, Utf8
// 1. Manual Allocation
ffi.Pointer<Utf8> allocateString(String dartString) {
// DO manually allocate memory using calloc
final ffi.Pointer<Utf8> cString = dartString.toNativeUtf8(allocator: calloc);
return cString;
}
void freeMemory(ffi.Pointer pointer) {
// DO manually free memory
calloc.free(pointer);
}
// 2. Native Finalizer for C-allocated memory
// Assume C provides: void free_resource(Resource* res);
final ffi.Pointer<ffi.NativeFunction<ffi.Void Function(ffi.Pointer)>> freeResourcePtr =
dylib.lookup('free_resource');
final ffi.NativeFinalizer _finalizer = ffi.NativeFinalizer(freeResourcePtr.cast());
class NativeResourceWrapper implements ffi.Finalizable {
final ffi.Pointer<ffi.Void> _cResource;
NativeResourceWrapper(this._cResource) {
// DO use Finalizable and NativeFinalizer to ensure cleanup
_finalizer.attach(this, _cResource.cast(), detach: this);
}
void dispose() {
_finalizer.detach(this);
// Manually call the free function if disposed early
final freeFunc = freeResourcePtr.asFunction<void Function(ffi.Pointer)>();
freeFunc(_cResource);
}
}6. Validate and Fix
6. 验证与修复
After implementing the bindings, instruct the user to run a basic test script.
- Validation: Does the Dart code successfully invoke the C function without a segmentation fault?
- Fix: If an error occurs, verify the library path and ensure the library is compiled for the correct architecture (e.g., ARM64 vs x86_64). On macOS, ensure the library is signed if running in a strict environment.
Invalid argument(s): Unknown library
实现绑定后,指导用户运行基础测试脚本。
- 验证: Dart代码能否成功调用C函数且不出现段错误?
- 修复: 若出现错误,请验证库路径,并确保库是针对正确架构编译的(例如ARM64 vs x86_64)。在macOS上,若运行在严格环境中,需确保库已签名。
Invalid argument(s): Unknown library
Constraints
约束条件
- DO NOT use fixed-width integers (e.g., ,
Int32) for CInt64,long, orint. You MUST usesize_ttypes (abiSpecificInteger,ffi.Long,ffi.Int).ffi.Size - DO NOT leave native memory unmanaged. Any pointer allocated via or
callocMUST have a correspondingmalloccall or be managed by an allocator lifecycle.free - DO NOT manually write bindings for C headers exceeding 10 functions/structs. You MUST use .
package:ffigen - DO NOT block the main Dart isolate with long-running C functions. For heavy C computation, you MUST integrate with to run the FFI calls on a background worker.
dart-concurrency-isolates - DO NOT assume dynamic library paths are absolute. Always construct paths dynamically using
dart:iochecks andPlatform.package:path
- 请勿对C的、
long或int使用固定宽度整数(如size_t、Int32)。必须使用Int64类型(abiSpecificInteger、ffi.Long、ffi.Int)。ffi.Size - 请勿让原生内存处于未管理状态。任何通过或
calloc分配的指针必须有对应的malloc调用,或由分配器生命周期管理。free - 请勿为超过10个函数/结构体的C头文件手动编写绑定。必须使用。
package:ffigen - 请勿使用长时间运行的C函数阻塞Dart主隔离区。对于重型C计算,必须与集成,在后台工作线程上运行FFI调用。
dart-concurrency-isolates - 请勿假设动态库路径是绝对路径。始终使用的
dart:io检查和Platform动态构造路径。package:path