Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Inspired by [queron query](https://github.com/FerionVE/guion/blob/master/src/queron/query.rs) and Rust's upcoming [Provider API](rust-lang/rust#96024). It uses a query type with GAT response type to allow downcasting and responding with non-'static types for e.g. traitcasting.
- Loading branch information
1 parent
bd9a0b3
commit 7eb1caf
Showing
21 changed files
with
197 additions
and
229 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,88 +1,107 @@ | ||
//! Macros for implementing traitcast for widgets | ||
|
||
use std::any::TypeId; | ||
use std::marker::PhantomData; | ||
use std::ptr::NonNull; | ||
|
||
use crate::env::Env; | ||
|
||
pub mod widget; | ||
pub mod handler; | ||
pub trait WQuery<E>: 'static { | ||
type Result<'a>: Sized + 'a; | ||
|
||
// TODO use ptr_metadata | ||
//fn covariant<'a,'s>(v: Self::Result<'a>) -> Self::Result<'s> where 'a: 's; | ||
} | ||
pub trait WQueryGeneric<E>: 'static { | ||
type Result<'a,G>: Sized + 'a where G: ?Sized + 'a; | ||
} | ||
|
||
/// U N S O U N D | ||
#[repr(C)] | ||
#[derive(Copy, Clone)] | ||
#[doc(hidden)] | ||
pub struct TraitObject { | ||
data: *mut (), | ||
vtable: *mut (), | ||
pub struct WQueryResponder<'s,'a,E> where 'a: 's { | ||
type_id: TypeId, // T | ||
response: NonNull<()>, // &'s mut Option<T::Result<'a>> | ||
_p: PhantomData<(&'s mut &'a mut (),E)>, // invariant 'a | ||
} | ||
|
||
const _: () = assert!(std::mem::size_of::<TraitObject>() == std::mem::size_of::<&dyn std::any::Any>()); | ||
const _: () = assert!(std::mem::align_of::<TraitObject>() == std::mem::align_of::<&dyn std::any::Any>()); | ||
impl<'a,E> WQueryResponder<'_,'a,E> where E: Env { | ||
#[inline] | ||
pub fn new<T>(respond_into: &mut Option<T::Result<'a>>) -> Self where T: WQuery<E> + ?Sized { | ||
Self { | ||
type_id: TypeId::of::<T>(), | ||
response: NonNull::<Option<T::Result<'a>>>::from(respond_into).cast::<()>(), | ||
_p: PhantomData, | ||
} | ||
} | ||
|
||
#[inline] | ||
pub fn try_respond<T>(&mut self, respond: impl FnOnce() -> T::Result<'a>) -> bool where T: WQuery<E> + ?Sized { | ||
self.try_downcast::<T>().map(#[inline] |v| *v = Some(respond()) ).is_some() | ||
} | ||
|
||
#[inline] | ||
pub fn try_respond_2<T>(&mut self, respond: impl FnOnce(&mut Option<T::Result<'a>>)) -> bool where T: WQuery<E> + ?Sized { | ||
self.try_downcast::<T>().map(respond).is_some() | ||
} | ||
|
||
/// This macro is used inside [`Widget/WidgetMut`](Widget) impls | ||
/// | ||
/// Example: | ||
/// ```ignore | ||
/// impl_traitcast!( | ||
/// dyn IButton => |s| s; | ||
/// dyn IButtonState => |s| &s.state; | ||
/// ); | ||
/// | ||
#[macro_export] | ||
macro_rules! impl_traitcast { | ||
($srcor:ty: $($trait:ty => |$id:ident| $access:expr; )*) => { | ||
#[inline] | ||
unsafe fn _as_trait_ref<'impl_traitcast_lt_a>(&'impl_traitcast_lt_a self, t: std::any::TypeId) -> Option<$crate::traitcast::TraitObject> { | ||
$( | ||
if t == std::any::TypeId::of::<<($srcor) as $crate::traitcast::TraitcastImpl::<'_,$trait>>::DestTypeID>() { | ||
let $id = self; | ||
let s: &'impl_traitcast_lt_a $trait = $access; | ||
let s = std::mem::transmute::<&'impl_traitcast_lt_a $trait,$crate::traitcast::TraitObject>(s); | ||
return Some(s); | ||
} | ||
);* | ||
/// non zero cost respond ops should be extracted into a fn/closure or use try_respond, so that this part can be inlined. | ||
#[inline] | ||
pub fn try_downcast<'s,T>(&'s mut self) -> Option<&'s mut Option<T::Result<'a>>> where 'a: 's, T: WQuery<E> + ?Sized { | ||
if TypeId::of::<T>() == self.type_id { | ||
Some(unsafe { | ||
self.response.cast::<Option<T::Result<'a>>>().as_mut() | ||
}) | ||
} else { | ||
None | ||
} | ||
} | ||
|
||
#[inline] | ||
pub fn fork(&mut self) -> WQueryResponder<'_,'a,E> { | ||
WQueryResponder { | ||
type_id: self.type_id, | ||
response: self.response, | ||
_p: PhantomData, | ||
} | ||
} | ||
} | ||
|
||
#[doc(hidden)] | ||
pub trait TraitcastImplBase<'a> { | ||
unsafe fn _as_trait_ref(&self, t: TypeId) -> Option<TraitObject>; | ||
pub struct WQueryResponderGeneric<'s,'a,Q,G,E> where 'a: 's, Q: WQueryGeneric<E> + ?Sized, G: ?Sized + 'a { | ||
response: &'s mut Option<Q::Result<'a,G>>, | ||
_p: PhantomData<(&'s mut &'a mut G,&'s mut Q,E)>, // invariant 'a | ||
} | ||
|
||
/// Trait to secure Traitcasting, generally implemented by [macro](traitcast_for) | ||
/// - Always implemented on `dyn WidgetDyn<E>` or `dyn Handler<E>` | ||
/// - `T` is the destination `dyn Trait` to which should be traitcasted | ||
/// - `DestTypeID` must be the same type as `T`, but with 'static lifetimes. Used to retrieve TypeID | ||
#[doc(hidden)] | ||
pub unsafe trait TraitcastImpl<'a,T>: TraitcastImplBase<'a> where T: ?Sized { | ||
type DestTypeID: ?Sized + 'static; | ||
impl<'a,'sa,Q,G,E> WQueryResponderGeneric<'sa,'a,Q,G,E> where E: Env, Q: WQueryGeneric<E> + ?Sized, G: ?Sized { | ||
#[inline] | ||
pub fn new(respond_into: &'sa mut Option<Q::Result<'a,G>>) -> Self { | ||
Self { | ||
response: respond_into, | ||
_p: PhantomData, | ||
} | ||
} | ||
|
||
#[inline] | ||
unsafe fn _try_traitcast_ref<'s>(senf: &'s Self) -> Result<&'s T,()> { | ||
// god plz fix https://github.com/rust-lang/rust/issues/51826 | ||
let t = TypeId::of::<Self::DestTypeID>(); | ||
let t = senf._as_trait_ref(t); | ||
if let Some(v) = t { | ||
Ok(std::mem::transmute_copy::<TraitObject,&'s T>(&v)) | ||
pub fn try_respond<T>(&mut self, fun: impl FnOnce() -> T::Result<'a,G>) -> bool where T: WQueryGeneric<E> + ?Sized { | ||
self.try_downcast::<T>().map(#[inline] |v| *v = Some(fun()) ).is_some() | ||
} | ||
|
||
#[inline] | ||
pub fn try_respond_2<T>(&mut self, fun: impl FnOnce(&mut Option<T::Result<'a,G>>)) -> bool where T: WQueryGeneric<E> + ?Sized { | ||
self.try_downcast::<T>().map(fun).is_some() | ||
} | ||
|
||
#[inline] | ||
pub fn try_downcast<'s,T>(&'s mut self) -> Option<&'s mut Option<T::Result<'a,G>>> where 'a: 's, T: WQueryGeneric<E> + ?Sized { | ||
if TypeId::of::<T>() == TypeId::of::<Q>() { | ||
Some(unsafe { | ||
let resp: &'s mut Option<Q::Result<'a,G>> = self.response; | ||
&mut *(resp as *mut _ as *mut Option<T::Result<'a,G>>) | ||
}) | ||
} else { | ||
Err(()) | ||
None | ||
} | ||
} | ||
} | ||
|
||
/// Syntax: | ||
/// `traitcast_for!([<...>] Trait[<...>] [where ...]);` | ||
#[macro_export] | ||
macro_rules! traitcast_for { | ||
( | ||
$( < $($args:ident),* $(,)* > )? | ||
$trait:path | ||
$(where $($preds:tt)+)? | ||
) => { | ||
unsafe impl<'w,TCO,$( $($args),* )?> $crate::traitcast::TraitcastImpl<'w,dyn $trait+'w> for TCO where TCO: $crate::traitcast::TraitcastImplBase<'w> + ?Sized, $($($preds)+ )? { | ||
type DestTypeID = dyn $trait+'static; | ||
#[inline] | ||
pub fn fork(&mut self) -> WQueryResponderGeneric<'_,'a,Q,G,E> { | ||
WQueryResponderGeneric { | ||
response: self.response, | ||
_p: PhantomData, | ||
} | ||
} | ||
} |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.