| //! Module to make Rust subclasses of C++ classes. See [`CppSubclass`] |
| //! for details. |
| |
| // Copyright 2021 Google LLC |
| // |
| // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or |
| // https://www.apache.org/licenses/LICENSE-2.0> or the MIT license |
| // <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your |
| // option. This file may not be copied, modified, or distributed |
| // except according to those terms. |
| |
| use std::{ |
| cell::RefCell, |
| pin::Pin, |
| rc::{Rc, Weak}, |
| }; |
| |
| use cxx::{memory::UniquePtrTarget, UniquePtr}; |
| |
| /// Deprecated - use [`subclass`] instead. |
| #[deprecated] |
| pub use autocxx_macro::subclass as is_subclass; |
| |
| /// Declare a Rust subclass of a C++ class. |
| /// You can use this in two ways: |
| /// * As an attribute macro on a struct which is to be a subclass. |
| /// In this case, you must specify the superclass as described below. |
| /// For instance, |
| /// ```nocompile |
| /// # use autocxx_macro::subclass as subclass; |
| /// #[subclass(superclass("MyCppSuperclass"))] |
| /// struct Bar {}; |
| /// ``` |
| /// * as a directive within the [include_cpp] macro, in which case you |
| /// must provide two arguments of the superclass and then the |
| /// subclass: |
| /// ``` |
| /// # use autocxx_macro::include_cpp_impl as include_cpp; |
| /// include_cpp!( |
| /// # parse_only!() |
| /// #include "input.h" |
| /// subclass!("MyCppSuperclass",Bar) |
| /// safety!(unsafe) |
| /// ); |
| /// struct Bar { |
| /// // ... |
| /// } |
| /// ``` |
| /// In this latter case, you'll need to implement the trait |
| /// [`CppSubclass`] for the struct, so it's |
| /// generally easier to use the former option. |
| /// |
| /// See [`CppSubclass`] for information about the |
| /// multiple steps you need to take to be able to make Rust |
| /// subclasses of a C++ class. |
| pub use autocxx_macro::subclass; |
| |
| /// A prelude containing all the traits and macros required to create |
| /// Rust subclasses of C++ classes. It's recommended that you: |
| /// |
| /// ```rust |
| /// use autocxx::subclass::prelude::*; |
| /// ``` |
| pub mod prelude { |
| pub use super::{ |
| is_subclass, subclass, CppPeerConstructor, CppSubclass, CppSubclassDefault, |
| CppSubclassRustPeerHolder, CppSubclassSelfOwned, CppSubclassSelfOwnedDefault, |
| }; |
| } |
| |
| /// A trait representing the C++ side of a Rust/C++ subclass pair. |
| #[doc(hidden)] |
| pub trait CppSubclassCppPeer: UniquePtrTarget { |
| fn relinquish_ownership(&self); |
| } |
| |
| /// A type used for how the C++ side of a Rust/C++ subclass pair refers to |
| /// the Rust side. |
| #[doc(hidden)] |
| pub enum CppSubclassRustPeerHolder<T> { |
| Owned(Rc<RefCell<T>>), |
| Unowned(Weak<RefCell<T>>), |
| } |
| |
| impl<T> CppSubclassRustPeerHolder<T> { |
| pub fn get(&self) -> Option<Rc<RefCell<T>>> { |
| match self { |
| CppSubclassRustPeerHolder::Owned(strong) => Some(strong.clone()), |
| CppSubclassRustPeerHolder::Unowned(weak) => weak.upgrade(), |
| } |
| } |
| pub fn relinquish_ownership(self) -> Self { |
| match self { |
| CppSubclassRustPeerHolder::Owned(strong) => { |
| CppSubclassRustPeerHolder::Unowned(Rc::downgrade(&strong)) |
| } |
| _ => self, |
| } |
| } |
| } |
| |
| /// A type showing how the Rust side of a Rust/C++ subclass pair refers to |
| /// the C++ side. |
| #[doc(hidden)] |
| pub enum CppSubclassCppPeerHolder<CppPeer: CppSubclassCppPeer> { |
| Empty, |
| Owned(Box<UniquePtr<CppPeer>>), |
| Unowned(*mut CppPeer), |
| } |
| |
| impl<CppPeer: CppSubclassCppPeer> Default for CppSubclassCppPeerHolder<CppPeer> { |
| fn default() -> Self { |
| CppSubclassCppPeerHolder::Empty |
| } |
| } |
| |
| impl<CppPeer: CppSubclassCppPeer> CppSubclassCppPeerHolder<CppPeer> { |
| fn pin_mut(&mut self) -> Pin<&mut CppPeer> { |
| match self { |
| CppSubclassCppPeerHolder::Empty => panic!("Peer not set up"), |
| CppSubclassCppPeerHolder::Owned(peer) => peer.pin_mut(), |
| CppSubclassCppPeerHolder::Unowned(peer) => unsafe { |
| // Safety: guaranteed safe because this is a pointer to a C++ object, |
| // and C++ never moves things in memory. |
| Pin::new_unchecked(peer.as_mut().unwrap()) |
| }, |
| } |
| } |
| fn get(&self) -> &CppPeer { |
| match self { |
| CppSubclassCppPeerHolder::Empty => panic!("Peer not set up"), |
| CppSubclassCppPeerHolder::Owned(peer) => peer.as_ref(), |
| // Safety: guaranteed safe because this is a pointer to a C++ object, |
| // and C++ never moves things in memory. |
| CppSubclassCppPeerHolder::Unowned(peer) => unsafe { peer.as_ref().unwrap() }, |
| } |
| } |
| fn set_owned(&mut self, peer: UniquePtr<CppPeer>) { |
| *self = Self::Owned(Box::new(peer)); |
| } |
| fn set_unowned(&mut self, peer: &mut UniquePtr<CppPeer>) { |
| // Safety: guaranteed safe because this is a pointer to a C++ object, |
| // and C++ never moves things in memory. |
| *self = Self::Unowned(unsafe { |
| std::pin::Pin::<&mut CppPeer>::into_inner_unchecked(peer.pin_mut()) |
| }); |
| } |
| } |
| |
| fn make_owning_peer<CppPeer, PeerConstructor, Subclass, PeerBoxer>( |
| me: Subclass, |
| peer_constructor: PeerConstructor, |
| peer_boxer: PeerBoxer, |
| ) -> Rc<RefCell<Subclass>> |
| where |
| CppPeer: CppSubclassCppPeer, |
| Subclass: CppSubclass<CppPeer>, |
| PeerConstructor: |
| FnOnce(&mut Subclass, CppSubclassRustPeerHolder<Subclass>) -> UniquePtr<CppPeer>, |
| PeerBoxer: FnOnce(Rc<RefCell<Subclass>>) -> CppSubclassRustPeerHolder<Subclass>, |
| { |
| let me = Rc::new(RefCell::new(me)); |
| let holder = peer_boxer(me.clone()); |
| let cpp_side = peer_constructor(&mut me.as_ref().borrow_mut(), holder); |
| me.as_ref() |
| .borrow_mut() |
| .peer_holder_mut() |
| .set_owned(cpp_side); |
| me |
| } |
| |
| /// A trait to be implemented by a subclass which knows how to construct |
| /// its C++ peer object. Specifically, the implementation here will |
| /// arrange to call one or other of the `make_unique` methods to be |
| /// found on the superclass of the C++ object. If the superclass |
| /// has a single trivial constructor, then this is implemented |
| /// automatically for you. If there are multiple constructors, or |
| /// a single constructor which takes parameters, you'll need to implement |
| /// this trait for your subclass in order to call the correct |
| /// constructor. |
| pub trait CppPeerConstructor<CppPeer: CppSubclassCppPeer>: Sized { |
| /// Create the C++ peer. This method will be automatically generated |
| /// for you *except* in cases where the superclass has multiple constructors, |
| /// or its only constructor takes parameters. In such a case you'll need |
| /// to implement this by calling a `make_unique` method on the |
| /// `<my subclass name>Cpp` type, passing `peer_holder` as the first |
| /// argument. |
| fn make_peer(&mut self, peer_holder: CppSubclassRustPeerHolder<Self>) -> UniquePtr<CppPeer>; |
| } |
| |
| /// 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 #[`macro@crate::subclass`] attribute. |
| /// This adds a field to your struct for autocxx record-keeping. You can |
| /// instead choose to implement [`CppSubclass`] a different way, in which case |
| /// you must provide the [`macro@crate::subclass`] inside your [`crate::include_cpp`] |
| /// macro. (`autocxx` will do the required codegen for your subclass |
| /// whether it discovers a [`macro@crate::subclass`] directive inside your |
| /// [`crate::include_cpp`], or elsewhere used as an attribute macro, |
| /// or both.) |
| /// * Use the [`CppSubclass`] trait, and instantiate the subclass using |
| /// [`CppSubclass::new_rust_owned`] or [`CppSubclass::new_cpp_owned`] |
| /// constructors. (You can use [`CppSubclassSelfOwned`] if you need that |
| /// instead; also, see [`CppSubclassSelfOwnedDefault`] and [`CppSubclassDefault`] |
| /// 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 implemente this trait |
| /// for you if there's no ambiguity. |
| /// |
| /// # 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: |
| /// 1. 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 strong [`Rc`] reference. This is the |
| /// ownership relationship set up by [`CppSubclass::new_cpp_owned`]. |
| /// 2. 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 a [`UniquePtr`]. This is what's set up by |
| /// [`CppSubclass::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.) |
| /// 3. 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](https://github.com/google/autocxx/issues/622). |
| /// |
| /// * *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` and `Mutex` internally rather than |
| /// `Rc` and `RefCell`, 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](https://github.com/google/autocxx/issues/610). |
| pub trait CppSubclass<CppPeer: CppSubclassCppPeer>: CppPeerConstructor<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. |
| 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. |
| fn peer_holder_mut(&mut self) -> &mut CppSubclassCppPeerHolder<CppPeer>; |
| |
| /// Return a reference to the C++ part of this object pair. |
| /// This can be used to register listeners, etc. |
| fn peer(&self) -> &CppPeer { |
| self.peer_holder().get() |
| } |
| |
| /// Return a mutable reference to the C++ part of this object pair. |
| /// This can be used to register listeners, etc. |
| fn peer_mut(&mut self) -> Pin<&mut CppPeer> { |
| self.peer_holder_mut().pin_mut() |
| } |
| |
| /// 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. |
| fn new_cpp_owned(me: Self) -> UniquePtr<CppPeer> { |
| let me = Rc::new(RefCell::new(me)); |
| let holder = CppSubclassRustPeerHolder::Owned(me.clone()); |
| let mut borrowed = me.as_ref().borrow_mut(); |
| let mut cpp_side = borrowed.make_peer(holder); |
| borrowed.peer_holder_mut().set_unowned(&mut cpp_side); |
| cpp_side |
| } |
| |
| /// 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. |
| fn new_rust_owned(me: Self) -> Rc<RefCell<Self>> { |
| make_owning_peer( |
| me, |
| |obj, holder| obj.make_peer(holder), |
| |me| CppSubclassRustPeerHolder::Unowned(Rc::downgrade(&me)), |
| ) |
| } |
| } |
| |
| /// Trait to be implemented by subclasses which are self-owned, i.e. not owned |
| /// externally by either Rust or C++ code, and thus need the ability to delete |
| /// themselves when some virtual function is called. |
| pub trait CppSubclassSelfOwned<CppPeer: CppSubclassCppPeer>: CppSubclass<CppPeer> { |
| /// Creates a new instance of this subclass which owns itself. |
| /// This is useful |
| /// for observers (etc.) which self-register to listen to events. |
| /// If an event occurs which would cause this to want to unregister, |
| /// use [`CppSubclassSelfOwned::delete_self`]. |
| /// The return value may be useful to register this, etc. but can ultimately |
| /// be discarded without destroying this object. |
| fn new_self_owned(me: Self) -> Rc<RefCell<Self>> { |
| make_owning_peer( |
| me, |
| |obj, holder| obj.make_peer(holder), |
| CppSubclassRustPeerHolder::Owned, |
| ) |
| } |
| |
| /// Relinquishes ownership from the C++ side. If there are no outstanding |
| /// references from the Rust side, this will result in the destruction |
| /// of this subclass instance. |
| fn delete_self(&self) { |
| self.peer().relinquish_ownership() |
| } |
| } |
| |
| /// Provides default constructors for subclasses which implement `Default`. |
| pub trait CppSubclassDefault<CppPeer: CppSubclassCppPeer>: CppSubclass<CppPeer> + Default { |
| /// Create a Rust-owned instance of this subclass, initializing with default values. See |
| /// [`CppSubclass`] for more details of the ownership models available. |
| fn default_rust_owned() -> Rc<RefCell<Self>>; |
| |
| /// Create a C++-owned instance of this subclass, initializing with default values. See |
| /// [`CppSubclass`] for more details of the ownership models available. |
| fn default_cpp_owned() -> UniquePtr<CppPeer>; |
| } |
| |
| impl<T, CppPeer> CppSubclassDefault<CppPeer> for T |
| where |
| T: CppSubclass<CppPeer> + Default, |
| CppPeer: CppSubclassCppPeer, |
| { |
| fn default_rust_owned() -> Rc<RefCell<Self>> { |
| Self::new_rust_owned(Self::default()) |
| } |
| |
| fn default_cpp_owned() -> UniquePtr<CppPeer> { |
| Self::new_cpp_owned(Self::default()) |
| } |
| } |
| |
| /// Provides default constructors for subclasses which implement `Default` |
| /// and are self-owning. |
| pub trait CppSubclassSelfOwnedDefault<CppPeer: CppSubclassCppPeer>: |
| CppSubclassSelfOwned<CppPeer> + Default |
| { |
| /// Create a self-owned instance of this subclass, initializing with default values. See |
| /// [`CppSubclass`] for more details of the ownership models available. |
| fn default_self_owned() -> Rc<RefCell<Self>>; |
| } |
| |
| impl<T, CppPeer> CppSubclassSelfOwnedDefault<CppPeer> for T |
| where |
| T: CppSubclassSelfOwned<CppPeer> + Default, |
| CppPeer: CppSubclassCppPeer, |
| { |
| fn default_self_owned() -> Rc<RefCell<Self>> { |
| Self::new_self_owned(Self::default()) |
| } |
| } |