Macro, #derive[Eq, PartialEq] for some calls

I have a macro like:

macro_rules! gen_pt2 {
    ($pt_name:ident, $align:literal, $type_name:ident) => {
        // define the struct
        #[repr(align($align), C)]
        #[derive(Clone, Copy, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
        pub struct $pt_name([$type_name; 2]);
  ...
  }
}

which I use as:

gen_pt2!(Pt2i8, 2, i8);
gen_pt2!(Pt2i16, 4, i16);
gen_pt2!(Pt2i32, 8, i32);
gen_pt2!(Pt2i64, 16, i64);

gen_pt2!(Pt2f32, 8, f32);
gen_pt2!(Pt2f64, 16, f64);

Now, I want to add Eq, PartialEq for the i8, i16, i32, i64 definitions, but not for the f32,f64 definitions.

What is the best way to modify the macro definition / how we call the macro to make this happen?

Duplicating the macro is not preferred as the "..." is 100+ lines.

You can make separate macros where the larger one invokes the smaller one for the common subset.

One way to do this would be, if we had arbitrary macro call-sites, to write a helper macro that would do something like:

macro_rules! maybe_Eq {
    (
        $(i8)? $(i16)? $(i32)? $(i64)?
    ) => (
        Eq,
    );

    (
        $(f32)? $(f64)?
    ) => (
        /* Nothing */
    );
}

And then use it like:

#[derive(Clone, Copy, maybe_Eq!($type_name) PartialEq, …]

But that doesn't work since, among other things, a derive name is neither a valid macro-call site nor something a macro can expand to.

When hitting this kind of situations, there is a big hammer that always works, at the price of being a tad cumbersome: CPS / callback-style macros:

macro_rules! with_maybe_Eq {
    (
        $(i8)? $(i16)? $(i32)? $(i64)?,
        $($rules:tt)*
    ) => (
        macro_rules! __emit__ { $($rules)* }
        __emit__! { Eq }
    );

    (
        $(f32)? $(f64)?,
        $($rules:tt)*
    ) => (
        macro_rules! __emit__ { $($rules)* }
        __emit__! { /* nothing */ }
    );
}

macro_rules! gen_pt2 {(
    $pt_name:ident, $align:literal, $type_name:ident $(,)?
) => (
    with_maybe_Eq! { $type_name, ( $($Eq:ident)? ) => (
        // define the struct
        #[repr(align($align), C)]
        #[derive(Clone, Copy, Debug, $($Eq ,)? PartialEq, serde::Serialize, serde::Deserialize)]
        pub struct $pt_name([$type_name; 2]);
    )}

    ...
)}

An intermediate solution for your situation would be to rename your current big macro so as to make it the internal one, and then add an extra internal parameter to it:

- macro_rules! gen_pt2 {
+ macro_rules! __gen_pt2__aux__ {
-     ($pt_name:ident, $align:literal, $type_name:ident                ) => (
+     ($pt_name:ident, $align:literal, $type_name:ident, $($Eq:ident)? ) => (
          // define the struct
          #[repr(align($align), C)]
-         #[derive(Clone, Copy, Debug,           PartialEq, serde::Serialize, serde::Deserialize)]
+         #[derive(Clone, Copy, Debug, $($Eq ,)? PartialEq, serde::Serialize, serde::Deserialize)]
          pub struct $pt_name([$type_name; 2]);

          ...
      );
  }

And then define the macro front-end:

macro_rules! gen_pt2 {
    ($pt_name:ident, $align:literal, f32) => (
        __gen_pt2__aux__! { $pt_name, $align, f32, /* nothing */ }
    );
    ($pt_name:ident, $align:literal, f64) => (
        __gen_pt2__aux__! { $pt_name, $align, f64, /* nothing */ }
    );
    ($pt_name:ident, $align:literal, $iN:ident) => (
        __gen_pt2__aux__! { $pt_name, $align, $iN, Eq }
    );
}

This involves a bit of code repetition (in the gen_pt2 branches), but is way more readable than the uncommon macro CPS :sweat_smile:

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.