FFIgen supports regexp-based renaming. The regexp must be a full match.
For renaming you can use regexp groups ($1 means group 1).
To renaming clang_dispose_string to string_dispose we 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 behavior 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*.
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'Native enums are, by default, generated into Dart enums with int get value and
fromValue(int). This works well in the case that your enums values are known
in advance and not going to change, and in return, you get the full benefits of
Dart enums like exhaustiveness checking.
However, if a native library adds another possible enum value after you generate
your bindings, and this new value is passed to your Dart code, this will result
in an ArgumentError at runtime. To fix this, you can regenerate the bindings
on the new header file, but if you wish to avoid this issue entirely, you can
tell FFIgen to generate plain Dart integers for your enum instead. To do this,
simply list your enum's name in the as-int section of your FFIgen config:
enums:
as-int:
include:
- MyIntegerEnum
- '*IntegerEnum'
exclude:
- FakeIntegerEnumFunctions that accept or return these enums will now accept or return integers instead, and it will be up to your code to map integer values to behavior and handle invalid values. But your code will be future-proof against new additions to the enums.
This happens when an excluded struct/union 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
unions:
dependency-only: opaqueBy default, the native pointers are private, but you can use the
symbol-address subkey for functions/globals and make them public by matching
with its name. The pointers are then accessible via nativeLibrary.addresses.
Example:
functions:
symbol-address:
include:
- 'myFunc' # Match function name.
- '.*' # Do this to expose all function pointers.
exclude: # If you only use exclude, then everything not excluded is generated.
- 'dispose'By default, these types are inline. But you can use the expose-typedef subkey
for functions to generate them. This will expose the Native and Dart type.
E.g. for a function named hello the generated typedefs are named as
NativeHello and DartHello.
Example:
functions:
expose-typedefs:
include:
- 'myFunc' # Match function name.
- '.*' # Do this to expose types for all functions.
exclude: # If you only use exclude, then everything not excluded is generated.
- 'dispose'Named declarations use their own names even when inside another typedef. However, unnamed declarations inside typedefs take the name of the first typedef that refers to them.
The following typedefs are not generated:
- They are not referred to anywhere in the included declarations.
- They refer to a struct/union having the same name as itself.
- They refer to a boolean, enum, inline array, Handle or any unsupported type.
FFIgen uses clang's own compiler frontend to parse and traverse the C
header files. FFIgen expands the macros using clang's macro expansion and
then traverses the expanded code. To do this, FFIgen generates temporary files
in a system tmp directory.
A custom temporary directory can be specified by setting the TEST_TMPDIR
environment variable.
FFIgen can sometimes generate a lot of logs, especially when it's parsing a lot of code.
SEVERElogs are something you definitely need to address. They can be caused due to syntax errors, or more generally missing header files (which need to be specified usingcompiler-optsin config).WARNINGlogs are something you can ignore, but should probably look into. These are mostly indications of declarations FFIgen couldn't generate due to limitations ofdart:ffi, private declarations (which can be resolved by renaming them via FFIgen's config) or other minor issues in the config file itself.- Everything else can be safely ignored. Its purpose is to simply let you know what FFIgen is doing.
- The verbosity of the logs can be changed by adding a flag with
the log level, e.g.
dart run ffigen --verbose <level>. Level options are[all, fine, info (default), warning, severe]. Theallandfinewill print a ton of logs are meant for debugging purposes only.
FFIgen can share type definitions using symbol files.
- A package can generate a symbol file using the
output.symbol-fileconfig. - And another package can then import this, using
import.symbol-filesconfig. - Doing so will reuse all the types such as Struct/Unions, and will automatically exclude generating other types (E.g. functions, enums, macros).
Checkout examples/shared_bindings for details.
For manually reusing definitions from another package, the library-imports
and type-map config can be used.