1

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.

  • 1
    Complex function-like macros should almost always be turned into actual functions. What are these macros really for? Or are you just asking out of curiosity? Curiosity is okay, but then please state it in the question. Otherwise if you have an underlying problem you want to solve then please ask about it directly instead (or your question would be a [xy problem](https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem)). – Some programmer dude Feb 10 '22 at 07:35
  • 1
    @Someprogrammerdude: I'm writing a macro in C++17 that takes the name of an aggregate class type (a simple struct) and the names of its members and generates (ADL) definitions of `operator==`, `operator!=`, `operator<`, `operator>`, `operator<=` and `operator>=`, by expanding to a `std::tie` over its members. It's not possible to do that from an actual function, as the list of member names (just identifiers) will fail at name lookup. They need to be prefixed with `&ClassName::`, what I am using the macro map for. You can see similar usage of macros in (for example) boost.python or pybind11 – Andrew Tomazos Feb 10 '22 at 07:45
  • 1
    That's a good reason for macros, but it should really be part of the question itself. Please take some time to read [the help pages](http://stackoverflow.com/help), take the SO [tour], read [ask], as well as [this question checklist](https://codeblog.jonskeet.uk/2012/11/24/stack-overflow-question-checklist/). – Some programmer dude Feb 10 '22 at 07:50

1 Answers1

1

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.

ecatmur
  • 152,476
  • 27
  • 293
  • 366
Artyer
  • 31,034
  • 3
  • 47
  • 75
  • Works, thanks. I added the enclosing solution as **Update 2**. Feel free to take a look and share any comments. – Andrew Tomazos Feb 10 '22 at 08:56
  • `BOOST_PP_COMMA_IF(BOOST_PP_DEC(r))` is incorrect; `r` is the repetition point for passing to `BOOST_PP_SEQ_FOR_EACH_R`, so it may work but only by accident. You should use `BOOST_PP_SEQ_FOR_EACH_I ` and `BOOST_PP_COMMA_IF(i)`. – ecatmur Feb 24 '22 at 16:51
  • Yes, it was working somehow just by accident (and only on 1.78.0 / Linux. 1.71 Linux and 1.78 Windows for each it doesn't work). I'll try the FOR_EACH_I solution. – Andrew Tomazos Mar 01 '22 at 00:44