Today, cbindgen has special-case handling for certain well-known standard types. For example:
NonNull<T> is erased as T*
Option<NonNull<T>> is erased as T*
Option<fn(i32) -> i64> is erased as int64_t (*)(int32_t)
Box<T> is erased as T* (but not in C++)
This is safe because of the semantics of those types (they somehow act transparent, even tho most of them are not actually #[repr(transparent)]).
Meanwhile, cbindgen supports user-defined #[repr(transparent)] structs by erasing them to typedefs:
#[repr(transparent)]
struct Foo(i32)
is (partly) erased to:
typedef int32_t Foo
However, typedefs (whether user-specified or replacing transparent structs) do not mix cleanly with special-case handling. So, for example, the following all cause cbindgen to emit opaque struct definitions for well-known types instead of optimizing them away correctly:
#[repr(transparent)]
struct Foo(NonNull<i32>);
type NullableFoo = Option<Foo>;
type Function = extern "C" fn(i: i32) -> i64;
type NullableFunction = Option<Function>;
This happens because simplify_standard_types only works for... well-known standard types. Users have no way to opt into similar semantics for their own typedefs and transparent structs.
One possible solution (prototyped as https://github.com/mozilla/cbindgen/pull/966) is to introduce /// cbindgen:transparent-typedef annotation that causes cbindgen to replace transparent structs and typedefs with their underlying type. This allows the following:
/// cbindgen:transparent-typedef
#[repr(transparent)]
struct Foo(NonNull<i32>);
/// cbindgen:transparent-typedef
type Function = extern "C" fn(i: i32) -> i64;
type NullableFoo = Option<Foo>;
type NullableFunction = Option<Function>;
template<typename T = void>
struct Option;
using Foo = int32_t*;
using Function = int64_t(*)(int32_t i);
using NullableFoo = Option<Foo>;
using NullableFunction = Option<Function>;
Today, cbindgen has special-case handling for certain well-known standard types. For example:
NonNull<T>
is erased asT*
Option<NonNull<T>>
is erased asT*
Option<fn(i32) -> i64>
is erased asint64_t (*)(int32_t)
Box<T>
is erased asT*
(but not in C++)This is safe because of the semantics of those types (they somehow act transparent, even tho most of them are not actually
#[repr(transparent)]
).Meanwhile, cbindgen supports user-defined
#[repr(transparent)]
structs by erasing them to typedefs:is (partly) erased to:
However, typedefs (whether user-specified or replacing transparent structs) do not mix cleanly with special-case handling. So, for example, the following all cause cbindgen to emit opaque struct definitions for well-known types instead of optimizing them away correctly:
This happens because
simplify_standard_types
only works for... well-known standard types. Users have no way to opt into similar semantics for their own typedefs and transparent structs.One possible solution (prototyped as https://github.com/mozilla/cbindgen/pull/966) is to introduce
/// cbindgen:transparent-typedef
annotation that causes cbindgen to replace transparent structs and typedefs with their underlying type. This allows the following:to export as
instead of today's output: