rust-lang / rust-bindgen

Automatically generates Rust FFI bindings to C (and some C++) libraries.
https://rust-lang.github.io/rust-bindgen/
BSD 3-Clause "New" or "Revised" License
4.5k stars 702 forks source link

C++ class method pointer treated as C function pointer #1894

Open ianrrees opened 4 years ago

ianrrees commented 4 years ago

Input C/C++ Header

class Thing {
    void (Thing::*method_ptr)(void);
};

Bindgen Invocation

$ bindgen header.hpp -o src/bindings.rs

Actual Results

#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct Thing {
    pub method_ptr: ::std::option::Option<unsafe extern "C" fn()>,
}

Expected Results

From C++, sizeof(Thing::method_ptr) == 16, however from Rust it's 8 (which appropriately causes the generated test for the size of Thing to fail). I'd expect either to get an error on binding generation if this isn't supported, or a generated method_ptr field with the same size as the C++ equivalent. This is with bindgen 0.55.1 on x86-64.

emilio commented 4 years ago

Yup, this is clearly a bug. I'm not sure how easy to fix this is though.

My guess is that we're going through here:

https://github.com/rust-lang/rust-bindgen/blob/87b2bc033f35234d292d41ee2ba165516345dd18/src/ir/ty.rs#L834-L835

Which is already in the hacky territory. We need to look at whether libclang exposes whether this is a method pointer or a function pointer to at least error sanely. If they don't then we need to go fix that first.

If we want to generate something sane, then we need to figure out what is the actual representation in memory of method pointers... At least on Itanium, it is described here: http://itanium-cxx-abi.github.io/cxx-abi/abi.html#member-pointers

It's too late right now for me to dig when the adj parameter is relevant (probably only vcalls of base classes?), but it seems doable to make it callable. We'd also need to check whether MSVC uses the same representation though.

ianrrees commented 4 years ago

Thanks for the quick response @emilio !

I should've added in the original ticket: the workaround I'm using is to mark method_ptr as an opaque type. In my use case, I don't actually need to access that member from Rust, just need it to have the right size to preserve alignment later in the class.