clay_core/
select.rs

1/// The basic macro for making a union of entities.
2///
3/// This macro is the most generic version.
4/// You can find more specific versions in `shape`, `material` and `object` modules.
5///
6/// You can read more about the technique [here](https://clay-rs.github.io/knowledge/#objects).
7#[macro_export]
8macro_rules! instance_select {
9    ($Select:ident: $Base:path: $Class:ty { $( $Enum:ident($Param:ident = $Instance:ty) ),+ $(,)? }) => {
10        pub enum $Select<
11            $( $Param:
12                $crate::Pack +
13                $crate::Instance<$Class> +
14                $Base
15                = $Instance
16            ),+
17        > {
18            $( $Enum($Param), )+
19        }
20
21        impl $Select {
22            #[allow(unused_assignments)]
23            fn index(&self) -> u32 {
24                let mut i = 0;
25                $(
26                    if let $Select::$Enum(_) = self {
27                        return i;
28                    }
29                    i += 1;
30                )+
31                unreachable!()
32            }
33
34            #[allow(unused_assignments)]
35            fn method_source(method: &str) -> String {
36                use $crate::class::*;
37                let cpref = format!(
38                    "{}_{}",
39                    <$Class>::name(),
40                    method,
41                ).to_uppercase();
42
43                let mut cases = Vec::new();
44                let mut i = 0;
45                $(
46                    let inst_name = <$Instance as $crate::Instance<$Class>>::inst_name();
47                    cases.push([
48                        format!("\tif (sel_idx == {}) {{", i),
49                        format!(
50                            "\t\treturn {}_{}({}_ARGS_B(1, 0));",
51                            inst_name, method, cpref,
52                        ),
53                        "\t}".to_string(),
54                    ].join("\n"));
55                    i += 1;
56                )+
57                let cases_text = cases.join(" else\n");
58                [
59                    &format!(
60                        "{}_RET {}_{}({}_ARGS_DEF) {{",
61                        cpref, Self::inst_name(), method, cpref,
62                    ),
63                    "\tint sel_idx = ibuf[0];",
64                    &cases_text,
65                    &format!("\treturn {}_RET_BAD;", cpref),
66                    "}",
67                ].join("\n")
68            }
69        }
70
71        impl $crate::Instance<$Class> for $Select {
72            fn source(cache: &mut std::collections::HashSet<u64>) -> String {
73                use $crate::{TypeHash, class::*};
74                if !cache.insert(Self::type_hash()) {
75                    return String::new()
76                }
77                let mut ms = Vec::new();
78                for method in <$Class>::methods().into_iter() {
79                    ms.push(Self::method_source(&method));
80                }
81                [
82                    $( <$Instance as $crate::Instance<$Class>>::source(cache), )+
83                    ms.join("\n"),
84                ].join("\n")
85            }
86
87            fn inst_name() -> String {
88                use $crate::TypeHash;
89                format!("__select_{:x}", Self::type_hash())
90            }
91        }
92
93        impl $crate::Pack for $Select {
94            fn size_int() -> usize {
95                let sizes = [
96                    $( <$Instance>::size_int(), )+
97                ];
98                1 + *sizes.iter().max().unwrap()
99            }
100            fn size_float() -> usize {
101                let sizes = [
102                    $( <$Instance>::size_float(), )+
103                ];
104                *sizes.iter().max().unwrap()
105            }
106            fn pack_to(&self, mut buffer_int: &mut [i32], buffer_float: &mut [f32]) {
107                use $crate::pack::*;
108                self.index().pack_int_to(buffer_int);
109                buffer_int = &mut buffer_int[1..];
110                match self {
111                    $( $Select::$Enum(x) => x.pack_to(buffer_int, buffer_float), )+
112                }
113            }
114        }
115
116        $(
117            impl From<$Instance> for $Select {
118                fn from(origin: $Instance) -> Self {
119                    $Select::$Enum(origin)
120                }
121            }
122        )+
123    };
124}
125
126#[cfg(test)]
127mod check {
128    use crate::{
129        shape::{
130            Shape, ShapeClass,
131            test::TestShape,
132        },
133        instance_select,
134    };
135
136    instance_select!(
137        TestSelect: Shape: ShapeClass {
138            Shape1(T1 = TestShape<i32>),
139            Shape2(T2 = TestShape<f32>),
140        }
141    );
142}