Macro_rules! for generating const-generic parameters

Hello,

I am currently working on a performance critical part of a person project (long-range router plotter for Elite: Dangerous, using parallel beam-search across and implicit graph) and i want to do some compile-time branch-elimination (at the cost of code size).

The signature for my worker functions looks like this:

pub(crate) fn route_beam_impl<
        const REFUEL: bool,
        const SHIP: bool,
        const LIMIT: bool,
    >(&self, args: ArgTuple) -> _

And contains a few ifs that disable parts of the code based on the const-generics parameters.

My idea is to generate an array of functions pointers for every combination of parameters using a macro and then at runtime computing an index into the array and calling the corresponding function (that call is outside the hot path, so the indirection doesn't matter) but i'm struggling with the implementation since I have almost no experience writing macros.

My idea was to have a macro invocation like const FN: [RouteFn;8] = generate_static_dispatch_array(Self::route_beam_impl; REFUEL, SHIP, LIMIT) which would then generate something like

 const FN: [RouteFn;8] = [
Self::route_beam_impl<false,false,false>,
Self::route_beam_impl<false,false,true>,
Self::route_beam_impl<false,true,false>,
...
];

But I'm not sure how to actually implement my idea.

Best regards,

Daniel

You could start from here:

macro_rules! comb {
    ( $( $($a:literal,)* $b:ty $(,$c:ty)* );+ ) => { comb!( $( $($a,)* false $(,$c)* );+ ; 
                                                            $( $($a,)* true $(,$c)* );+ ); };
    ( $( $($a:literal),+ );+ ) => { [$( Self::route_beam_impl::<$($a),+>(), )+] };
}

const FN: [RouteFn; 8] = comb!(bool, bool, bool);

The bool type can be replaced by any other type; it's just there to differentiate the literal true or false from what hasn't been replaced yet.

It will expand to:

const FN: [RouteFn; 8] = [Self::route_beam_impl::<false, false, false>(), Self::route_beam_impl::<true, false, false>(), Self::route_beam_impl::<false, true, false>(), Self::route_beam_impl::<true, true, false>(), Self::route_beam_impl::<false, false, true>(), Self::route_beam_impl::<true, false, true>(), Self::route_beam_impl::<false, true, true>(), Self::route_beam_impl::<true, true, true>(), ];

You can replace by something else by changing the last pattern in the macro, but it must produce a valid output in the context it's used, which is why I had to use the turbofish syntax.

The macro replaces a list of comma-separated mix or bool and true/false, those lists being separated by semicolons. When there are only true/false remaining, it goes to the 2nd pattern to create the array.

It's a little tricky, to be honest, and that won't make your code very easy to read.

PS: There's a limit to how much you can recurse into a macro, but the default limit is 128, so you should be safe.

That does exactly what i needed, thank you very much!

1 Like