commit | 12e914671cd401dfc6d6fb76c86fe2d8dfba3b2a | [log] [tgz] |
---|---|---|
author | Prerak Mann <mannprerak2@gmail.com> | Fri Apr 16 21:33:09 2021 +0530 |
committer | GitHub <noreply@github.com> | Fri Apr 16 18:03:09 2021 +0200 |
tree | 4163c4f7ce7bf8f91c261e4cdb126e164ebe0370 | |
parent | baa730a59fd67f757493faa5ce378eb4cfea44bb [diff] |
Support for Packed struct (#205)
Binding generator for FFI bindings.
For some header file example.h:
int sum(int a, int b);
Add configurations to Pubspec File:
ffigen: output: 'generated_bindings.dart' headers: entry-points: - 'example.h'
Output (generated_bindings.dart).
class NativeLibrary { final Pointer<T> Function<T extends NativeType>(String symbolName) _lookup; NativeLibrary(DynamicLibrary dynamicLibrary) : _lookup = dynamicLibrary.lookup; NativeLibrary.fromLookup( Pointer<T> Function<T extends NativeType>(String symbolName) lookup) : _lookup = lookup; int sum(int a, int b) { return _sum(a, b); } late final _sum_ptr = _lookup<NativeFunction<_c_sum>>('sum'); late final _dart_sum _sum = _sum_ptr.asFunction<_dart_sum>(); } typedef _c_sum = Int32 Function(Int32 a, Int32 b); typedef _dart_sum = int Function(int a, int b);
ffigen
under dev_dependencies
in your pubspec.yaml
.pubspec.yaml
or in a custom YAML file (see configurations).dart run ffigen
.Jump to FAQ.
package:ffigen
uses LLVM. Install LLVM (9+) in the following way.
sudo apt-get install libclang-dev
.winget install -e --id LLVM.LLVM
.brew install llvm
.Configurations can be provided in 2 ways-
pubspec.yaml
file under the key ffigen
.dart run ffigen --config config.yaml
The following configuration options are available-
output: 'generated_bindings.dart'
llvm-path: - '/usr/local/opt/llvm' - 'C:\Program Files\llvm` - '/usr/lib/llvm-11'
headers: entry-points: - 'folder/**.h' - 'folder/specific_header.h' include-directives: - '**index.h' - '**/clang-c/**' - '/full/path/to/a/header.h'
name: 'SQLite'
description: 'Bindings to SQLite'
compiler-opts: - '-I/usr/lib/llvm-9/include/'
and/or via the command line -
dart run ffigen --compiler-opts "-I/headers -L 'path/to/folder name/file'"
compiler-opts-automatic: macos: include-c-standard-library: false
functions: include: # 'exclude' is also available. # Matches using regexp. - [a-z][a-zA-Z0-9]* # '.' matches any character. - prefix.* # Matches with exact name - someFuncName # Full names have higher priority. - anotherName rename: # Regexp groups based replacement. 'clang_(.*)': '$1' 'clang_dispose': 'dispose' # Removes '_' from beginning. '_(.*)': '$1' symbol-address: # Used to expose symbol and typedef. include: - myFunc enums: member-rename: '(.*)': # Matches any enum. # Removes '_' from beginning # enum member name. '_(.*)': '$1' # Full names have higher priority. 'CXTypeKind': # $1 keeps only the 1st # group i.e only '(.*)'. 'CXType(.*)': '$1' globals: exclude: - aGlobal rename: # Removes '_' from # beginning of a name. '_(.*)': '$1'
structs: pack: # Matches with the generated name. 'NoPackStruct': none # No packing '.*': 1 # Pack all structs with value 1
array-workaround: true
comments: style: any length: full
structs: dependency-only: opaque
sort: true
use-supported-typedefs: true
dart-bool: true
use-dart-handle: true
preamble: | /// AUTO GENERATED FILE, DO NOT EDIT. /// /// Generated by `package:ffigen`.
typedef-map: 'my_custom_type': 'IntPtr' 'size_t': 'Int64'
# 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
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() external int _unique_data_item_0; @ffi.Uint64() external int _unique_data_item_1; @ffi.Uint64() external 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.'); } } }
cd examples/<example_u_want_to_run>
, Run dart pub get
.dart run ffigen
.cd test/native_test
.dart build_test_dylib.dart
.Run tests from the root of the package with dart run test
.
Note: If llvm is not installed in one of the default locations, tests may fail.
Ffigen supports regexp based renaming, the regexp must be a full match, for renaming you can use regexp groups ($1
means group 1).
E.g - For renaming clang_dispose_string
to string_dispose
. We can can match it using clang_(.*)_(.*)
and rename with $2_$1
.
Here's an example of how to remove prefix underscores from any struct and its members.
structs: ... rename: '_(.*)': '$1' # Removes prefix underscores from all structures. member-rename: '.*': # Matches any struct. '_(.*)': '$1' # Removes prefix underscores from members.
The default behaviour is to include everything directly/transitively under each of the entry-points
specified.
If you only want to have declarations directly particular header you can do so using include-directives
. You can use glob matching to match header paths.
headers: entry-points: - 'path/to/my_header.h' include-directives: - '**my_header.h' # This glob pattern matches the header path.
Ffigen supports including/excluding declarations using full regexp matching.
Here's an example to filter functions using names
functions: include: - 'clang.*' # Include all functions starting with clang. exclude: - '.*dispose': # Exclude all functions ending with dispose.
This will include clang_help
. But will exclude clang_dispose
.
Note: exclude overrides include.
Ffigen treats char*
just as any other pointer,(Pointer<Int8>
). To convert these to/from String
, you can use package:ffi. Use ptr.cast<Utf8>().toDartString()
to convert char*
to dart string
and "str".toNativeUtf8()
to convert string
to char*
.
Although dart:ffi
doesn't have a NativeType for bool
, they can be implemented as Uint8
. Ffigen generates dart bool
for function parameters and return type by default. To disable this, and use int
instead, set dart-bool: false
in configurations.
Unnamed enums are handled separately, under the key unnamed-enums
, and are generated as top level constants.
Here's an example that shows how to include/exclude/rename unnamed enums
unnamed-enums: include: - 'CX_.*' exclude: - '.*Flag' rename: 'CXType_(.*)': '$1'
This happens when an excluded struct is a dependency to some included declaration. (A dependency means a struct is being passed/returned by a function or is member of another struct in some way)
Note: If you supply structs
-> dependency-only
as opaque
ffigen will generate these struct dependencies as Opaque
if they were only passed by reference(pointer).
structs: dependency-only: opaque
By default all native pointers and typedefs are hidden, but you can use the symbol-address
subkey for functions/globals and make them public by matching with its name. The pointers are then accesible via nativeLibrary.addresses
and the native typedef are prefixed with Native_
.
Example -
functions: symbol-address: include: - 'myFunc' - '.*' # Do this to expose all pointers.