Skip to content

Commit

Permalink
Traitcast 2.0
Browse files Browse the repository at this point in the history
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
qwertz19281 committed Dec 4, 2022
1 parent bd9a0b3 commit 7eb1caf
Show file tree
Hide file tree
Showing 21 changed files with 197 additions and 229 deletions.
20 changes: 10 additions & 10 deletions src/handler/mod.rs
@@ -1,10 +1,8 @@
//! Handlers can be chained and dispatch events and other stuff

use std::any::TypeId;

use crate::aliases::{ERenderer, ESize};
use crate::env::Env;
use crate::traitcast::TraitObject;
use crate::traitcast::{WQueryResponder, WQueryResponderGeneric, WQueryGeneric};
use crate::{event_new, EventResp};
use crate::newpath::{PathResolvusDyn, PathStack};
use crate::queron::Queron;
Expand Down Expand Up @@ -77,13 +75,10 @@ pub trait Handler<E>: 'static where E: Env {
false
}

/// The [`impl_traitcast`] macro should be used to implement this function
#[allow(unused)]
#[doc(hidden)]
#[inline]
unsafe fn _as_trait_ref(&self, t: TypeId) -> Option<TraitObject> {
None
}
//TODO separate from WQuery, as this definitely doesn't query the widget behind the handler but the handler itself
fn respond_query<'a>(&'a self, t: WQueryResponder<'_,'a,E>);

fn respond_query_generic<'a,Q,G>(&'a self, t: WQueryResponderGeneric<'_,'a,Q,G,E>) where Q: WQueryGeneric<E> + ?Sized, G: ?Sized;
}

impl<E> Handler<E> for () where E: Env {
Expand Down Expand Up @@ -167,6 +162,11 @@ impl<E> Handler<E> for () where E: Env {
fn is_tail(&self) -> bool {
true
}

#[inline]
fn respond_query<'a>(&'a self, _: WQueryResponder<'_,'a,E>) {}
#[inline]
fn respond_query_generic<'a,Q,G>(&'a self, _: WQueryResponderGeneric<'_,'a,Q,G,E>) where Q: WQueryGeneric<E> + ?Sized, G: ?Sized {}
}

impl<E> HandlerBuilder<E> for () where E: Env {
Expand Down
5 changes: 5 additions & 0 deletions src/handler/standard/imp.rs
Expand Up @@ -379,6 +379,11 @@ impl<SB,E> Handler<E> for StdHandlerLive<SB,E> where
//todo!();
self.sup._size(w, path, stack, cache, root, ctx)
}

#[inline]
fn respond_query<'a>(&'a self, _: crate::traitcast::WQueryResponder<'_,'a,E>) {}
#[inline]
fn respond_query_generic<'a,Q,G>(&'a self, _: crate::traitcast::WQueryResponderGeneric<'_,'a,Q,G,E>) where Q: crate::traitcast::WQueryGeneric<E> + ?Sized, G: ?Sized {}
}

/*impl<S,E> AsHandler<Self,E> for StdHandler<S,E> where S: Handler<E>, E: Env, E::Context<'_>: Context<E,Handler=Self> {
Expand Down
7 changes: 5 additions & 2 deletions src/text/stor.rs
Expand Up @@ -8,7 +8,7 @@ use std::sync::{MutexGuard, RwLockReadGuard, RwLockWriteGuard};

use crate::cachor::{MutCell, AsCachor};
use crate::env::Env;
use crate::traitcast_for_from_widget;
use crate::traitcast::WQuery;
use crate::util::immu::Immutable;

use super::layout::TxtLayout;
Expand All @@ -31,7 +31,10 @@ pub trait TextStor<E> {
}
}

traitcast_for_from_widget!(TextStor<E>); //TODO mutable Traitcast
//TODO mutable Traitcast
impl<E> WQuery<E> for dyn TextStor<E> where E: Env {
type Result<'a> = &'a (dyn TextStor<E> + 'a);
}

pub trait TextStorMut<E>: TextStor<E> {
fn replace(&mut self, replace_range: Range<usize>, insert: &str);
Expand Down
37 changes: 0 additions & 37 deletions src/traitcast/handler.rs

This file was deleted.

149 changes: 84 additions & 65 deletions src/traitcast/mod.rs
@@ -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,
}
}
}
57 changes: 0 additions & 57 deletions src/traitcast/widget.rs

This file was deleted.

16 changes: 10 additions & 6 deletions src/widget/dyn_tunnel.rs
Expand Up @@ -3,7 +3,7 @@ use std::marker::PhantomData;

use crate::event_new::downcast_map::EventDowncastMap;
use crate::queron::Queron;
use crate::traitcast::TraitObject;
use crate::traitcast::{WQueryResponder, WQueryResponderGeneric, WQueryGeneric};
use crate::util::error::GuionResolveErrorChildInfo;
use crate::util::tabulate::{TabulateNextChildOrigin, TabulateDirection, TabulateOrigin, TabulateNextChildResponse, TabulateResponse};
use crate::{EventResp, ProtectedReturn, event_new};
Expand Down Expand Up @@ -147,7 +147,7 @@ pub trait WidgetDyn<E> where E: Env + 'static {
fn debug_type_name_dyn(&self, dest: &mut Vec<&'static str>);
fn debugged_type_name_dyn(&self) -> Vec<&'static str>;

unsafe fn _as_trait_ref_dyn(&self, t: TypeId) -> Option<TraitObject>;
fn respond_query_dyn<'a>(&'a self, t: WQueryResponder<'_,'a,E>);

fn erase_dyn<'s>(&self) -> &(dyn WidgetDyn<E>+'s) where Self: 's;

Expand Down Expand Up @@ -348,8 +348,8 @@ impl<T,E> WidgetDyn<E> for T where T: Widget<E> + ?Sized, E: Env {
self.debugged_type_name()
}
#[inline]
unsafe fn _as_trait_ref_dyn(&self, t: TypeId) -> Option<TraitObject> {
self._as_trait_ref(t)
fn respond_query_dyn<'a>(&'a self, t: WQueryResponder<'_,'a,E>) {
self.respond_query(t)
}
#[inline]
fn erase_dyn<'s>(&self) -> &(dyn WidgetDyn<E>+'s) where Self: 's {
Expand Down Expand Up @@ -608,8 +608,12 @@ impl<E> Widget<E> for dyn WidgetDyn<E> + '_ where E: Env {
self.debugged_type_name_dyn()
}
#[inline]
unsafe fn _as_trait_ref(&self, t: TypeId) -> Option<TraitObject> {
self._as_trait_ref_dyn(t)
fn respond_query<'a>(&'a self, t: WQueryResponder<'_,'a,E>) {
self.respond_query_dyn(t)
}
#[inline]
fn respond_query_generic<'a,Q,G>(&'a self, _: WQueryResponderGeneric<'_,'a,Q,G,E>) where Q: WQueryGeneric<E> + ?Sized, G: ?Sized {
//TODO generic query not supported on dyn tunnel
}
#[inline]
fn erase<'s>(&self) -> &(dyn WidgetDyn<E>+'s) where Self: 's {
Expand Down

0 comments on commit 7eb1caf

Please sign in to comment.