wit-bindgen C and C++ Bindings Generator
This tool generates C bindings for a chosen WIT world that are also compatible with C++.
Usage
To generate bindings with this crate, issue the c subcommand to wit-bindgen:
See the output of wit-bindgen help c for available options.
This command will generate either two or three files, depending on command line options:
<world>.h: Header file that declares the available bindings.<world>.c: Source file that implements bindings for imports, helper functions, and wrappers around exported functions.<world>_component_type.o: An object file that contains type information for the world that bindings were generated for. This file is not generated if the--no-object-filecommand line flag is passed.
<world> in each of these is the name of the WIT world, converted to snake case (e.g. i-am-a-component generates i_am_a_component.h)
Generated Bindings
Memory Ownership
In this section, your component refers to the component for which you are generating bindings.
In general, your component is responsible for allocating memory for data it produces and freeing memory for data it consumes. wit-bindgen automatically generates *_free functions for types that require allocation (e.g. strings, lists, records with fields that require allocation) to properly deallocate their memory, including any nested allocated types.
There are additional ownership considerations for string and resource. See the Strings and Resources sections for details.
Ownership rules differ for imported functions and exported functions, so we'll tackle these in turn.
Imported functions
Imported functions are functions that are implemented in another component and that your component calls.
Ownership rules:
- Arguments: Your component is responsible for allocating memory for the input arguments and freeing this memory when it is no longer required. Your input data will not be modified or freed when you call the imported function; the data will be copied into the linear memory of the component that implements the function.
- Return values: Your component receives ownership of data returned by imported functions and is responsible for freeing it.
Here is an example of an imported function that requires allocations for both input parameters and the return value:
package cat:registry;
interface cat-registry-api {
record cat {
name: string,
nicknames: list<string>,
}
get-cat-by-name: func(name: string) -> option<cat>;
}
world cat-registry-user {
import cat-registry-api;
export run: func();
}
Here is a possible implementation of the run export that calls get-cat-by-name with Poptart as an argument without leaking memory:
void
Note that we free the memory allocated for the import's argument as well as its return value.
Exported functions
Exported functions are functions that your component implements and makes available to other components.
Ownership rules:
- Arguments: Your component receives ownership of all arguments passed to the exported function and is responsible for freeing them.
- Return values: Your component is responsible for allocating the memory for return values. It is not responsible for freeing them:
wit-bindgenautomatically generates cleanup functions named*_post_returnwhich will be called by the component who imports your function. This generated function assumes that allstrings andlists in the return value are dynamically allocated. - Resource borrows: There are special requirements for cleaning up borrows of imported resources received as parameters; see the Imported resources section for more details.
Here is an example of an exported function that requires allocations for both input parameters and the return value:
package cat:registry;
interface cat-registry-api {
record cat {
name: string,
nicknames: list<string>,
}
get-cat-by-name: func(name: string) -> option<cat>;
}
world cat-registry {
export cat-registry-api;
}
Here is a possible implementation of the get-cat-by-name export that correctly manages the memory of the parameter and return value:
bool
Importantly, the generated *_post_return function that frees the return value assumes that lists and strings in the return value reference dynamically-allocated memory that can be freed with free. As such, if you call *_string_set with a string literal to set one of the fields of the return value, the bindings will try to free the string literal. Unless you want to replace the generated *_post_return function with a version that only frees some of the data, always dynamically allocate the contents of your strings and lists in your return values. The *_post_return functions are defined as weak symbols so that you can simply define your own versions to override the defaults and they will be selected by the linker.
Type Mappings
Primitive types
| WIT Type | C Type |
|---|---|
bool |
bool |
u8 |
uint8_t |
u16 |
uint16_t |
u32 |
uint32_t |
u64 |
uint64_t |
s8 |
int8_t |
s16 |
int16_t |
s32 |
int32_t |
s64 |
int64_t |
f32 |
float |
f64 |
double |
char |
uint32_t |
Note that uint32_t is used to represent WIT's char type because WIT chars are Unicode Scalar Values.
Strings
The representation of strings depends on the --string-encoding command line flag. The two supported options are utf8 and utf16. In both cases, they are represented by a pointer to the string data along with their length in Unicode code units:
// UTF-8 version
typedef struct my_world_string_t my_world_string_t;
// UTF-16 version
typedef struct my_world_string_t my_world_string_t;
Alongside the string type, the following functions will be defined (assuming UTF-8 strings):
// Sets the string `ret` to reference the input string `s` without copying it
void ;
// Creates a copy of the input nul-terminated string `s` and
// stores it into the component model string `ret`.
void ;
// Deallocates the string pointed to by `ret`, deallocating
// the memory behind the string.
void ;
For UTF-16 strings, those char8_t*s become char16_t*s and the following function is also supplied:
// Returns the length of the UTF-16 string `s` in code units
size_t ;
If a component calls *_string_set, the component is responsible for freeing the string if it was dynamically allocated. For example:
my_world_string_t static_string;
;
// Use static_string
// ...
// Do *not* call my_world_string_free(&static_string);
my_world_string_t dynamic_string;
char8_t* my_string = ; // calls malloc internally
;
// Use dynamic_string
// ...
// Free the memory
;
Lists
WIT lists are represented as structures containing a pointer and length:
// list<u8>
typedef struct my_world_list_u8_t my_world_list_u8_t;
A helper function is generated for freeing lists:
// Frees the memory associated with the list
void ;
Variants
WIT variants are represented as tagged unions:
// variant cat-toy { ball, teddy(string), wand }
typedef struct my_world_cat_toy_t my_world_cat_toy_t;
A helper function is generated for freeing variants:
void ;
Results
results are specified with a union and is_err field:
// result<string, u32>
typedef struct my_world_result_string_u32_t;
If the --no-sig-flattening command line argument is passed, then bindings for functions that return result<T,E> take a single out parameter, which is consistent with other return types. Otherwise, the return value is flattened by taking out parameters for T and E and returning !is_err as a boolean result. For example:
package my:example;
interface string-getter {
type error = u32;
get-string-by-index: func(index: u32) -> result<string, error>;
}
Depending on the command line options passed, this will generate one of these two functions:
// --no-sig-flattening passed, the usual single out parameter is used
extern void
;
// Flag not passed, the return value is flattened
extern bool
;
A helper function is generated for freeing results:
void ;
Options
option<T>s are specified with an is_some boolean member that discriminates whether a T value is contained:
// option<string>
typedef struct my_world_option_string_t my_world_option_string_t;
If the --no-sig-flattening command line argument is passed, then bindings for functions that return option<T> take a single out parameter, which is consistent with other return types. Otherwise, the return value is flattened by taking an out parameter for T and returning is_some as a boolean result. For example:
package my:example;
interface string-getter {
get-string-by-index: func(index: u32) -> option<string>;
}
Depending on the command line options passed, this will generate one of these two functions:
// --no-sig-flattening passed, the usual single out parameter is used
extern void ;
// Flag not passed, the return value is flattened
extern bool ;
A helper function is generated for freeing options:
void ;
Enums and Flags
Enums are mapped to typedefs for a sufficiently-wide integer backing type:
// enum cat-breed { persian, siamese, ragdoll }
typedef uint8_t my_world_cat_breed_t;
Flags are similarly defined with bitfield constants:
// flags cat-flags { is-fluffy, has-been-fed, is-sleepy }
typedef uint8_t my_world_cat_flags_t;
Resources
WIT resources are represented differently for imports and exports, and each have memory ownership constraints that you must be mindful of.
The following WIT definition will be used in the following examples:
package cat:example;
// A cat registry that defines an opaque resource
interface registry-api {
// The exporter of this resource will define how the cat is represented
resource cat {
get-name: func() -> string;
get-nicknames: func() -> list<string>;
}
// Various functions that will demonstrate different uses of the exported resource
adopt-cat: func(name: string) -> option<cat>;
notify-adopted-cat-is-happy: func(cat: borrow<cat>);
enroll-as-therapy-cat: func(cat: cat);
// Setup and teardown functions for the registry, which will be called
// by the adopter
init: func();
destroy: func();
}
// An adoption authority that adopters must notify when they adopt a cat
interface adoption-authority-api {
use registry-api.{cat};
// This will demonstrate exporting functions that borrow an imported resource
notify-adoption: func(cat: borrow<cat>);
}
// The adoption authority imports the registry API to use the cat resource
world adoption-authority {
import registry-api;
export adoption-authority-api;
}
world registry {
export registry-api;
}
// The adopter is the main component in this example.
// It is responsible for initializing the registry,
// adopting cats, and notifying the adoption authority.
world adopter {
import adoption-authority-api;
import registry-api;
// Entry point that can be called from the command line
export wasi:cli/run@0.2.6;
}
Imported resources
Imported resources are represented as opaque handles with helper functions for resource management.
The following bindings will be generated for imports of cat:
// An owning handle to a cat
typedef struct cat_example_registry_api_own_cat_t cat_example_registry_api_own_cat_t;
// A borrowing handle to a cat
typedef struct cat_example_registry_api_borrow_cat_t cat_example_registry_api_borrow_cat_t;
// Functions to call the methods of the cat resource
extern void
;
extern void
;
// Drop an owning handle to a cat
extern void
;
// Drop a borrowing handle to a cat
// Only generated if autodropping borrows is turned off (described below)
extern void
;
// Retrieve a borrowing handle to a cat from an owning handle
extern cat_example_registry_api_borrow_cat_t
;
A component may borrow or own an imported resource. This is communicated in WIT by whether the resource type is wrapped in borrow<...> or not. The importing component is responsible for dropping any owning references to imported resources using the generated *_drop_own function.
The importing component's responsibility for dropping borrows of imported resources depends on the command line arguments passed to wit-bindgen and whether it obtained the borrow by calling a generated *_borrow_* function on an owned resource, or by receiving it as an argument to one of its exported functions. The relevant cases are:
- If the borrow was passed as an argument to one of the importing component's exports:
- If the bindings were generated with
--autodrop-borrows=yes, then borrowed handles will be automatically dropped when the exported function returns; no additional action is required by importing component - If the bindings were generated with
--autodrop-borrow=no, or this command line option was not supplied, then the importing component must drop the borrowed handle by calling the generated*_drop_borrowfunction.
- If the bindings were generated with
- If the component obtained the borrow by calling
*_borrow*_on an owned resource, it must not call*_drop_borrow
Here is a potential implementation of the run export that correctly handles the lifetime of the owned cat resource retrieved from get-cat-by-name:
bool
A correct implementation of notify-adoption depends on whether autodropping borrows is enabled. Here is a possible implementation for both cases:
// Autodropping borrows enabled
void
// Autodropping borrows disabled
void
Exported resources
Exported resources are represented by an opaque handle to a custom internal representation defined by the component.
The following bindings will be generated for exports of cat:
// An owning opaque handle for a cat
typedef struct exports_cat_example_registry_api_own_cat_t exports_cat_example_registry_api_own_cat_t;
// The internal representation of the resource - must be implemented by the exporting component
typedef struct exports_cat_example_registry_api_cat_t
exports_cat_example_registry_api_cat_t;
// Borrows are represented as a pointer to the internal representation rather than an opaque handle
typedef exports_cat_example_registry_api_cat_t*
exports_cat_example_registry_api_borrow_cat_t;
// Declarations for the resource methods that the exporting component must implement
void ;
void ;
// Drop an owning handle to a cat
extern void
;
// Create an owning opaque handle to a cat from an internal representation
extern exports_cat_example_registry_api_own_cat_t
;
// Get the internal representation of a cat from an owning opaque handle
extern exports_cat_example_registry_api_cat_t*
;
// Destroy a cat resource - must be implemented by the exporting component
void ;
Here are possible implementations for the functions and type that must be implemented by the component:
;
// Custom destructor that frees the memory of a cat resource
void
// Implementation for the get-name method
void
// Implementation for the get-nicknames method
void
A component may borrow or own an exported resource. This is communicated in WIT by whether the resource type is wrapped in borrow<...> or not. The exporting component is responsible for dropping any owning references to resources that it creates through calls to *_new using the generated *_drop_own function. Components must not drop borrowing handles to resources that they export.
The implementations of the destructor and the cat methods would be the same as the above example. Here are possible implementations of the other functions:
// Scary scary global state
// This tracks all the cats that are registered,
// and will be filled in by the `init` function.
exports_cat_example_registry_api_own_cat_t *g_cats;
size_t g_cat_count = 0;
const size_t MAX_CAT_COUNT = 32;
bool
void
void
void
void
C++ compatibilitity
All types and functions are wrapped in the following block, which gives the symbols C linkage when compiling in C++ mode:
extern "C" }