Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame^] | 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 | |
| 12 | use std::{ |
| 13 | cell::RefCell, |
| 14 | pin::Pin, |
| 15 | rc::{Rc, Weak}, |
| 16 | }; |
| 17 | |
| 18 | use cxx::{memory::UniquePtrTarget, UniquePtr}; |
| 19 | |
| 20 | /// Deprecated - use [`subclass`] instead. |
| 21 | #[deprecated] |
| 22 | pub 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. |
| 56 | pub 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 | /// ``` |
| 64 | pub 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)] |
| 73 | pub 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)] |
| 80 | pub enum CppSubclassRustPeerHolder<T> { |
| 81 | Owned(Rc<RefCell<T>>), |
| 82 | Unowned(Weak<RefCell<T>>), |
| 83 | } |
| 84 | |
| 85 | impl<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 | pub enum CppSubclassCppPeerHolder<CppPeer: CppSubclassCppPeer> { |
| 106 | Empty, |
| 107 | Owned(Box<UniquePtr<CppPeer>>), |
| 108 | Unowned(*mut CppPeer), |
| 109 | } |
| 110 | |
| 111 | impl<CppPeer: CppSubclassCppPeer> Default for CppSubclassCppPeerHolder<CppPeer> { |
| 112 | fn default() -> Self { |
| 113 | CppSubclassCppPeerHolder::Empty |
| 114 | } |
| 115 | } |
| 116 | |
| 117 | impl<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 | |
| 150 | fn make_owning_peer<CppPeer, PeerConstructor, Subclass, PeerBoxer>( |
| 151 | me: Subclass, |
| 152 | peer_constructor: PeerConstructor, |
| 153 | peer_boxer: PeerBoxer, |
| 154 | ) -> Rc<RefCell<Subclass>> |
| 155 | where |
| 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. |
| 181 | pub 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 |
| 210 | /// non-trivial superclass constructor.) autocxx will implemente this trait |
| 211 | /// for you if there's no ambiguity. |
| 212 | /// |
| 213 | /// # How to access your Rust structure from outside |
| 214 | /// |
| 215 | /// Use [`CppSubclass::new_rust_owned`] then use [`std::cell::RefCell::borrow`] |
| 216 | /// or [`std::cell::RefCell::borrow_mut`] to obtain the underlying Rust struct. |
| 217 | /// |
| 218 | /// # How to call C++ methods on the subclass |
| 219 | /// |
| 220 | /// Do the same. You should find that your subclass struct `impl`s all the |
| 221 | /// C++ methods belonging to the superclass. |
| 222 | /// |
| 223 | /// # How to implement virtual methods |
| 224 | /// |
| 225 | /// Simply add an `impl` for the `struct`, implementing the relevant method. |
| 226 | /// The C++ virtual function call will be redirected to your Rust implementation. |
| 227 | /// |
| 228 | /// # How _not_ to implement virtual methods |
| 229 | /// |
| 230 | /// If you don't want to implement a virtual method, don't: the superclass |
| 231 | /// method will be called instead. Naturally, you must implement any pure virtual |
| 232 | /// methods. |
| 233 | /// |
| 234 | /// # How it works |
| 235 | /// |
| 236 | /// This actually consists of two objects: this object itself and a C++-side |
| 237 | /// peer. The ownership relationship between those two things can work in three |
| 238 | /// different ways: |
| 239 | /// 1. Neither object is owned by Rust. The C++ peer is owned by a C++ |
| 240 | /// [`UniquePtr`] held elsewhere in C++. That C++ peer then owns |
| 241 | /// this Rust-side object via a strong [`Rc`] reference. This is the |
| 242 | /// ownership relationship set up by [`CppSubclass::new_cpp_owned`]. |
| 243 | /// 2. The object pair is owned by Rust. Specifically, by a strong |
| 244 | /// [`Rc`] reference to this Rust-side object. In turn, the Rust-side object |
| 245 | /// owns the C++-side peer via a [`UniquePtr`]. This is what's set up by |
| 246 | /// [`CppSubclass::new_rust_owned`]. The C++-side peer _does not_ own the Rust |
| 247 | /// object; it just has a weak pointer. (Otherwise we'd get a reference |
| 248 | /// loop and nothing would ever be freed.) |
| 249 | /// 3. The object pair is self-owned and will stay around forever until |
| 250 | /// [`CppSubclassSelfOwned::delete_self`] is called. In this case there's a strong reference |
| 251 | /// from the C++ to the Rust and from the Rust to the C++. This is useful |
| 252 | /// for cases where the subclass is listening for events, and needs to |
| 253 | /// stick around until a particular event occurs then delete itself. |
| 254 | /// |
| 255 | /// # Limitations |
| 256 | /// |
| 257 | /// * *Re-entrancy*. The main thing to look out for is re-entrancy. If a |
| 258 | /// (non-const) virtual method is called on your type, which then causes you |
| 259 | /// to call back into C++, which results in a _second_ call into a (non-const) |
| 260 | /// virtual method, we will try to create two mutable references to your |
| 261 | /// subclass which isn't allowed in Rust and will therefore panic. |
| 262 | /// |
| 263 | /// A future version of autocxx may provide the option of treating all |
| 264 | /// non-const methods (in C++) as const methods on the Rust side, which will |
| 265 | /// give the option of using interior mutability ([`std::cell::RefCell`]) |
| 266 | /// for you to safely handle this situation, whilst remaining compatible |
| 267 | /// with existing C++ interfaces. If you need this, indicate support on |
| 268 | /// [this issue](https://github.com/google/autocxx/issues/622). |
| 269 | /// |
| 270 | /// * *Thread safety*. The subclass object is not thread-safe and shouldn't |
| 271 | /// be passed to different threads in C++. A future version of this code |
| 272 | /// will give the option to use `Arc` and `Mutex` internally rather than |
| 273 | /// `Rc` and `RefCell`, solving this problem. |
| 274 | /// |
| 275 | /// * *Protected methods.* We don't do anything clever here - they're public. |
| 276 | /// |
| 277 | /// * *Non-trivial class hierarchies*. We don't yet consider virtual methods |
| 278 | /// on base classes of base classes. This is a temporary limitation, |
| 279 | /// [see this issue](https://github.com/google/autocxx/issues/610). |
| 280 | pub trait CppSubclass<CppPeer: CppSubclassCppPeer>: CppPeerConstructor<CppPeer> { |
| 281 | /// Return the field which holds the C++ peer object. This is normally |
| 282 | /// implemented by the #[`is_subclass`] macro, but you're welcome to |
| 283 | /// implement it yourself if you prefer. |
| 284 | fn peer_holder(&self) -> &CppSubclassCppPeerHolder<CppPeer>; |
| 285 | |
| 286 | /// Return the field which holds the C++ peer object. This is normally |
| 287 | /// implemented by the #[`is_subclass`] macro, but you're welcome to |
| 288 | /// implement it yourself if you prefer. |
| 289 | fn peer_holder_mut(&mut self) -> &mut CppSubclassCppPeerHolder<CppPeer>; |
| 290 | |
| 291 | /// Return a reference to the C++ part of this object pair. |
| 292 | /// This can be used to register listeners, etc. |
| 293 | fn peer(&self) -> &CppPeer { |
| 294 | self.peer_holder().get() |
| 295 | } |
| 296 | |
| 297 | /// Return a mutable reference to the C++ part of this object pair. |
| 298 | /// This can be used to register listeners, etc. |
| 299 | fn peer_mut(&mut self) -> Pin<&mut CppPeer> { |
| 300 | self.peer_holder_mut().pin_mut() |
| 301 | } |
| 302 | |
| 303 | /// Creates a new instance of this subclass. This instance is owned by the |
| 304 | /// returned [`cxx::UniquePtr`] and thus would typically be returned immediately |
| 305 | /// to C++ such that it can be owned on the C++ side. |
| 306 | fn new_cpp_owned(me: Self) -> UniquePtr<CppPeer> { |
| 307 | let me = Rc::new(RefCell::new(me)); |
| 308 | let holder = CppSubclassRustPeerHolder::Owned(me.clone()); |
| 309 | let mut borrowed = me.as_ref().borrow_mut(); |
| 310 | let mut cpp_side = borrowed.make_peer(holder); |
| 311 | borrowed.peer_holder_mut().set_unowned(&mut cpp_side); |
| 312 | cpp_side |
| 313 | } |
| 314 | |
| 315 | /// Creates a new instance of this subclass. This instance is not owned |
| 316 | /// by C++, and therefore will be deleted when it goes out of scope in |
| 317 | /// Rust. |
| 318 | fn new_rust_owned(me: Self) -> Rc<RefCell<Self>> { |
| 319 | make_owning_peer( |
| 320 | me, |
| 321 | |obj, holder| obj.make_peer(holder), |
| 322 | |me| CppSubclassRustPeerHolder::Unowned(Rc::downgrade(&me)), |
| 323 | ) |
| 324 | } |
| 325 | } |
| 326 | |
| 327 | /// Trait to be implemented by subclasses which are self-owned, i.e. not owned |
| 328 | /// externally by either Rust or C++ code, and thus need the ability to delete |
| 329 | /// themselves when some virtual function is called. |
| 330 | pub trait CppSubclassSelfOwned<CppPeer: CppSubclassCppPeer>: CppSubclass<CppPeer> { |
| 331 | /// Creates a new instance of this subclass which owns itself. |
| 332 | /// This is useful |
| 333 | /// for observers (etc.) which self-register to listen to events. |
| 334 | /// If an event occurs which would cause this to want to unregister, |
| 335 | /// use [`CppSubclassSelfOwned::delete_self`]. |
| 336 | /// The return value may be useful to register this, etc. but can ultimately |
| 337 | /// be discarded without destroying this object. |
| 338 | fn new_self_owned(me: Self) -> Rc<RefCell<Self>> { |
| 339 | make_owning_peer( |
| 340 | me, |
| 341 | |obj, holder| obj.make_peer(holder), |
| 342 | CppSubclassRustPeerHolder::Owned, |
| 343 | ) |
| 344 | } |
| 345 | |
| 346 | /// Relinquishes ownership from the C++ side. If there are no outstanding |
| 347 | /// references from the Rust side, this will result in the destruction |
| 348 | /// of this subclass instance. |
| 349 | fn delete_self(&self) { |
| 350 | self.peer().relinquish_ownership() |
| 351 | } |
| 352 | } |
| 353 | |
| 354 | /// Provides default constructors for subclasses which implement `Default`. |
| 355 | pub trait CppSubclassDefault<CppPeer: CppSubclassCppPeer>: CppSubclass<CppPeer> + Default { |
| 356 | /// Create a Rust-owned instance of this subclass, initializing with default values. See |
| 357 | /// [`CppSubclass`] for more details of the ownership models available. |
| 358 | fn default_rust_owned() -> Rc<RefCell<Self>>; |
| 359 | |
| 360 | /// Create a C++-owned instance of this subclass, initializing with default values. See |
| 361 | /// [`CppSubclass`] for more details of the ownership models available. |
| 362 | fn default_cpp_owned() -> UniquePtr<CppPeer>; |
| 363 | } |
| 364 | |
| 365 | impl<T, CppPeer> CppSubclassDefault<CppPeer> for T |
| 366 | where |
| 367 | T: CppSubclass<CppPeer> + Default, |
| 368 | CppPeer: CppSubclassCppPeer, |
| 369 | { |
| 370 | fn default_rust_owned() -> Rc<RefCell<Self>> { |
| 371 | Self::new_rust_owned(Self::default()) |
| 372 | } |
| 373 | |
| 374 | fn default_cpp_owned() -> UniquePtr<CppPeer> { |
| 375 | Self::new_cpp_owned(Self::default()) |
| 376 | } |
| 377 | } |
| 378 | |
| 379 | /// Provides default constructors for subclasses which implement `Default` |
| 380 | /// and are self-owning. |
| 381 | pub trait CppSubclassSelfOwnedDefault<CppPeer: CppSubclassCppPeer>: |
| 382 | CppSubclassSelfOwned<CppPeer> + Default |
| 383 | { |
| 384 | /// Create a self-owned instance of this subclass, initializing with default values. See |
| 385 | /// [`CppSubclass`] for more details of the ownership models available. |
| 386 | fn default_self_owned() -> Rc<RefCell<Self>>; |
| 387 | } |
| 388 | |
| 389 | impl<T, CppPeer> CppSubclassSelfOwnedDefault<CppPeer> for T |
| 390 | where |
| 391 | T: CppSubclassSelfOwned<CppPeer> + Default, |
| 392 | CppPeer: CppSubclassCppPeer, |
| 393 | { |
| 394 | fn default_self_owned() -> Rc<RefCell<Self>> { |
| 395 | Self::new_self_owned(Self::default()) |
| 396 | } |
| 397 | } |