cbindgen
cbindgen copied to clipboard
Support generating opaque type with correct size and alignment for non-`#[repr(C)]` types
We talked about this a bit at the SF all-hands. If we use '-Z print-type-sizes' we can figure out the size of a non-repr(C) type and make an opaque C type of the same size and alignment.
@Gankro is going to explore what a stable version of '-Z print-type-sizes' would be.
Yeah that should be do-able. I'll try and prototype something sometime.
I have prepared a proposal that I'll be submitting to internals.rust-lang.org
https://gist.github.com/Gankro/b4aa176f4db24671be66f2a3c57e487b
That proposal seems reasonable from a cbindgen perspective,
If we're attempting to provide fields equivalent to repr(rust) we'll need to parse $type_name with syn. But that shouldn't be a problem.
The proposal seems good. I think we need to come up with some more motivating examples. I tried to do this last night but struggled a bit. I'm going to think more.
I think it's good enough to kick off discussion; posted here: https://internals.rust-lang.org/t/stabilizing-a-machine-readable-zprint-type-sizes/5525
Gentle ping - we would very much appreciate this in wgpu.
I am no longer of the opinion that it's a good idea to use things like -Zprint-type-sizes or other such hacks. The original premise of this bug is flawed, you need detailed information on the shape of the struct, as I discuss here:
https://gankra.github.io/blah/rust-layouts-and-abis/#abi
@kvark why is this useful?
@emilio if I understand correctly, it would allow us to put opaque (from C++ point of view) types (by value) into repr(C) structs that C++ passes around (from Rust to Rust).
So you want to put non-repr(C) stuff inside repr(C) structs and pass those by value?
That sounds to me like, unless those types are very simple, it could go wrong (if they're supposed to have destructors and such)... Do you have any concrete use case? But anyhow given rustc's type layout is basically undefined, and cbindgen doesn't hook into rustc, this is not quite easy.
#[repr(C)]
struct Foo {
map: HashMap<i32, i32>,
}
extern "C" fn foo_set(foo: &mut Foo, key: i32, value: i32) {
foo.map.insert(key, value);
}
extern "C" fn foo_finalize(foo: Foo) {
println!("{:?}", foo);
// destructor is called here
}
So the only thing that it potentially allows you is to avoid a heap allocation, right?
More specifically, allows to avoid indirection on every access.
I am strongly interested in this feature as well. Specifically we would like to write code like this:
pub struct Foo { .. }
extern "C" unsafe fn foo_new(ptr: *mut Foo, data: &Bar) {
core::ptr::write(ptr, Foo::new(data));
}
extern "C" fn foo_do_stuff(foo: &mut Foo, ..) { .. }
Not only it will be more efficient (since users will be able to use stack-allocated Foo), but also will allow us to make our library completely allocation-free.
Possibly relevant, I did something like this in https://verdagon.dev/blog/exploring-seamless-rust-interop-part-1
The tricky parts:
#[repr(align(n))]only works if there's analigned_nentry in the cbindgen.toml- We needed a MaybeUninit<T> to avoid some undefined behavior when we transmuted something into the opaque type.
- Getting the right size for the opaque type required running a program first that prints it out.