minijinja/value/
type_erase.rs1macro_rules! type_erase {
6 ($v:vis trait $t_name:ident => $erased_t_name:ident {
7 $(fn $f:ident(&self $(, $p:ident: $t:ty $(,)?)*) $(-> $r:ty)?;)*
8 $(
9 impl $impl_name:path {
10 $(
11 fn $f_impl:ident(
12 &self $(, $p_impl:ident: $t_impl:ty $(,)?)*
13 ) $(-> $r_impl:ty)?;
14 )*
15 }
16 )*
17 }) => {
18 #[doc = concat!("Type-erased version of [`", stringify!($t_name), "`]")]
19 $v struct $erased_t_name {
20 ptr: *const (),
21 vtable: *const (),
22 }
23
24 const _: () = {
25 struct VTable {
26 $($f: fn(*const (), $($p: $t),*) $(-> $r)?,)*
27 $($($f_impl: fn(*const (), $($p_impl: $t_impl),*) $(-> $r_impl)?,)*)*
28 __type_id: fn() -> std::any::TypeId,
29 __type_name: fn() -> &'static str,
30 __incref: fn(*const ()),
31 __decref: fn(*const ()),
32 }
33
34 #[inline(always)]
35 fn vt(e: &$erased_t_name) -> &VTable {
36 unsafe { &*(e.vtable as *const VTable) }
37 }
38
39 impl $erased_t_name {
40 #[doc = concat!("Returns a new boxed, type-erased [`", stringify!($t_name), "`].")]
41 $v fn new<T: $t_name + 'static>(v: std::sync::Arc<T>) -> Self {
42 let ptr = std::sync::Arc::into_raw(v) as *const T as *const ();
43 let vtable = &VTable {
44 $(
45 $f: |ptr, $($p),*| unsafe {
46 let arc = std::mem::ManuallyDrop::new(std::sync::Arc::<T>::from_raw(ptr as *const T));
47 <T as $t_name>::$f(&arc, $($p),*)
48 },
49 )*
50 $($(
51 $f_impl: |ptr, $($p_impl),*| unsafe {
52 let arc = std::mem::ManuallyDrop::new(std::sync::Arc::<T>::from_raw(ptr as *const T));
53 <T as $impl_name>::$f_impl(&*arc, $($p_impl),*)
54 },
55 )*)*
56 __type_id: || std::any::TypeId::of::<T>(),
57 __type_name: || std::any::type_name::<T>(),
58 __incref: |ptr| unsafe {
59 std::sync::Arc::<T>::increment_strong_count(ptr as *const T);
60 },
61 __decref: |ptr| unsafe {
62 std::sync::Arc::from_raw(ptr as *const T);
63 },
64 };
65
66 Self { ptr, vtable: vtable as *const VTable as *const () }
67 }
68
69 $(
70 #[doc = concat!(
71 "Calls [`", stringify!($t_name), "::", stringify!($f),
72 "`] of the underlying boxed value."
73 )]
74 $v fn $f(&self, $($p: $t),*) $(-> $r)? {
75 (vt(self).$f)(self.ptr, $($p),*)
76 }
77 )*
78
79 $v fn type_name(&self) -> &'static str {
81 (vt(self).__type_name)()
82 }
83
84 $v fn downcast_ref<T: 'static>(&self) -> Option<&T> {
88 if (vt(self).__type_id)() == std::any::TypeId::of::<T>() {
89 unsafe {
90 return Some(&*(self.ptr as *const T));
91 }
92 }
93
94 None
95 }
96
97 $v fn downcast<T: 'static>(&self) -> Option<Arc<T>> {
101 if (vt(self).__type_id)() == std::any::TypeId::of::<T>() {
102 unsafe {
103 std::sync::Arc::<T>::increment_strong_count(self.ptr as *const T);
104 return Some(std::sync::Arc::<T>::from_raw(self.ptr as *const T));
105 }
106 }
107
108 None
109 }
110
111 $v fn is<T: 'static>(&self) -> bool {
115 self.downcast::<T>().is_some()
116 }
117 }
118
119 impl Clone for $erased_t_name {
120 fn clone(&self) -> Self {
121 (vt(self).__incref)(self.ptr);
122 Self {
123 ptr: self.ptr,
124 vtable: self.vtable,
125 }
126 }
127 }
128
129 impl Drop for $erased_t_name {
130 fn drop(&mut self) {
131 (vt(self).__decref)(self.ptr);
132 }
133 }
134
135 impl<T: $t_name + 'static> From<Arc<T>> for $erased_t_name {
136 fn from(value: Arc<T>) -> Self {
137 $erased_t_name::new(value)
138 }
139 }
140
141 $(
142 impl $impl_name for $erased_t_name {
143 $(
144 fn $f_impl(&self, $($p_impl: $t_impl),*) $(-> $r_impl)? {
145 (vt(self).$f_impl)(self.ptr, $($p_impl),*)
146 }
147 )*
148 }
149 )*
150 };
151 };
152}