Skip to content

Commit 52fda40

Browse files
committed
Document semantic difference between constructors and wrappers
1 parent 8ebeef4 commit 52fda40

File tree

1 file changed

+77
-0
lines changed

1 file changed

+77
-0
lines changed

book/src/cpp.md

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,3 +79,80 @@ cannot translate into Rust:
7979
large structs in C that would not fit into a register. This also applies to types with any base classes
8080
in the MSVC ABI (see [x64 calling convention](https://learn.microsoft.com/en-us/cpp/build/x64-calling-convention?view=msvc-170#return-values)).
8181
Because bindgen does not know about these rules generated interfaces using such types are currently invalid.
82+
83+
## Constructor semantics
84+
85+
`bindgen` will generate a wrapper for any class constructor declared in the input headers. For example, this headers file
86+
87+
```c++
88+
class MyClass {
89+
public:
90+
MyClass();
91+
void method();
92+
};
93+
```
94+
95+
Will produce the following code:
96+
```rust,ignore
97+
#[repr(C)]
98+
#[derive(Debug, Copy, Clone)]
99+
pub struct MyClass {
100+
pub _address: u8,
101+
}
102+
extern "C" {
103+
#[link_name = "\u{1}_ZN7MyClass6methodEv"]
104+
pub fn MyClass_method(this: *mut MyClass);
105+
}
106+
extern "C" {
107+
#[link_name = "\u{1}_ZN7MyClassC1Ev"]
108+
pub fn MyClass_MyClass(this: *mut MyClass);
109+
}
110+
impl MyClass {
111+
#[inline]
112+
pub unsafe fn method(&mut self) {
113+
MyClass_method(self)
114+
}
115+
#[inline]
116+
pub unsafe fn new() -> Self {
117+
let mut __bindgen_tmp = ::std::mem::MaybeUninit::uninit();
118+
MyClass_MyClass(__bindgen_tmp.as_mut_ptr());
119+
__bindgen_tmp.assume_init()
120+
}
121+
}
122+
```
123+
This `MyClass::new` rust method can be used as a substitute for the `MyClass`
124+
C++ constructor. However, the address of the value from inside the method will
125+
be different than from the outside. This is because the `__bindgen_tmp` value
126+
is moved when the `MyClass::new` method returns.
127+
128+
In contrast, the C++ constructor will not move the value, meaning that the
129+
address of the value will be the same inside and outside the constructor.
130+
If the original C++ relies on this semantic difference somehow, you should use the
131+
`MyClass_MyClass` binding directly instead of the `MyClass::new` method.
132+
133+
In other words, the rust equivalent to the following C++ code
134+
135+
```c++
136+
MyClass instance = MyClass();
137+
instance.method();
138+
```
139+
140+
is not this
141+
142+
```rust,ignore
143+
let instance = MyClass::new();
144+
instance.method();
145+
```
146+
147+
but this
148+
149+
```rust,ignore
150+
let instance = std::mem::MaybeUninit::<MyClass>::uninit();
151+
MyClass_MyClass(instance.as_mut_ptr());
152+
instance.assume_init_mut().method();
153+
```
154+
155+
You can easily verify this fact if you provide a implementation for `MyClass`
156+
and `method` that prints the the `this` pointer address. However, you can
157+
ignore this fact if you know that the original C++ code does not rely on the
158+
instance address in its internal logic.

0 commit comments

Comments
 (0)