pub trait CppSubclass<CppPeer: CppSubclassCppPeer>: CppPeerConstructor<CppPeer> {
// Required methods
fn peer_holder(&self) -> &CppSubclassCppPeerHolder<CppPeer>;
fn peer_holder_mut(&mut self) -> &mut CppSubclassCppPeerHolder<CppPeer>;
// Provided methods
fn peer(&self) -> &CppPeer { ... }
fn peer_mut(&mut self) -> Pin<&mut CppPeer> { ... }
fn new_cpp_owned(me: Self) -> UniquePtr<CppPeer> ⓘ { ... }
fn new_rust_owned(me: Self) -> Rc<RefCell<Self>> { ... }
}Expand description
A subclass of a C++ type.
To create a Rust subclass of a C++ class, you must do these things:
- Create a
structto act as your subclass, and add the #crate::subclassattribute. This adds a field to your struct for autocxx record-keeping. You can instead choose to implementCppSubclassa different way, in which case you must provide thecrate::subclassinside yourcrate::include_cppmacro. (autocxxwill do the required codegen for your subclass whether it discovers acrate::subclassdirective inside yourcrate::include_cpp, or elsewhere used as an attribute macro, or both.) - Use the
CppSubclasstrait, and instantiate the subclass usingCppSubclass::new_rust_ownedorCppSubclass::new_cpp_ownedconstructors. (You can useCppSubclassSelfOwnedif you need that instead; also, seeCppSubclassSelfOwnedDefaultandCppSubclassDefaultto arrange for easier constructors to exist. - You may need to implement
CppPeerConstructorfor your subclass, but only if autocxx determines that there are multiple possible superclass constructors so you need to call one explicitly (or if there’s a single non-trivial superclass constructor.) autocxx will implement this trait for you if there’s no ambiguity and FFI functions are safe to call due toautocxx::safety!being used.
§How to access your Rust structure from outside
Use CppSubclass::new_rust_owned then use std::cell::RefCell::borrow
or std::cell::RefCell::borrow_mut to obtain the underlying Rust struct.
§How to call C++ methods on the subclass
Do the same. You should find that your subclass struct impls all the
C++ methods belonging to the superclass.
§How to implement virtual methods
Simply add an impl for the struct, implementing the relevant method.
The C++ virtual function call will be redirected to your Rust implementation.
§How not to implement virtual methods
If you don’t want to implement a virtual method, don’t: the superclass method will be called instead. Naturally, you must implement any pure virtual methods.
§How it works
This actually consists of two objects: this object itself and a C++-side peer. The ownership relationship between those two things can work in three different ways:
- Neither object is owned by Rust. The C++ peer is owned by a C++
UniquePtrheld elsewhere in C++. That C++ peer then owns this Rust-side object via a strongRcreference. This is the ownership relationship set up byCppSubclass::new_cpp_owned. - The object pair is owned by Rust. Specifically, by a strong
Rcreference to this Rust-side object. In turn, the Rust-side object owns the C++-side peer via aUniquePtr. This is what’s set up byCppSubclass::new_rust_owned. The C++-side peer does not own the Rust object; it just has a weak pointer. (Otherwise we’d get a reference loop and nothing would ever be freed.) - The object pair is self-owned and will stay around forever until
CppSubclassSelfOwned::delete_selfis called. In this case there’s a strong reference from the C++ to the Rust and from the Rust to the C++. This is useful for cases where the subclass is listening for events, and needs to stick around until a particular event occurs then delete itself.
§Limitations
-
Re-entrancy. The main thing to look out for is re-entrancy. If a (non-const) virtual method is called on your type, which then causes you to call back into C++, which results in a second call into a (non-const) virtual method, we will try to create two mutable references to your subclass which isn’t allowed in Rust and will therefore panic.
A future version of autocxx may provide the option of treating all non-const methods (in C++) as const methods on the Rust side, which will give the option of using interior mutability (
std::cell::RefCell) for you to safely handle this situation, whilst remaining compatible with existing C++ interfaces. If you need this, indicate support on this issue. -
Thread safety. The subclass object is not thread-safe and shouldn’t be passed to different threads in C++. A future version of this code will give the option to use
ArcandMutexinternally rather thanRcandRefCell, solving this problem. -
Protected methods. We don’t do anything clever here - they’re public.
-
Non-trivial class hierarchies. We don’t yet consider virtual methods on base classes of base classes. This is a temporary limitation, see this issue.
Required Methods§
Sourcefn peer_holder(&self) -> &CppSubclassCppPeerHolder<CppPeer>
fn peer_holder(&self) -> &CppSubclassCppPeerHolder<CppPeer>
Return the field which holds the C++ peer object. This is normally
implemented by the #is_subclass macro, but you’re welcome to
implement it yourself if you prefer.
Sourcefn peer_holder_mut(&mut self) -> &mut CppSubclassCppPeerHolder<CppPeer>
fn peer_holder_mut(&mut self) -> &mut CppSubclassCppPeerHolder<CppPeer>
Return the field which holds the C++ peer object. This is normally
implemented by the #is_subclass macro, but you’re welcome to
implement it yourself if you prefer.
Provided Methods§
Sourcefn peer(&self) -> &CppPeer
fn peer(&self) -> &CppPeer
Return a reference to the C++ part of this object pair. This can be used to register listeners, etc.
Sourcefn peer_mut(&mut self) -> Pin<&mut CppPeer>
fn peer_mut(&mut self) -> Pin<&mut CppPeer>
Return a mutable reference to the C++ part of this object pair. This can be used to register listeners, etc.
Sourcefn new_cpp_owned(me: Self) -> UniquePtr<CppPeer> ⓘ
fn new_cpp_owned(me: Self) -> UniquePtr<CppPeer> ⓘ
Creates a new instance of this subclass. This instance is owned by the
returned cxx::UniquePtr and thus would typically be returned immediately
to C++ such that it can be owned on the C++ side.
Sourcefn new_rust_owned(me: Self) -> Rc<RefCell<Self>>
fn new_rust_owned(me: Self) -> Rc<RefCell<Self>>
Creates a new instance of this subclass. This instance is not owned by C++, and therefore will be deleted when it goes out of scope in Rust.
Dyn Compatibility§
This trait is not dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety", so this trait is not object safe.