Declarative macro to apply a macro to each item?

I am aware about order that macros are expanded.
Is it possible to write a generic macro which apply macro, lets say generate, to items generated by macro primitives?

for_each!( generate, primitives );

where for_each, generate, primitives are 3 macroses.

Playground

Why not use repetition?

macro_rules! generate {
    ( $($Type:ident),+ ) => {
       $( let _x: $Type; )+
    };
}

fn main() {
    generate!(i8, i16, i32, i64, i128, u8, u16, u32, u64, u128, f32, f64);
}

Repetition is a generic technique for macros to do for_each stuff.

3 Likes

You might be looking for something called the Callback Pattern. This is where you modify the inner macros (i.e. generate and primitives`) to take the name of a callback macro which will be invoked on each item inside them.

It's a little different from your original example, but could you adapt something like this to your use case?

macro_rules! apply {
    ($callback:ident, $($args:tt),*) => {
        $(
            $callback!($args);
        )*
    }
}

macro_rules! for_each_primitive {
    ($callback:ident) => {
        apply!($callback, i8, i16, i32, i64, i128, u8, u16, u32, u64, u128, f32, f64);
    };
}

macro_rules! generate {
    ( $Type:ident ) => {
        let x: $Type; /* some code */
    };
}

fn main() {
    for_each_primitive!(generate);
}

(playground)

I wrote a worked example for this sort of thing a while back:

7 Likes

Thank you. Problem of the solution it does not allow to move the list into a macro and reuse it.

Thanks, that really solve the original problem. But in my case I have extra arguments applied to each type and attempt to use prefix in apply! does not work and it looks like a problem without solution.

Here:

macro_rules! apply
{
  ( $Callback:ident @PREFIX( $( $Prefix : tt )* ) @ARGS( $( $Arg:tt ),* ) ) =>
  {
    $( $Callback!( @PREFIX( $( $Prefix )* ) @ARGS( $Arg ) ); )* /* ! meta-variable `Prefix` repeats 0 times, but `Arg` repeats 12 times */
    // $( $Callback!( @PREFIX() @ARGS( $Arg ) ); )* /* <- works if prefix is not paseed */
  }
}

Is that solvable?

Payground

Yes. Have a close look at the @ARGS( $Arg ) and compare it to @PREFIX( $( $Prefix )* ). You've also wrapped the entire Callback!() call in a level of repetition, but $Arg and $Prefix are unrelated.

macro_rules! apply
{
    (
        $Callback:ident
        @PREFIX( $( $Prefix : tt )* )
        @ARGS( $( $Arg:tt ),* )
    ) =>
    {
        $Callback!(
            @PREFIX( $( $Prefix )* )
            @ARGS( $( $Arg )* )
        );
  }
}

Making those two tweaks gave me the following output:

i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64
2 Likes

Thank you. It runs, but that solution change a bit semantics.
Output should be

i8
hello!
i16
hello!
...

But it is

i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64

It would not be possible to repeat different times in a single repetition.

But it can be possible to repeat a single token in a single repetition.

1 Like