autocxx
                                
                                 autocxx copied to clipboard
                                
                                    autocxx copied to clipboard
                            
                            
                            
                        Implement `AsRef` for casting to superclasses
If you have a reference b_ref: &B, and B is a subclass of A, it would sometimes be useful to have an &A in order to pass into APIs which expect a &A. Experiments suggest that this is ergonomic using AsRef, at least for constant references (not so much for pinned mutable references).
Isn't this casting from subclasses to superclasses? (title)
#663 does this for const references, but mutable references continue to be problemmy.
hey, for my own knowledge, as a rust beginner, why are mutable references a problem? by that I mean, if base methods are generated in the subclass with pin mut ?
I can't remember for sure; the only comment I can find is here which says it's due to the Pinning. It might be relatively easy to flip that boolean and fix it up to see what works and what goes wrong...
I did a bit more investigation into what it would take to add mutable casts using autocxx::PinMut. This is largely a note to my future self.
The first obstacle is that the signature of pin_mut is currently fn pin_mut(&mut self) -> std::pin::Pin<&mut T>. We might need to change that. Any option isn't great:
- Sticking with fn pin_mut(&mut self) -> std::pin::Pin<&mut T>: that&mut selfprevents us from implementing this forPin<&mut T>without significant changes to the code generator to be aware of this special case. (Code generation for these is done by pretending bindgen gave us a functionfn cast(this: *mut T)which our translation layer automatically turns into a cxx-compatiblePin<&mut T>). While we could possibly add this special casing, I'm a little nervous thatself: &mut Pin<&mut T>will have complications, e.g. we'll have to juggle two lifetimes, and it just doesn't feel right.
- fn pin_mut(self) -> std::pin::Pin<&mut T>: not object safe. That's probably OK. We'd be- impling the trait for for- Pin<&mut T>rather than- T. That might be OK too. For the current usage of- PinMutwithin the fake chromium render frame example, it would be a bit annoying. But most of all, I don't know that it's possible to use syntax like- + 'aanywhere to indicate that the- selfmust contain a reference to which the return type's lifetime can be bound. So I think this option is out.
- fn pin_mut(self: std::pin::Pin<&mut T>) -> std::pin::Pin<&mut T>seems perhaps least bad for usage with these casts, but completely breaks the ability to use this trait for the sort of object lifetime handle management code we've got in- fake-chromium-render-frame-host.
More thought required.
A note for others who run across this - as an alternative to the Rust workaround in this repo's examples https://github.com/google/autocxx/blob/762dbf397ebc5ab0697b8656b06af69851187da6/examples/chromium-fake-render-frame-host/src/render_frame_host.rs#L226
It seems that C++ upcast function(s) can also be a workable approach as shown in https://github.com/dtolnay/cxx/issues/797#issuecomment-809951817.
That approach works trivially in autocxx for my use case where I'm trying to upcast a std::unique_ptr:
// in .h
inline std::unique_ptr<A> upcast_b(std::unique_ptr<B> b) { return b; }
// in .rs
include_cpp! {
    safety!(unsafe_ffi)
    generate!("upcast_b")
}
let a: UniquePtr<A> = ffi::upcast_b(b);
I don't have any opinion on whether performing the upcast in Rust or C++ is better.
Apparently PyO3 uses Deref for this purpose rather than AsRef, so casts are automatic/transparent.
A note for others who run across this - as an alternative to the Rust workaround in this repo's examples https://github.com/google/autocxx/blob/762dbf397ebc5ab0697b8656b06af69851187da6/examples/chromium-fake-render-frame-host/src/render_frame_host.rs#L226
Based on this, I found that
unsafe fn upcast<D, S>(derived: Pin<&mut D>) -> Pin<&mut S> {
    let subclass_obs_ptr = Pin::into_inner_unchecked(derived) as *mut D;
    std::pin::Pin::new_unchecked(&mut *subclass_obs_ptr.cast::<S>())
}
works very well for most cases.
You can use this both with UniquePtr's via UniquePtr::pin_mut() and autocxx::prelude::New aswell as casting between generated ans self implemented ffi's.