autocxx/
subclass.rs

1//! Module to make Rust subclasses of C++ classes. See [`CppSubclass`]
2//! for details.
3
4// Copyright 2021 Google LLC
5//
6// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
7// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
8// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
9// option. This file may not be copied, modified, or distributed
10// except according to those terms.
11
12use std::{
13    cell::RefCell,
14    pin::Pin,
15    rc::{Rc, Weak},
16};
17
18use cxx::{memory::UniquePtrTarget, UniquePtr};
19
20/// Deprecated - use [`subclass`] instead.
21#[deprecated]
22pub use autocxx_macro::subclass as is_subclass;
23
24/// Declare a Rust subclass of a C++ class.
25/// You can use this in two ways:
26/// * As an attribute macro on a struct which is to be a subclass.
27///   In this case, you must specify the superclass as described below.
28///   For instance,
29///   ```nocompile
30///   # use autocxx_macro::subclass as subclass;
31///   #[subclass(superclass("MyCppSuperclass"))]
32///   struct Bar {};
33///   ```
34/// * as a directive within the [crate::include_cpp] macro, in which case you
35///   must provide two arguments of the superclass and then the
36///   subclass:
37///   ```
38///   # use autocxx_macro::include_cpp_impl as include_cpp;
39///   include_cpp!(
40///   #   parse_only!()
41///       #include "input.h"
42///       subclass!("MyCppSuperclass",Bar)
43///       safety!(unsafe)
44///   );
45///   struct Bar {
46///     // ...
47///   }
48///   ```
49///   In this latter case, you'll need to implement the trait
50///   [`CppSubclass`] for the struct, so it's
51///   generally easier to use the former option.
52///
53/// See [`CppSubclass`] for information about the
54/// multiple steps you need to take to be able to make Rust
55/// subclasses of a C++ class.
56pub use autocxx_macro::subclass;
57
58/// A prelude containing all the traits and macros required to create
59/// Rust subclasses of C++ classes. It's recommended that you:
60///
61/// ```rust
62/// use autocxx::subclass::prelude::*;
63/// ```
64pub mod prelude {
65    pub use super::{
66        is_subclass, subclass, CppPeerConstructor, CppSubclass, CppSubclassDefault,
67        CppSubclassRustPeerHolder, CppSubclassSelfOwned, CppSubclassSelfOwnedDefault,
68    };
69}
70
71/// A trait representing the C++ side of a Rust/C++ subclass pair.
72#[doc(hidden)]
73pub trait CppSubclassCppPeer: UniquePtrTarget {
74    fn relinquish_ownership(&self);
75}
76
77/// A type used for how the C++ side of a Rust/C++ subclass pair refers to
78/// the Rust side.
79#[doc(hidden)]
80pub enum CppSubclassRustPeerHolder<T> {
81    Owned(Rc<RefCell<T>>),
82    Unowned(Weak<RefCell<T>>),
83}
84
85impl<T> CppSubclassRustPeerHolder<T> {
86    pub fn get(&self) -> Option<Rc<RefCell<T>>> {
87        match self {
88            CppSubclassRustPeerHolder::Owned(strong) => Some(strong.clone()),
89            CppSubclassRustPeerHolder::Unowned(weak) => weak.upgrade(),
90        }
91    }
92    pub fn relinquish_ownership(self) -> Self {
93        match self {
94            CppSubclassRustPeerHolder::Owned(strong) => {
95                CppSubclassRustPeerHolder::Unowned(Rc::downgrade(&strong))
96            }
97            _ => self,
98        }
99    }
100}
101
102/// A type showing how the Rust side of a Rust/C++ subclass pair refers to
103/// the C++ side.
104#[doc(hidden)]
105#[derive(Default)]
106pub enum CppSubclassCppPeerHolder<CppPeer: CppSubclassCppPeer> {
107    #[default]
108    Empty,
109    Owned(Box<UniquePtr<CppPeer>>),
110    Unowned(*mut CppPeer),
111}
112
113impl<CppPeer: CppSubclassCppPeer> CppSubclassCppPeerHolder<CppPeer> {
114    fn pin_mut(&mut self) -> Pin<&mut CppPeer> {
115        match self {
116            CppSubclassCppPeerHolder::Empty => panic!("Peer not set up"),
117            CppSubclassCppPeerHolder::Owned(peer) => peer.pin_mut(),
118            CppSubclassCppPeerHolder::Unowned(peer) => unsafe {
119                // Safety: guaranteed safe because this is a pointer to a C++ object,
120                // and C++ never moves things in memory.
121                Pin::new_unchecked(peer.as_mut().unwrap())
122            },
123        }
124    }
125    fn get(&self) -> &CppPeer {
126        match self {
127            CppSubclassCppPeerHolder::Empty => panic!("Peer not set up"),
128            CppSubclassCppPeerHolder::Owned(peer) => peer.as_ref(),
129            // Safety: guaranteed safe because this is a pointer to a C++ object,
130            // and C++ never moves things in memory.
131            CppSubclassCppPeerHolder::Unowned(peer) => unsafe { peer.as_ref().unwrap() },
132        }
133    }
134    fn set_owned(&mut self, peer: UniquePtr<CppPeer>) {
135        *self = Self::Owned(Box::new(peer));
136    }
137    fn set_unowned(&mut self, peer: &mut UniquePtr<CppPeer>) {
138        // Safety: guaranteed safe because this is a pointer to a C++ object,
139        // and C++ never moves things in memory.
140        *self = Self::Unowned(unsafe {
141            std::pin::Pin::<&mut CppPeer>::into_inner_unchecked(peer.pin_mut())
142        });
143    }
144}
145
146fn make_owning_peer<CppPeer, PeerConstructor, Subclass, PeerBoxer>(
147    me: Subclass,
148    peer_constructor: PeerConstructor,
149    peer_boxer: PeerBoxer,
150) -> Rc<RefCell<Subclass>>
151where
152    CppPeer: CppSubclassCppPeer,
153    Subclass: CppSubclass<CppPeer>,
154    PeerConstructor:
155        FnOnce(&mut Subclass, CppSubclassRustPeerHolder<Subclass>) -> UniquePtr<CppPeer>,
156    PeerBoxer: FnOnce(Rc<RefCell<Subclass>>) -> CppSubclassRustPeerHolder<Subclass>,
157{
158    let me = Rc::new(RefCell::new(me));
159    let holder = peer_boxer(me.clone());
160    let cpp_side = peer_constructor(&mut me.as_ref().borrow_mut(), holder);
161    me.as_ref()
162        .borrow_mut()
163        .peer_holder_mut()
164        .set_owned(cpp_side);
165    me
166}
167
168/// A trait to be implemented by a subclass which knows how to construct its C++
169/// peer object. Specifically, the implementation here will arrange to call one
170/// or other of the `new` methods to be found on the peer type. If the C++
171/// superclass has a single trivial constructor, then this is implemented
172/// automatically for you. If there are multiple constructors, or a single
173/// constructor which takes parameters, you'll need to implement this trait for
174/// your subclass in order to call the correct constructor.
175pub trait CppPeerConstructor<CppPeer: CppSubclassCppPeer>: Sized {
176    /// Create the C++ peer. This method will be automatically generated
177    /// for you *except* in cases where the superclass has multiple constructors,
178    /// or its only constructor takes parameters. In such a case you'll need to
179    /// implement this by calling a `new` method on the `<my subclass name>Cpp`
180    /// type, passing `peer_holder` as the first argument.
181    fn make_peer(&mut self, peer_holder: CppSubclassRustPeerHolder<Self>) -> UniquePtr<CppPeer>;
182}
183
184/// A subclass of a C++ type.
185///
186/// To create a Rust subclass of a C++ class, you must do these things:
187/// * Create a `struct` to act as your subclass, and add the #[`macro@crate::subclass`] attribute.
188///   This adds a field to your struct for autocxx record-keeping. You can
189///   instead choose to implement [`CppSubclass`] a different way, in which case
190///   you must provide the [`macro@crate::subclass`] inside your [`crate::include_cpp`]
191///   macro. (`autocxx` will do the required codegen for your subclass
192///   whether it discovers a [`macro@crate::subclass`] directive inside your
193///   [`crate::include_cpp`], or elsewhere used as an attribute macro,
194///   or both.)
195/// * Use the [`CppSubclass`] trait, and instantiate the subclass using
196///   [`CppSubclass::new_rust_owned`] or [`CppSubclass::new_cpp_owned`]
197///   constructors. (You can use [`CppSubclassSelfOwned`] if you need that
198///   instead; also, see [`CppSubclassSelfOwnedDefault`] and [`CppSubclassDefault`]
199///   to arrange for easier constructors to exist.
200/// * You _may_ need to implement [`CppPeerConstructor`] for your subclass,
201///   but only if autocxx determines that there are multiple possible superclass
202///   constructors so you need to call one explicitly (or if there's a single
203///   non-trivial superclass constructor.) autocxx will implement this trait
204///   for you if there's no ambiguity and FFI functions are safe to call due to
205///   `autocxx::safety!` being used.
206///
207/// # How to access your Rust structure from outside
208///
209/// Use [`CppSubclass::new_rust_owned`] then use [`std::cell::RefCell::borrow`]
210/// or [`std::cell::RefCell::borrow_mut`] to obtain the underlying Rust struct.
211///
212/// # How to call C++ methods on the subclass
213///
214/// Do the same. You should find that your subclass struct `impl`s all the
215/// C++ methods belonging to the superclass.
216///
217/// # How to implement virtual methods
218///
219/// Simply add an `impl` for the `struct`, implementing the relevant method.
220/// The C++ virtual function call will be redirected to your Rust implementation.
221///
222/// # How _not_ to implement virtual methods
223///
224/// If you don't want to implement a virtual method, don't: the superclass
225/// method will be called instead. Naturally, you must implement any pure virtual
226/// methods.
227///
228/// # How it works
229///
230/// This actually consists of two objects: this object itself and a C++-side
231/// peer. The ownership relationship between those two things can work in three
232/// different ways:
233/// 1. Neither object is owned by Rust. The C++ peer is owned by a C++
234///    [`UniquePtr`] held elsewhere in C++. That C++ peer then owns
235///    this Rust-side object via a strong [`Rc`] reference. This is the
236///    ownership relationship set up by [`CppSubclass::new_cpp_owned`].
237/// 2. The object pair is owned by Rust. Specifically, by a strong
238///    [`Rc`] reference to this Rust-side object. In turn, the Rust-side object
239///    owns the C++-side peer via a [`UniquePtr`]. This is what's set up by
240///    [`CppSubclass::new_rust_owned`]. The C++-side peer _does not_ own the Rust
241///    object; it just has a weak pointer. (Otherwise we'd get a reference
242///    loop and nothing would ever be freed.)
243/// 3. The object pair is self-owned and will stay around forever until
244///    [`CppSubclassSelfOwned::delete_self`] is called. In this case there's a strong reference
245///    from the C++ to the Rust and from the Rust to the C++. This is useful
246///    for cases where the subclass is listening for events, and needs to
247///    stick around until a particular event occurs then delete itself.
248///
249/// # Limitations
250///
251/// * *Re-entrancy*. The main thing to look out for is re-entrancy. If a
252///   (non-const) virtual method is called on your type, which then causes you
253///   to call back into C++, which results in a _second_ call into a (non-const)
254///   virtual method, we will try to create two mutable references to your
255///   subclass which isn't allowed in Rust and will therefore panic.
256///
257///   A future version of autocxx may provide the option of treating all
258///   non-const methods (in C++) as const methods on the Rust side, which will
259///   give the option of using interior mutability ([`std::cell::RefCell`])
260///   for you to safely handle this situation, whilst remaining compatible
261///   with existing C++ interfaces. If you need this, indicate support on
262///   [this issue](https://github.com/google/autocxx/issues/622).
263///
264/// * *Thread safety*. The subclass object is not thread-safe and shouldn't
265///   be passed to different threads in C++. A future version of this code
266///   will give the option to use `Arc` and `Mutex` internally rather than
267///   `Rc` and `RefCell`, solving this problem.
268///
269/// * *Protected methods.* We don't do anything clever here - they're public.
270///
271/// * *Non-trivial class hierarchies*. We don't yet consider virtual methods
272///   on base classes of base classes. This is a temporary limitation,
273///   [see this issue](https://github.com/google/autocxx/issues/610).
274pub trait CppSubclass<CppPeer: CppSubclassCppPeer>: CppPeerConstructor<CppPeer> {
275    /// Return the field which holds the C++ peer object. This is normally
276    /// implemented by the #[`is_subclass`] macro, but you're welcome to
277    /// implement it yourself if you prefer.
278    fn peer_holder(&self) -> &CppSubclassCppPeerHolder<CppPeer>;
279
280    /// Return the field which holds the C++ peer object. This is normally
281    /// implemented by the #[`is_subclass`] macro, but you're welcome to
282    /// implement it yourself if you prefer.
283    fn peer_holder_mut(&mut self) -> &mut CppSubclassCppPeerHolder<CppPeer>;
284
285    /// Return a reference to the C++ part of this object pair.
286    /// This can be used to register listeners, etc.
287    fn peer(&self) -> &CppPeer {
288        self.peer_holder().get()
289    }
290
291    /// Return a mutable reference to the C++ part of this object pair.
292    /// This can be used to register listeners, etc.
293    fn peer_mut(&mut self) -> Pin<&mut CppPeer> {
294        self.peer_holder_mut().pin_mut()
295    }
296
297    /// Creates a new instance of this subclass. This instance is owned by the
298    /// returned [`cxx::UniquePtr`] and thus would typically be returned immediately
299    /// to C++ such that it can be owned on the C++ side.
300    fn new_cpp_owned(me: Self) -> UniquePtr<CppPeer> {
301        let me = Rc::new(RefCell::new(me));
302        let holder = CppSubclassRustPeerHolder::Owned(me.clone());
303        let mut borrowed = me.as_ref().borrow_mut();
304        let mut cpp_side = borrowed.make_peer(holder);
305        borrowed.peer_holder_mut().set_unowned(&mut cpp_side);
306        cpp_side
307    }
308
309    /// Creates a new instance of this subclass. This instance is not owned
310    /// by C++, and therefore will be deleted when it goes out of scope in
311    /// Rust.
312    fn new_rust_owned(me: Self) -> Rc<RefCell<Self>> {
313        make_owning_peer(
314            me,
315            |obj, holder| obj.make_peer(holder),
316            |me| CppSubclassRustPeerHolder::Unowned(Rc::downgrade(&me)),
317        )
318    }
319}
320
321/// Trait to be implemented by subclasses which are self-owned, i.e. not owned
322/// externally by either Rust or C++ code, and thus need the ability to delete
323/// themselves when some virtual function is called.
324pub trait CppSubclassSelfOwned<CppPeer: CppSubclassCppPeer>: CppSubclass<CppPeer> {
325    /// Creates a new instance of this subclass which owns itself.
326    /// This is useful
327    /// for observers (etc.) which self-register to listen to events.
328    /// If an event occurs which would cause this to want to unregister,
329    /// use [`CppSubclassSelfOwned::delete_self`].
330    /// The return value may be useful to register this, etc. but can ultimately
331    /// be discarded without destroying this object.
332    fn new_self_owned(me: Self) -> Rc<RefCell<Self>> {
333        make_owning_peer(
334            me,
335            |obj, holder| obj.make_peer(holder),
336            CppSubclassRustPeerHolder::Owned,
337        )
338    }
339
340    /// Relinquishes ownership from the C++ side. If there are no outstanding
341    /// references from the Rust side, this will result in the destruction
342    /// of this subclass instance.
343    fn delete_self(&self) {
344        self.peer().relinquish_ownership()
345    }
346}
347
348/// Provides default constructors for subclasses which implement `Default`.
349pub trait CppSubclassDefault<CppPeer: CppSubclassCppPeer>: CppSubclass<CppPeer> + Default {
350    /// Create a Rust-owned instance of this subclass, initializing with default values. See
351    /// [`CppSubclass`] for more details of the ownership models available.
352    fn default_rust_owned() -> Rc<RefCell<Self>>;
353
354    /// Create a C++-owned instance of this subclass, initializing with default values. See
355    /// [`CppSubclass`] for more details of the ownership models available.
356    fn default_cpp_owned() -> UniquePtr<CppPeer>;
357}
358
359impl<T, CppPeer> CppSubclassDefault<CppPeer> for T
360where
361    T: CppSubclass<CppPeer> + Default,
362    CppPeer: CppSubclassCppPeer,
363{
364    fn default_rust_owned() -> Rc<RefCell<Self>> {
365        Self::new_rust_owned(Self::default())
366    }
367
368    fn default_cpp_owned() -> UniquePtr<CppPeer> {
369        Self::new_cpp_owned(Self::default())
370    }
371}
372
373/// Provides default constructors for subclasses which implement `Default`
374/// and are self-owning.
375pub trait CppSubclassSelfOwnedDefault<CppPeer: CppSubclassCppPeer>:
376    CppSubclassSelfOwned<CppPeer> + Default
377{
378    /// Create a self-owned instance of this subclass, initializing with default values. See
379    /// [`CppSubclass`] for more details of the ownership models available.
380    fn default_self_owned() -> Rc<RefCell<Self>>;
381}
382
383impl<T, CppPeer> CppSubclassSelfOwnedDefault<CppPeer> for T
384where
385    T: CppSubclassSelfOwned<CppPeer> + Default,
386    CppPeer: CppSubclassCppPeer,
387{
388    fn default_self_owned() -> Rc<RefCell<Self>> {
389        Self::new_self_owned(Self::default())
390    }
391}