specialize_call/macros.rs
1/// A macro producing the invocations of the specified generic function with specific types, depending on the value available in runtime.
2///
3/// Examples:
4/// ```rust
5///
6/// struct A<const I: usize>();
7/// struct B<const I: usize>();
8/// struct C<const I: usize>();
9///
10/// #[derive(Debug)]
11/// enum Select<const I: usize> {
12/// A,
13/// B,
14/// C,
15/// }
16///
17/// use core::any::type_name as tn;
18///
19/// #[test]
20/// fn specialize_call_1() {
21/// // there is a function `do_it` that takes one runtime argument, and one type-parameter.
22/// fn do_it<T>(_arg: usize) -> &'static str {
23/// tn::<T>()
24/// }
25///
26/// assert_eq!(
27/// specialize_call!(
28/// // invoke `do_it::<?>`
29/// do_it,
30/// // use `(1)` as the arguments list
31/// (1),
32/// // choose the type-parameter depending on this value
33/// Select::<1>::A,
34/// // the choice map
35/// [
36/// // if the provided value is `Select::A`, then invoke `do_it::<A>`
37/// (Select::<1>::A => A::<1>),
38/// // if the provided value is `Select::B`, then invoke `do_it::<B>`
39/// (Select::<1>::B => B::<1>),
40/// ]),
41/// Some(tn::<A<1>>())
42/// );
43///
44/// assert_eq!(
45/// specialize_call!(do_it, (2), Select::<1>::B, [
46/// (Select::<1>::A => A::<1>),
47/// (Select::<1>::B => B::<1>),
48/// ]),
49/// Some(tn::<B<1>>()),
50/// );
51///
52/// assert_eq!(
53/// specialize_call!(do_it, (3), Select::<1>::C, [
54/// (Select::<1>::A => A::<1>),
55/// (Select::<1>::B => B::<1>),
56/// ]),
57/// None::<&str>,
58/// );
59/// }
60/// ```
61#[macro_export]
62macro_rules! specialize_call {
63 (@single_mapping,
64 ( $($acc_p:pat),* ),
65 ( $($acc_t:ty),* ),
66 $func:tt, $args:tt, $select:tt,
67 [ ($p:pat => $($t:ty),+ ) $(, $p_t:tt)* $(,)* ],
68 $mappings:tt
69 ) => {
70 specialize_call!(@specialize_call,
71 ( $($acc_p,)* $p ),
72 ( $($acc_t,)* $($t),+ ),
73
74 $func, $args, $select,
75 $mappings
76 );
77
78 specialize_call!(@single_mapping,
79 ( $($acc_p),* ),
80 ( $($acc_t),* ),
81 $func, $args, $select,
82 [ $($p_t),* ],
83 $mappings
84 );
85 };
86 (@single_mapping,
87 $acc_p:tt, $acc_t: tt,
88 $func:tt, $args:tt, $select:tt,
89 [],
90 $mappings:tt
91 ) => {};
92
93 (@specialize_call,
94 $acc_p:tt,
95 $acc_t:tt,
96 $func:tt,
97 $args:tt,
98 $select:tt,
99
100 ($head:tt $(, $tail:tt)*)
101 ) => {
102 specialize_call!(@single_mapping,
103 $acc_p, $acc_t,
104 $func, $args, $select,
105 $head,
106 ( $($tail),* )
107 );
108 };
109 (@specialize_call,
110 $acc_p:tt,
111 $acc_t:tt,
112 $func:tt,
113 $args:tt,
114 $select:tt,
115
116 ()
117 ) => {
118 specialize_call!(@maybe_invoke,
119 $acc_p,
120 $acc_t,
121 $select,
122 $func,
123 $args
124 );
125 };
126
127 (@maybe_invoke,
128 ( $($acc_p:pat),* ),
129 ( $($acc_t:ty),* ),
130 $select:expr,
131 $func:ident,
132 ($($arg:expr),*)
133 ) => {
134 #[allow(unused_parens)]
135 if matches!($select, ( $($acc_p),* )) { break Some($func::<$($acc_t),*>( $($arg),* )); }
136 };
137
138 ($func:ident, ($($arg:expr),* $(,)*), $select:expr, $($mapping:tt),+ $(,)*) => {
139 loop {
140 specialize_call!(@specialize_call,
141 (), (),
142 $func,
143 ($($arg),*),
144 $select,
145 ( $($mapping),* )
146 );
147 break None
148 }
149 };
150}