blob: 6c6ee310cc5df7b3b5d8cb01335b5ee2793057d4 [file] [log] [blame]
Brian Silverman4e662aa2022-05-11 23:10:19 -07001//! 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 [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)]
105pub enum CppSubclassCppPeerHolder<CppPeer: CppSubclassCppPeer> {
106 Empty,
107 Owned(Box<UniquePtr<CppPeer>>),
108 Unowned(*mut CppPeer),
109}
110
111impl<CppPeer: CppSubclassCppPeer> Default for CppSubclassCppPeerHolder<CppPeer> {
112 fn default() -> Self {
113 CppSubclassCppPeerHolder::Empty
114 }
115}
116
117impl<CppPeer: CppSubclassCppPeer> CppSubclassCppPeerHolder<CppPeer> {
118 fn pin_mut(&mut self) -> Pin<&mut CppPeer> {
119 match self {
120 CppSubclassCppPeerHolder::Empty => panic!("Peer not set up"),
121 CppSubclassCppPeerHolder::Owned(peer) => peer.pin_mut(),
122 CppSubclassCppPeerHolder::Unowned(peer) => unsafe {
123 // Safety: guaranteed safe because this is a pointer to a C++ object,
124 // and C++ never moves things in memory.
125 Pin::new_unchecked(peer.as_mut().unwrap())
126 },
127 }
128 }
129 fn get(&self) -> &CppPeer {
130 match self {
131 CppSubclassCppPeerHolder::Empty => panic!("Peer not set up"),
132 CppSubclassCppPeerHolder::Owned(peer) => peer.as_ref(),
133 // Safety: guaranteed safe because this is a pointer to a C++ object,
134 // and C++ never moves things in memory.
135 CppSubclassCppPeerHolder::Unowned(peer) => unsafe { peer.as_ref().unwrap() },
136 }
137 }
138 fn set_owned(&mut self, peer: UniquePtr<CppPeer>) {
139 *self = Self::Owned(Box::new(peer));
140 }
141 fn set_unowned(&mut self, peer: &mut UniquePtr<CppPeer>) {
142 // Safety: guaranteed safe because this is a pointer to a C++ object,
143 // and C++ never moves things in memory.
144 *self = Self::Unowned(unsafe {
145 std::pin::Pin::<&mut CppPeer>::into_inner_unchecked(peer.pin_mut())
146 });
147 }
148}
149
150fn make_owning_peer<CppPeer, PeerConstructor, Subclass, PeerBoxer>(
151 me: Subclass,
152 peer_constructor: PeerConstructor,
153 peer_boxer: PeerBoxer,
154) -> Rc<RefCell<Subclass>>
155where
156 CppPeer: CppSubclassCppPeer,
157 Subclass: CppSubclass<CppPeer>,
158 PeerConstructor:
159 FnOnce(&mut Subclass, CppSubclassRustPeerHolder<Subclass>) -> UniquePtr<CppPeer>,
160 PeerBoxer: FnOnce(Rc<RefCell<Subclass>>) -> CppSubclassRustPeerHolder<Subclass>,
161{
162 let me = Rc::new(RefCell::new(me));
163 let holder = peer_boxer(me.clone());
164 let cpp_side = peer_constructor(&mut me.as_ref().borrow_mut(), holder);
165 me.as_ref()
166 .borrow_mut()
167 .peer_holder_mut()
168 .set_owned(cpp_side);
169 me
170}
171
172/// A trait to be implemented by a subclass which knows how to construct
173/// its C++ peer object. Specifically, the implementation here will
174/// arrange to call one or other of the `make_unique` methods to be
175/// found on the superclass of the C++ object. If the superclass
176/// has a single trivial constructor, then this is implemented
177/// automatically for you. If there are multiple constructors, or
178/// a single constructor which takes parameters, you'll need to implement
179/// this trait for your subclass in order to call the correct
180/// constructor.
181pub trait CppPeerConstructor<CppPeer: CppSubclassCppPeer>: Sized {
182 /// Create the C++ peer. This method will be automatically generated
183 /// for you *except* in cases where the superclass has multiple constructors,
184 /// or its only constructor takes parameters. In such a case you'll need
185 /// to implement this by calling a `make_unique` method on the
186 /// `<my subclass name>Cpp` type, passing `peer_holder` as the first
187 /// argument.
188 fn make_peer(&mut self, peer_holder: CppSubclassRustPeerHolder<Self>) -> UniquePtr<CppPeer>;
189}
190
191/// A subclass of a C++ type.
192///
193/// To create a Rust subclass of a C++ class, you must do these things:
194/// * Create a `struct` to act as your subclass, and add the #[`macro@crate::subclass`] attribute.
195/// This adds a field to your struct for autocxx record-keeping. You can
196/// instead choose to implement [`CppSubclass`] a different way, in which case
197/// you must provide the [`macro@crate::subclass`] inside your [`crate::include_cpp`]
198/// macro. (`autocxx` will do the required codegen for your subclass
199/// whether it discovers a [`macro@crate::subclass`] directive inside your
200/// [`crate::include_cpp`], or elsewhere used as an attribute macro,
201/// or both.)
202/// * Use the [`CppSubclass`] trait, and instantiate the subclass using
203/// [`CppSubclass::new_rust_owned`] or [`CppSubclass::new_cpp_owned`]
204/// constructors. (You can use [`CppSubclassSelfOwned`] if you need that
205/// instead; also, see [`CppSubclassSelfOwnedDefault`] and [`CppSubclassDefault`]
206/// to arrange for easier constructors to exist.
207/// * You _may_ need to implement [`CppPeerConstructor`] for your subclass,
208/// but only if autocxx determines that there are multiple possible superclass
209/// constructors so you need to call one explicitly (or if there's a single
Brian Silvermanf3ec38b2022-07-06 20:43:36 -0700210/// non-trivial superclass constructor.) autocxx will implement this trait
211/// for you if there's no ambiguity and FFI functions are safe to call due to
212/// `autocxx::safety!` being used.
Brian Silverman4e662aa2022-05-11 23:10:19 -0700213///
214/// # How to access your Rust structure from outside
215///
216/// Use [`CppSubclass::new_rust_owned`] then use [`std::cell::RefCell::borrow`]
217/// or [`std::cell::RefCell::borrow_mut`] to obtain the underlying Rust struct.
218///
219/// # How to call C++ methods on the subclass
220///
221/// Do the same. You should find that your subclass struct `impl`s all the
222/// C++ methods belonging to the superclass.
223///
224/// # How to implement virtual methods
225///
226/// Simply add an `impl` for the `struct`, implementing the relevant method.
227/// The C++ virtual function call will be redirected to your Rust implementation.
228///
229/// # How _not_ to implement virtual methods
230///
231/// If you don't want to implement a virtual method, don't: the superclass
232/// method will be called instead. Naturally, you must implement any pure virtual
233/// methods.
234///
235/// # How it works
236///
237/// This actually consists of two objects: this object itself and a C++-side
238/// peer. The ownership relationship between those two things can work in three
239/// different ways:
240/// 1. Neither object is owned by Rust. The C++ peer is owned by a C++
241/// [`UniquePtr`] held elsewhere in C++. That C++ peer then owns
242/// this Rust-side object via a strong [`Rc`] reference. This is the
243/// ownership relationship set up by [`CppSubclass::new_cpp_owned`].
244/// 2. The object pair is owned by Rust. Specifically, by a strong
245/// [`Rc`] reference to this Rust-side object. In turn, the Rust-side object
246/// owns the C++-side peer via a [`UniquePtr`]. This is what's set up by
247/// [`CppSubclass::new_rust_owned`]. The C++-side peer _does not_ own the Rust
248/// object; it just has a weak pointer. (Otherwise we'd get a reference
249/// loop and nothing would ever be freed.)
250/// 3. The object pair is self-owned and will stay around forever until
251/// [`CppSubclassSelfOwned::delete_self`] is called. In this case there's a strong reference
252/// from the C++ to the Rust and from the Rust to the C++. This is useful
253/// for cases where the subclass is listening for events, and needs to
254/// stick around until a particular event occurs then delete itself.
255///
256/// # Limitations
257///
258/// * *Re-entrancy*. The main thing to look out for is re-entrancy. If a
259/// (non-const) virtual method is called on your type, which then causes you
260/// to call back into C++, which results in a _second_ call into a (non-const)
261/// virtual method, we will try to create two mutable references to your
262/// subclass which isn't allowed in Rust and will therefore panic.
263///
264/// A future version of autocxx may provide the option of treating all
265/// non-const methods (in C++) as const methods on the Rust side, which will
266/// give the option of using interior mutability ([`std::cell::RefCell`])
267/// for you to safely handle this situation, whilst remaining compatible
268/// with existing C++ interfaces. If you need this, indicate support on
269/// [this issue](https://github.com/google/autocxx/issues/622).
270///
271/// * *Thread safety*. The subclass object is not thread-safe and shouldn't
272/// be passed to different threads in C++. A future version of this code
273/// will give the option to use `Arc` and `Mutex` internally rather than
274/// `Rc` and `RefCell`, solving this problem.
275///
276/// * *Protected methods.* We don't do anything clever here - they're public.
277///
278/// * *Non-trivial class hierarchies*. We don't yet consider virtual methods
279/// on base classes of base classes. This is a temporary limitation,
280/// [see this issue](https://github.com/google/autocxx/issues/610).
281pub trait CppSubclass<CppPeer: CppSubclassCppPeer>: CppPeerConstructor<CppPeer> {
282 /// Return the field which holds the C++ peer object. This is normally
283 /// implemented by the #[`is_subclass`] macro, but you're welcome to
284 /// implement it yourself if you prefer.
285 fn peer_holder(&self) -> &CppSubclassCppPeerHolder<CppPeer>;
286
287 /// Return the field which holds the C++ peer object. This is normally
288 /// implemented by the #[`is_subclass`] macro, but you're welcome to
289 /// implement it yourself if you prefer.
290 fn peer_holder_mut(&mut self) -> &mut CppSubclassCppPeerHolder<CppPeer>;
291
292 /// Return a reference to the C++ part of this object pair.
293 /// This can be used to register listeners, etc.
294 fn peer(&self) -> &CppPeer {
295 self.peer_holder().get()
296 }
297
298 /// Return a mutable reference to the C++ part of this object pair.
299 /// This can be used to register listeners, etc.
300 fn peer_mut(&mut self) -> Pin<&mut CppPeer> {
301 self.peer_holder_mut().pin_mut()
302 }
303
304 /// Creates a new instance of this subclass. This instance is owned by the
305 /// returned [`cxx::UniquePtr`] and thus would typically be returned immediately
306 /// to C++ such that it can be owned on the C++ side.
307 fn new_cpp_owned(me: Self) -> UniquePtr<CppPeer> {
308 let me = Rc::new(RefCell::new(me));
309 let holder = CppSubclassRustPeerHolder::Owned(me.clone());
310 let mut borrowed = me.as_ref().borrow_mut();
311 let mut cpp_side = borrowed.make_peer(holder);
312 borrowed.peer_holder_mut().set_unowned(&mut cpp_side);
313 cpp_side
314 }
315
316 /// Creates a new instance of this subclass. This instance is not owned
317 /// by C++, and therefore will be deleted when it goes out of scope in
318 /// Rust.
319 fn new_rust_owned(me: Self) -> Rc<RefCell<Self>> {
320 make_owning_peer(
321 me,
322 |obj, holder| obj.make_peer(holder),
323 |me| CppSubclassRustPeerHolder::Unowned(Rc::downgrade(&me)),
324 )
325 }
326}
327
328/// Trait to be implemented by subclasses which are self-owned, i.e. not owned
329/// externally by either Rust or C++ code, and thus need the ability to delete
330/// themselves when some virtual function is called.
331pub trait CppSubclassSelfOwned<CppPeer: CppSubclassCppPeer>: CppSubclass<CppPeer> {
332 /// Creates a new instance of this subclass which owns itself.
333 /// This is useful
334 /// for observers (etc.) which self-register to listen to events.
335 /// If an event occurs which would cause this to want to unregister,
336 /// use [`CppSubclassSelfOwned::delete_self`].
337 /// The return value may be useful to register this, etc. but can ultimately
338 /// be discarded without destroying this object.
339 fn new_self_owned(me: Self) -> Rc<RefCell<Self>> {
340 make_owning_peer(
341 me,
342 |obj, holder| obj.make_peer(holder),
343 CppSubclassRustPeerHolder::Owned,
344 )
345 }
346
347 /// Relinquishes ownership from the C++ side. If there are no outstanding
348 /// references from the Rust side, this will result in the destruction
349 /// of this subclass instance.
350 fn delete_self(&self) {
351 self.peer().relinquish_ownership()
352 }
353}
354
355/// Provides default constructors for subclasses which implement `Default`.
356pub trait CppSubclassDefault<CppPeer: CppSubclassCppPeer>: CppSubclass<CppPeer> + Default {
357 /// Create a Rust-owned instance of this subclass, initializing with default values. See
358 /// [`CppSubclass`] for more details of the ownership models available.
359 fn default_rust_owned() -> Rc<RefCell<Self>>;
360
361 /// Create a C++-owned instance of this subclass, initializing with default values. See
362 /// [`CppSubclass`] for more details of the ownership models available.
363 fn default_cpp_owned() -> UniquePtr<CppPeer>;
364}
365
366impl<T, CppPeer> CppSubclassDefault<CppPeer> for T
367where
368 T: CppSubclass<CppPeer> + Default,
369 CppPeer: CppSubclassCppPeer,
370{
371 fn default_rust_owned() -> Rc<RefCell<Self>> {
372 Self::new_rust_owned(Self::default())
373 }
374
375 fn default_cpp_owned() -> UniquePtr<CppPeer> {
376 Self::new_cpp_owned(Self::default())
377 }
378}
379
380/// Provides default constructors for subclasses which implement `Default`
381/// and are self-owning.
382pub trait CppSubclassSelfOwnedDefault<CppPeer: CppSubclassCppPeer>:
383 CppSubclassSelfOwned<CppPeer> + Default
384{
385 /// Create a self-owned instance of this subclass, initializing with default values. See
386 /// [`CppSubclass`] for more details of the ownership models available.
387 fn default_self_owned() -> Rc<RefCell<Self>>;
388}
389
390impl<T, CppPeer> CppSubclassSelfOwnedDefault<CppPeer> for T
391where
392 T: CppSubclassSelfOwned<CppPeer> + Default,
393 CppPeer: CppSubclassCppPeer,
394{
395 fn default_self_owned() -> Rc<RefCell<Self>> {
396 Self::new_self_owned(Self::default())
397 }
398}