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
struct
to act as your subclass, and add the #crate::subclass
attribute. This adds a field to your struct for autocxx record-keeping. You can instead choose to implementCppSubclass
a different way, in which case you must provide thecrate::subclass
inside yourcrate::include_cpp
macro. (autocxx
will do the required codegen for your subclass whether it discovers acrate::subclass
directive inside yourcrate::include_cpp
, or elsewhere used as an attribute macro, or both.) - Use the
CppSubclass
trait, and instantiate the subclass usingCppSubclass::new_rust_owned
orCppSubclass::new_cpp_owned
constructors. (You can useCppSubclassSelfOwned
if you need that instead; also, seeCppSubclassSelfOwnedDefault
andCppSubclassDefault
to arrange for easier constructors to exist. - You may need to implement
CppPeerConstructor
for 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 impl
s 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++
UniquePtr
held elsewhere in C++. That C++ peer then owns this Rust-side object via a strongRc
reference. This is the ownership relationship set up byCppSubclass::new_cpp_owned
. - The object pair is owned by Rust. Specifically, by a strong
Rc
reference 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_self
is 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
Arc
andMutex
internally rather thanRc
andRefCell
, 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.