Map Range (Rust) Macro
Below is a (rust) macro for applying another macro to a range of values 0..N
where N
is specified as a sequence of big endian octal digits. This is
primarily useful for implementing traits for fixed-sized arrays.
This macro may eventually end up in some crate but I haven't found a good place to put it (and it doesn't really deserve its own crate).
The Macro
macro_rules! map_range {
/* The actual macro */
(%# apply $tmpl:ident, $shift:expr, $offset:expr, $by:expr, (0 $($rest:tt)*)) => {
map_range!(%# apply $tmpl, $shift, $offset, $by*2, ($($rest)*));
map_range!(%# apply $tmpl, $shift, $offset + $by, $by*2, ($($rest)*));
};
(%# apply $tmpl:ident, $shift:expr, $offset:expr, $by:expr, (1 $($rest:tt)*)) => {
map_range!(%# apply $tmpl, $shift-$by, $offset, $by, (0 $($rest)*));
$tmpl!((($offset) - ($shift)));
};
(%# apply $tmpl:ident, $shift:expr, $offset:expr, $by:expr, ()) => { };
/* Convert from little endien octal to big endian binary */
(%# convert $tmpl:ident, (0 $($octal:tt)*), ($($binary:tt)*)) => {
map_range!(%# convert $tmpl, ($($octal)*), (0 0 0 $($binary)*));
};
(%# convert $tmpl:ident, (1 $($octal:tt)*), ($($binary:tt)*)) => {
map_range!(%# convert $tmpl, ($($octal)*), (1 0 0 $($binary)*));
};
(%# convert $tmpl:ident, (2 $($octal:tt)*), ($($binary:tt)*)) => {
map_range!(%# convert $tmpl, ($($octal)*), (0 1 0 $($binary)*));
};
(%# convert $tmpl:ident, (3 $($octal:tt)*), ($($binary:tt)*)) => {
map_range!(%# convert $tmpl, ($($octal)*), (1 1 0 $($binary)*));
};
(%# convert $tmpl:ident, (4 $($octal:tt)*), ($($binary:tt)*)) => {
map_range!(%# convert $tmpl, ($($octal)*), (0 0 1 $($binary)*));
};
(%# convert $tmpl:ident, (5 $($octal:tt)*), ($($binary:tt)*)) => {
map_range!(%# convert $tmpl, ($($octal)*), (1 0 1 $($binary)*));
};
(%# convert $tmpl:ident, (6 $($octal:tt)*), ($($binary:tt)*)) => {
map_range!(%# convert $tmpl, ($($octal)*), (0 1 1 $($binary)*));
};
(%# convert $tmpl:ident, (7 $($octal:tt)*), ($($binary:tt)*)) => {
map_range!(%# convert $tmpl, ($($octal)*), (1 1 1 $($binary)*));
};
(%# convert $tmpl:ident, (), $binary:tt) => {
map_range!(%# apply $tmpl, 0, 0, 1, $binary);
};
/* Public API */
($tmpl:ident @ $($num:tt)*) => {
map_range!(%# convert $tmpl, ($($num)*), ());
};
}
Example
use std::mem;
trait AsArray<T> {
fn as_array(&self) -> &T;
}
macro_rules! array_impl {
($value:expr) => {
impl<T> AsArray<[T; $value]> for [T] {
fn as_array(&self) -> &[T; $value] {
const LEN: usize = $value;
if self.len() == LEN {
unsafe { mem::transmute(self.as_ptr()) }
} else {
panic!();
}
}
}
};
}
// Call array_impl!(i) for i in 0..256. "2 0 0" means 0o200 (octal 200).
map_range!(array_impl @ 2 0 0);