Search code examples
c++boostc-preprocessorvariadic-macrosboost-preprocessor

How to "map" a variadic macro with boost preprocessor?


Say I have a macro F:

#define F(x) /*...*/

and a macro G that takes one or more arguments:

#define G(...) /*...*/

and I want to write a macro H that takes one or more arguments that expands to G with F applied to each argument:

#define H(...) /* G(F(arg1),F(arg2),...,F(argn)) */

How can H be implemented with boost.preprocessor ?

For example:

#include <boost/preprocessor.hpp>

#define F(x) A x

#define G(...) B __VA_ARGS__ C

#define H(...) ???

H(X, Y, Z)

The final line should preprocess to:

B A X, A Y, A Z C

What code should replace the ??? ?

Update: This similar question C Macros: How to map another macro to variadic arguments? describes how to do it without boost preprocessor, but mentions EVAL and MAP from boost preprocessor but I can't seem to find either of those in the documentation: https://www.boost.org/doc/libs/1_78_0/libs/preprocessor/doc/index.html Am I blind?

Update 2: I've got it working thanks to @Artyer. For posterity, here is the code of the final solution of the enclosing use case mentioned in the comments (generating comparison operators for a struct):

#pragma once

#include <boost/preprocessor.hpp>
#define COMPARISON_H_STRUCT_MEMBER_LOOKUP(member) (_struct.member)

#define COMPARISON_H_COMMA_SEPARATED_MAP(r, macro, i, elem) \
  BOOST_PP_COMMA_IF(i) macro(elem)

#define COMPARISON_H_DECL_TIE_FOR(ClassName, seq)                              \
  inline auto comparison_h_tie_struct(const ClassName& _struct) {              \
    return std::tie(BOOST_PP_SEQ_FOR_EACH_I(COMPARISON_H_COMMA_SEPARATED_MAP,  \
                            COMPARISON_H_STRUCT_MEMBER_LOOKUP, \
                                    seq));                             \
  }

#define DECL_STRUCT_EQ_OPS(ClassName, ...)                                    \
  COMPARISON_H_DECL_TIE_FOR(ClassName, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__)) \
  COMPARISON_H_DECL_OP(ClassName, ==)                                         \
  COMPARISON_H_DECL_OP(ClassName, !=)

#define DECL_STRUCT_CMP_OPS(ClassName, ...)                                   \
  COMPARISON_H_DECL_TIE_FOR(ClassName, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__)) \
  COMPARISON_H_DECL_OP(ClassName, ==)                                         \
  COMPARISON_H_DECL_OP(ClassName, !=)                                         \
  COMPARISON_H_DECL_OP(ClassName, <)                                          \
  COMPARISON_H_DECL_OP(ClassName, >)                                          \
  COMPARISON_H_DECL_OP(ClassName, <=)                                         \
  COMPARISON_H_DECL_OP(ClassName, >=)

Example usage:

struct TestStruct {
  int member1, member2, member3;
};

DECL_STRUCT_CMP_OPS(TestStruct, member1, member2, member3)

int main() {
  TestStruct a, b;
  for (int i = 0; i < 27; i++)
    for (int j = 0; j < 27; j++) {
      a.member1 = (i / 9) % 3;
      b.member1 = (j / 9) % 3;
      a.member2 = (i / 3) % 3;
      b.member2 = (j / 3) % 3;
      a.member3 = (i / 1) % 3;
      b.member3 = (j / 1) % 3;

      assert((i == j) == (a == b));
      assert((i != j) == (a != b));
      assert((i < j) == (a < b));
      assert((i > j) == (a > b));
      assert((i <= j) == (a <= b));
      assert((i >= j) == (a >= b));
    }
}

Update 3: Added FOR_EACH_I fix.


Solution

  • You can use BOOST_PP_SEQ_FOR_EACH_I to do this "mapping" operation:

    #define VARIADIC_MAP(r, macro, i, elem) BOOST_PP_COMMA_IF(i) macro(elem)
    #define H(...) G(BOOST_PP_SEQ_FOR_EACH_I(VARIADIC_MAP, F, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__)))
    

    The BOOST_PP_COMMA_IF(i) prepends a , before every value except the first so this expands to what you want.