use std::pin::Pin;
pub unsafe trait Upcast<T> {
#[doc(hidden)]
unsafe fn upcast_ptr(this: *const Self) -> *const T;
#[doc(hidden)]
unsafe fn from_base_ptr(base: *const T) -> *const Self;
fn upcast(&self) -> &T {
unsafe { &*Self::upcast_ptr(self) }
}
fn upcast_mut(&mut self) -> &mut T {
unsafe { &mut *Self::upcast_ptr(self).cast_mut() }
}
fn upcast_pin(self: Pin<&mut Self>) -> Pin<&mut T> {
unsafe { Pin::new_unchecked(&mut *Self::upcast_ptr(&*self).cast_mut()) }
}
}
pub trait Downcast: Sized {
fn downcast<Sub: Upcast<Self>>(&self) -> Option<&Sub> {
unsafe {
let ptr = Sub::from_base_ptr(self);
if ptr.is_null() {
None
} else {
Some(&*ptr)
}
}
}
fn downcast_mut<Sub: Upcast<Self>>(&mut self) -> Option<&mut Sub> {
unsafe {
let ptr = Sub::from_base_ptr(self);
if ptr.is_null() {
None
} else {
Some(&mut *ptr.cast_mut())
}
}
}
fn downcast_pin<Sub: Upcast<Self>>(self: Pin<&mut Self>) -> Option<Pin<&mut Sub>> {
unsafe {
let ptr = Sub::from_base_ptr(&*self);
if ptr.is_null() {
None
} else {
Some(Pin::new_unchecked(&mut *ptr.cast_mut()))
}
}
}
}
impl<T: Sized> Downcast for T {}
unsafe impl<T> Upcast<T> for T {
unsafe fn upcast_ptr(this: *const Self) -> *const Self {
this
}
unsafe fn from_base_ptr(base: *const T) -> *const Self {
base
}
fn upcast(&self) -> &Self {
self
}
fn upcast_mut(&mut self) -> &mut Self {
self
}
fn upcast_pin(self: Pin<&mut Self>) -> Pin<&mut Self> {
self
}
}
#[macro_export]
macro_rules! impl_transitive_cast {
($first:ty, $second:ty, $third:ty) => {
unsafe impl ::cxx_qt::casting::Upcast<$third> for $first {
unsafe fn upcast_ptr(this: *const Self) -> *const $third {
let base = <Self as Upcast<$second>>::upcast_ptr(this);
<$second as Upcast<$third>>::upcast_ptr(base)
}
unsafe fn from_base_ptr(base: *const $third) -> *const Self {
let base = <$second as Upcast<$third>>::from_base_ptr(base);
if base.is_null() {
std::ptr::null()
} else {
<Self as Upcast<$second>>::from_base_ptr(base)
}
}
}
};
($first:ty, $second:ty, $third:ty, $($rest:ty),*) => {
impl_transitive_cast!($first, $second, $third);
impl_transitive_cast!($first, $third, $($rest),*);
};
}