Skip to content

Instantly share code, notes, and snippets.

@Ryex
Last active April 29, 2024 04:13
Show Gist options
  • Save Ryex/d48f8a5f857ab90bd003e89372a6aa8d to your computer and use it in GitHub Desktop.
Save Ryex/d48f8a5f857ab90bd003e89372a6aa8d to your computer and use it in GitHub Desktop.
use macro_rules_attribute::apply;
use macro_rules_attribute::derive;
macro_rules! object_trait {
(@intf $trait_name:ident $trt:path) => {
paste::paste! {
#[allow(missing_docs)]
pub type [<$trt Ref>]<'a, T> = &'a dyn $trt<ID = <T as $trait_name>::ID>;
#[allow(missing_docs)]
pub type [<$trt RefMut>]<'a, T> = &'a mut dyn $trt<ID = <T as $trait_name>::ID>;
}
};
( $trait_name:ident {$($trt:path),*}) => {
$(
object_trait!{@intf $trait_name $trt}
)*
pub trait $trait_name {
type ID;
fn id(&self) -> &Self::ID;
fn as_object(&self) -> &dyn $trait_name<ID = Self::ID>;
fn as_object_mut(&mut self) -> &mut dyn $trait_name<ID = Self::ID>;
paste::paste!{$(
#[inline(always)]
fn [<as_ $trt:lower>](&self) -> Option<[<$trt Ref>]<Self>> {
None
}
#[inline(always)]
fn [<as_ $trt:lower _mut>](&mut self) -> Option<[<$trt RefMut>]<Self>> {
None
}
)*}
}
};
}
pub(in crate) use object_trait;
macro_rules! ObjectInterface {
{
#[custom(implements($trait_name:ident {$($trt:path),*}))]
$( #[$attr:meta] )*
$viz:vis struct $struct:ident {
$(
$(#[#field1:meta])*
$field1_viz:vis
$field1_name:ident : $field1_ty:ty,
),*
#[custom(object_id)]
$(#[$id_attr:meta])*
$id_viz:vis $field_id:ident: $id_typ:ty,
$(
$(#[#field2:meta])*
$field2_viz:vis
$field2_name:ident : $field2_ty:ty,
),*
}
} => {
impl $trait_name for $struct {
type ID = $id_typ;
fn id(&self) -> &Self::ID {
&self.$field_id
}
#[inline(always)]
fn as_object(&self) -> &dyn $trait_name<ID = Self::ID> {
self
}
#[inline(always)]
fn as_object_mut(&mut self) -> &mut dyn $trait_name<ID = Self::ID> {
self
}
paste::paste!{$(
#[inline(always)]
fn [<as_ $trt:lower>](&self) -> Option<[<$trt Ref>]<Self>> {
Some(self)
}
#[inline(always)]
fn [<as_ $trt:lower _mut>](&mut self) -> Option<[<$trt RefMut>]<Self>> {
Some(self)
}
)*}
}
};
}
pub(in crate) use ObjectInterface;
macro_rules! ObjectTrait {
{
#[custom(object_trait = $trait_name:ident)]
$(#[$attr:meta])*
$viz:vis trait $trt:ident $(: $($bound:path)* )? {
$($tbody:tt)*
}
} => {
$(#[$attr])*
$viz trait $trt: $($($bound)* +)? $trait_name {
$($tbody)*
}
};
}
pub(in crate) use ObjectTrait;
object_trait!(Object {Greeter, Memory});
#[apply(ObjectTrait!)]
#[custom(object_trait = Object)]
pub trait Greeter {
fn hello(&self) -> String;
}
#[apply(ObjectTrait!)]
#[custom(object_trait = Object)]
pub trait Memory {
fn get_memory(&self) -> &Vec<u32>;
fn set_memory(&mut self, index: usize, val: u32);
}
#[derive(ObjectInterface!)]
#[custom(implements(Object {Greeter}))]
struct TypeA {
#[custom(object_id)]
id: u32,
}
impl Greeter for TypeA {
fn hello(&self) -> String {
"hello World".to_owned()
}
}
#[derive(ObjectInterface!)]
#[custom(implements(Object {Memory, Greeter}))]
struct TypeB {
#[custom(object_id)]
id: u32,
pub mem: Vec<u32>,
}
impl Greeter for TypeB {
fn hello(&self) -> String {
"hello rachel".to_owned()
}
}
impl Memory for TypeB {
fn get_memory(&self) -> &Vec<u32> {
&self.mem
}
fn set_memory(&mut self, index: usize, val: u32) {
self.mem[index] = val;
}
}
#[derive(ObjectInterface!)]
#[custom(implements(Object {Memory}))]
struct TypeC {
#[custom(object_id)]
id: u32,
pub mem: Vec<u32>,
}
impl Memory for TypeC {
fn get_memory(&self) -> &Vec<u32> {
&self.mem
}
fn set_memory(&mut self, index: usize, val: u32) {
self.mem[index] = val;
}
}
fn main() {
let a = Box::new(TypeA { id: 1 });
let b = Box::new(TypeB {
id: 2,
mem: vec![0, 2, 4],
});
let c = Box::new(TypeC {
id: 3,
mem: vec![5, 7, 9],
});
let mut container: Vec<Box<dyn Object<ID = u32>>> = vec![a, b, c];
for obj in container.iter_mut() {
let greeter = obj.as_greeter();
if let Some(greeter) = greeter {
println!("generic {}", greeter.hello());
} else {
println!("no greeter");
}
let memory = obj.as_memory_mut();
if let Some(memory) = memory {
memory.set_memory(0, 200);
println!("generic {:?}", memory.get_memory());
} else {
println!("no memory");
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment