Experimental binding generator for FFI bindings.
Example
For some header file example.h:
int sum(int a, int b);
Add configurations to Pubspec File:
ffigen: output: 'generated_bindings.dart' headers: - 'example.h'
Output (generated_bindings.dart).
class NativeLibrary { final DynamicLibrary _dylib; NativeLibrary(DynamicLibrary dynamicLibrary) : _dylib = dynamicLibrary; int sum(int a, int b) { _sum ??= _dylib.lookupFunction<_c_sum, _dart_sum>('sum'); return _sum(a, b); } _dart_sum _sum;; } typedef _c_sum = ffi.Int32 Function(Int32 a, Int32 b); typedef _dart_sum = int Function(int a,int b);
Using this package
- Add this package as dev_dependency in your
pubspec.yaml
. - Setup for use (see Setup).
- Configurations must be provided in
pubspec.yaml
or in a custom YAML file (see configurations). - Run the tool-
pub run ffigen
.
Setup
package:ffigen
uses LLVM. Install LLVM in the following way.
ubuntu/linux
- Install libclangdev –
sudo apt-get install libclang-dev
.
Windows
- Install Visual Studio with C++ development support.
- Install LLVM.
MacOS
- Install Xcode.
- Install LLVM –
brew install llvm
.
Configurations
Configurations can be provided in 2 ways-
- In the project’s
pubspec.yaml
file under the keyffigen
. - Via a custom YAML file, then specify this file while running –
pub run ffigen --config config.yaml
The following configuration options are available-
Key | Required | Explaination | Example |
---|---|---|---|
output | yes | Output path of the generated bindings. | output: ‘generated_bindings.dart’ |
headers | yes | List of C headers to use. Glob syntax is allowed. | headers: – ‘folder/**.h’ – ‘folder/specific_header.h’ |
header-filter | no | Name of headers to include/exclude. | header-filter: include: – ‘index.h’ – ‘platform.h’ |
name | prefer | Name of generated class. | name: ‘SQLite’ |
description | prefer | Dart Doc for generated class. | description: ‘Bindings to SQLite’ |
compiler-opts | no | Pass compiler options to clang. | compiler-opts: ‘-I/usr/lib/llvm-9/include/’ |
functions structs enums | no | Filters for declarations. Default: all are included | functions: include: # Exclude is also available. names: # Matches with exact name. – someFuncName – anotherName matches: # Matches using regexp. – prefix.* – [a-z][a-zA-Z0-9]* prefix: ‘cx_’ # Prefix added to all functions. prefix-replacement: # Replaces a functions’s prefix. ‘clang_’: ” ‘_’: ‘C’ |
array-workaround | no | Should generate workaround for fixed arrays in Structs. See Array Workaround Default: false | array-workaround: true |
comments | no | Extract documentation comments for declarations. Options: brief/full/none Default: brief By default clang only parses documentation comments. To enable ordinary comments (starting with // or /*) add -fparse-all-comments to compiler-opts . | comments: ‘full’ |
sort | no | Sort the bindings according to name. Default: false, i.e keep the order as in the source files. | sort: true |
use-supported-typedefs | no | Should automatically map typedefs, E.g uint8_t => Uint8, int16_t => Int16 etc. Default: true | use-supported-typedefs: true |
preamble | no | Raw header of the file, pasted as-it-is. | preamble: | /// AUTO GENERATED FILE, DO NOT EDIT. /// /// Generated by `package:ffigen`. |
size-map | no | Size of integers to use (in bytes). The defaults (see example) may not be portable on all OS. Do not change these unless absolutely sure. | # These are optional and also default, # Omitting any and the default will be used. size-map: char: 1 unsigned char: 1 short: 2 unsigned short: 2 int: 4 unsigned int: 4 long: 8 unsigned long: 8 long long: 8 unsigned long long: 8 enum: 4 |
Array-Workaround
Fixed size array’s in structs aren’t currently supported by Dart. However we provide a workaround, using which array items can now be accessed using []
operator.
Here’s a C structure from libclang-
typedef struct { unsigned long long data[3]; } CXFileUniqueID;
The generated code is –
class CXFileUniqueID extends ffi.Struct { @ffi.Uint64() int _unique_data_item_0; @ffi.Uint64() int _unique_data_item_1; @ffi.Uint64() int _unique_data_item_2; /// Helper for array `data`. ArrayHelper_CXFileUniqueID_data_level0 get data => ArrayHelper_CXFileUniqueID_data_level0(this, [3], 0, 0); } /// Helper for array `data` in struct `CXFileUniqueID`. class ArrayHelper_CXFileUniqueID_data_level0 { final CXFileUniqueID _struct; final List<int> dimensions; final int level; final int _absoluteIndex; int get length => dimensions[level]; ArrayHelper_CXFileUniqueID_data_level0( this._struct, this.dimensions, this.level, this._absoluteIndex); void _checkBounds(int index) { if (index >= length || index < 0) { throw RangeError( 'Dimension $level: index not in range 0..${length} exclusive.'); } } int operator [](int index) { _checkBounds(index); switch (_absoluteIndex + index) { case 0: return _struct._unique_data_item_0; case 1: return _struct._unique_data_item_1; case 2: return _struct._unique_data_item_2; default: throw Exception('Invalid Array Helper generated.'); } } void operator []=(int index, int value) { _checkBounds(index); switch (_absoluteIndex + index) { case 0: _struct._unique_data_item_0 = value; break; case 1: _struct._unique_data_item_1 = value; break; case 2: _struct._unique_data_item_2 = value; break; default: throw Exception('Invalid Array Helper generated.'); } } }
Limitations
- Multi OS support for types such as long. Issue #7
- Function’s passing/returning structs by value are skipped. Issue #3
- Structs containing structs will have all their members removed. Issue #4
Trying out examples
cd examples/<example_u_want_to_run>
, Runpub get
.- Run
pub run ffigen
.
Running Tests
- Run setup to build the LLVM wrapper –
pub run ffigen:setup
. - Dynamic library for some tests also need to be built before running the examples.
cd test/native_test
.- Run
dart build_test_dylib.dart
.
Run tests from the root of the package with pub run test
.
Download FFI binding generator source code on GitHub
https://github.com/dart-lang/ffigen
Provides the list of the opensource Flutter apps collection with GitHub repository.