Trait autocxx::subclass::CppSubclass
source · 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.