An approach to defining numbered implementations is to use a recursive macro. A unique number can be created by counting arguments, in this case counting trailing arguments.
The problem with this, is the indices are reversed where the first struct has the largest number, the last struct zero.
If you only need the numbers to be unique, it wont matter, however in this case I want each structs index to match the order its passed to the macro.
Input arguments can be reversed using a recursive macro, see this example.
Using this macro, its possible to write a generic macro:
apply_args_reverse!(macro_name, arg1 arg2 arg3)
Which expands into:
macro_name!(arg3 arg2 arg1)
Of course thats not very useful on its own, but it can be useful if the arguments aren't written directly, but passed as arguments.
This can be used to create make a macro that expands with the number of each argument as follows:
struct Foo {_var: bool}
struct Bar {_var: u8}
struct Baz {_var: i16}
trait NumberStruct {
fn struct_number() -> usize;
}
macro_rules! count_args_space {
() => {0_usize};
($_head:tt $($tail:tt)*) => {1_usize + count_args_space!($($tail)*)};
}
macro_rules! number_structs_impl {
(@single $t:tt $($tail:tt)*) => (
impl NumberStruct for $t {
fn struct_number() -> usize {
return count_args_space!($($tail)*);
}
}
);
() => {};
($head:tt $($tail:tt)*) => {
number_structs_impl!(@single $head $($tail)*);
number_structs_impl!($($tail)*);
};
}
macro_rules! apply_args_reverse {
($macro_id:tt [] $($reversed:tt)*) => {
$macro_id!($($reversed) *);
};
($macro_id:tt [$first:tt $($rest:tt)*] $($reversed:tt)*) => {
apply_args_reverse!($macro_id [$($rest)*] $first $($reversed)*);
};
// Entry point, use brackets to recursively reverse above.
($macro_id:tt, $($t:tt)*) => {
apply_args_reverse!($macro_id [ $($t)* ]);
};
}
// Note that both commands below work, and can be swapped to reverse argument order.
// number_structs_impl!(Foo Bar Baz);
apply_args_reverse!(number_structs_impl, Foo Bar Baz);
fn main() {
// see if the numbers are correct
macro_rules! print_numbers {
($($t:tt)*) => ($(
print!("{}:{} ", stringify!($t), $t::struct_number());
)*)
}
print_numbers!(Baz Bar Foo);
println!();
}
Notice the statements:
number_structs_impl!(Foo Bar Baz);
... and
apply_args_reverse!(number_structs_impl, Foo Bar Baz);
... are interchangeable, swapping which is commented reverses the order of numbers assigned to each struct.
Note: keeping my other answer, while this is more concise, it's also more fragile, prone to hard-to-troubleshoot problems, since macro expansion gets deeply nested (I found this while getting it to work at least).