Open derchr opened 3 years ago
Seems reasonable to me. Part of the issue is that Box<dyn Trait>
is not the same size as a real pointer, so you're double-boxing which seems unfortunate.
I think the hard part would be to know what to generate. MyTrait**
isn't ideal, because it allows C to have a MyTrait*
by dereferencing the pointer.
Something that should already work would be something like:
pub struct MyStruct(Box<dyn MyTrait>);
extern "C" fn MyStruct_Create(...) -> Box<MyStruct> { ... }
And should also be nicer to work with shouldn't it?
Although Rust's ABI for &dyn Trait
isn't stable (yet?), CBindGen could allow users to generate C++ code for Rust traits if they activate a flag.
I put together a little example project showing how this could work. For this Rust code
pub trait Foo {
extern "C" fn foo(&self);
}
#[no_mangle]
pub extern "C" fn get_foo() -> &'dyn Foo {
// ...
}
we could generate this C++ header:
struct FooObj;
struct FooFns {
VTable metadata;
void (* const foo)(const FooObj *self);
};
struct FooDyn: Dyn<FooObj, FooFns> {
void foo() const { fns->foo(self); }
};
FooDyn get_foo();
To keep the C++ code generated for a specific trait compact, I moved a bit of boilerplate shared by all such generated code to two helper classes. They could be in a C++ header file provided by CBindGen. (Of course, they could also be generated into each header file, if that's preferred.)
struct VTable {
void (* const drop_in_place)(void*);
const uintptr_t size_of;
const uintptr_t align_of;
};
template<class Obj, class Fns>
struct Dyn {
Obj* const self;
Fns* const fns;
};
Rust's &dyn Trait
values are fat pointers consisting of a pointer to the actual object (a struct) and a pointer to the vtable. The vtable in turn consist of a bit of metadata and the function pointers. The C++ classes shown above mirror these data structures. (The VTable
struct above was actually generated by CBindGen from this Rust struct.)
The struct FooObj
type is just a marker that adds a bit of type safety. (We could use void*
instead if we want to generate less code.)
The list of typed function pointers in the vtable is generated into struct FooFns
.
The method definitions in struct FooDyn
are sweet syntactic sugar: we could omit them and use e.g. FooDyn dyn = get_foo(); dyn.fns->foo(dyn.self)
, but get_foo().foo()
is much more readable.
Note that we don't use any virtual methods in C++. We don't want the C++ compiler to generate vtables – the Rust compiler already did that.
Of course, Rust may change its ABI any time (for example, they might add more metadata fields), but I think the basic structure will remain the same, and adapting CBindGen to such changes shouldn't be too much work.
Users will have to re-generate their C++ headers if they want to interface with a Rust version with a different ABI. I guess that would be acceptable for many users.
A bigger problem may be that some users may want to support multiple Rust versions with the same C++ code. I've got rough ideas how CBindGen could help with that, but nothing specific. Depends on how Rust changes its ABI.
Maybe the name of the flag that activates this feature should contain something scary like unstable
. Or maybe telling users about the dangers is enough.
Also, it would be nice if we could somehow (with attr?) make cbindgen proceed anyway. For example I have a type:
#[repr(transparent)]
pub struct ObjId<T: ?Sized>(c_uint, PhantomData<T>);
which should be perfectly safe to use with dyn X
because it's just an index. Right now I'm resorting to a hack described in
https://github.com/eqrion/cbindgen/issues/385
While it's already possible with some hacks, it would be nice to support them properly.
I understand that it is not possible to add support for fat pointers like
&dyn Trait
as it's ABI is not stable. But it should be possible to support pointers to fat pointers.Code like this:
Should generate a c-Header like this:
Currently this will not work for two reasons:
dyn
and live with the compiler warning (or adding#[allow(bare_trait_objects)]
)I was inspired to open a feature request after watching this video.