diff --git a/book/src/cpp.md b/book/src/cpp.md index 6928584627..f5091b355e 100644 --- a/book/src/cpp.md +++ b/book/src/cpp.md @@ -79,3 +79,81 @@ cannot translate into Rust: large structs in C that would not fit into a register. This also applies to types with any base classes in the MSVC ABI (see [x64 calling convention](https://learn.microsoft.com/en-us/cpp/build/x64-calling-convention?view=msvc-170#return-values)). Because bindgen does not know about these rules generated interfaces using such types are currently invalid. + +## Constructor semantics + +`bindgen` will generate a wrapper for any class constructor declared in the +input headers. For example, this headers file + +```c++ +class MyClass { + public: + MyClass(); + void method(); +}; +``` + +Will produce the following code: +```rust,ignore +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct MyClass { + pub _address: u8, +} +extern "C" { + #[link_name = "\u{1}_ZN7MyClass6methodEv"] + pub fn MyClass_method(this: *mut MyClass); +} +extern "C" { + #[link_name = "\u{1}_ZN7MyClassC1Ev"] + pub fn MyClass_MyClass(this: *mut MyClass); +} +impl MyClass { + #[inline] + pub unsafe fn method(&mut self) { + MyClass_method(self) + } + #[inline] + pub unsafe fn new() -> Self { + let mut __bindgen_tmp = ::std::mem::MaybeUninit::uninit(); + MyClass_MyClass(__bindgen_tmp.as_mut_ptr()); + __bindgen_tmp.assume_init() + } +} +``` +This `MyClass::new` Rust method can be used as a substitute for the `MyClass` +C++ constructor. However, the address of the value from inside the method will +be different than from the outside. This is because the `__bindgen_tmp` value +is moved when the `MyClass::new` method returns. + +In contrast, the C++ constructor will not move the value, meaning that the +address of the value will be the same inside and outside the constructor. +If the original C++ relies on this semantic difference somehow, you should use the +`MyClass_MyClass` binding directly instead of the `MyClass::new` method. + +In other words, the Rust equivalent for the following C++ code + +```c++ +MyClass instance = MyClass(); +instance.method(); +``` + +is not this + +```rust,ignore +let instance = MyClass::new(); +instance.method(); +``` + +but this + +```rust,ignore +let instance = std::mem::MaybeUninit::::uninit(); +MyClass_MyClass(instance.as_mut_ptr()); +instance.assume_init_mut().method(); +``` + +You can easily verify this fact if you provide a implementation for `MyClass` +and `method` that prints the the `this` pointer address. However, you can +ignore this fact if you know that the original C++ code does not rely on the +instance address in its internal logic.