Skip to content

Document semantic difference between constructors and wrappers #2385

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 10, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 78 additions & 0 deletions book/src/cpp.md
Original file line number Diff line number Diff line change
Expand Up @@ -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::<MyClass>::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.