3

I have a list defined as a preprocessor value #define LIST 0, 1, 2, 3, 4, 5, 6, 7, 8, 9. I want to write a macro that gets an index 0 or 1 and evaluates to a subset of the LIST such that for index 0 it will evaluate to 0, 2, 4, 6, 8 and for index 1 it will evaluate to 1, 3, 5, 7, 9. It is guaranteed that LIST's length is even but I don't know the content in advance (it is auto generated by the users of the library I supply). This question is a follow up on this question

#define LIST 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
#define MACRO(index) \
use LIST and index

// For LIST that given in the example
printf("%d %d %d %d %d\n", MACRO(0)); // print 0 2 4 6 8
printf("%d %d %d %d %d\n", MACRO(1)); // print 1 3 5 7 9
Constructor
  • 7,273
  • 2
  • 24
  • 66
e271p314
  • 3,841
  • 7
  • 36
  • 61
  • 2
    Do you know the length? Is the `printf` the real use case? How do you know how much `%d` do you need? Is this C or C++11? – mch Dec 18 '17 at 12:47
  • The `printf` is just an example, I don't know the length in advance, I just know it is even. For the sake of this question you can assume LIST is only numbers, you can see the question I added in the link to get more context about the problem I want to solve. I'm using c++11 (g++ 5.3.1) – e271p314 Dec 18 '17 at 12:49

1 Answers1

2

This should be doable with help of Boost.Preprocessor:

#define OUTPUT_CORRECT_OFFSET(r, offset, idx, elem) \
  BOOST_PP_EXPR_IF(BOOST_PP_EQUAL(BOOST_PP_MOD(idx, 2), offset), (elem))

#define MACRO(index) \
  BOOST_PP_SEQ_ENUM( \
    BOOST_PP_SEQ_FOR_EACH_I( \
      OUTPUT_CORRECT_OFFSET, \
      index, \
      BOOST_PP_TUPLE_TO_SEQ((LIST)) \
    ) \
  )

[Live example]

It works by turning LIST into a Boost.Preprocessor sequence, then iterating over it and keeping only those elements whose index modulo 2 matches the index parameter of MACRO, and finally turning the resulting sequence back into a comma-separated list.


Note that there is a limit on the maximum size of a Boost.Preprocessor tuple. In version 1.66.0 (the latest as of this writing), it's 64. If your LIST is larger, it cannot be treated as a tuple (which the code above does by using (LIST) to put a pair of parentheses around it).

If you have control over the format of LIST, you could change it do be a Boost.Preprocessor sequence directly:

#define LIST (0)(1)(2)(3)(4)(5)(6)(7)(8)(9)

While sequences have a size limit too, it's far larger (256 in Boost 1.66.0). The macro would then change as follows:

#define MACRO(index) \
  BOOST_PP_SEQ_ENUM( \
    BOOST_PP_SEQ_FOR_EACH_I( \
      OUTPUT_CORRECT_OFFSET, \
      index, \
      LIST \
    ) \
  )

If the sequence limit is still not enough, you will have to consider a more powerful code generation technique, such as a stand-alone macro processor outside the native C++ framework.

Angew is no longer proud of SO
  • 167,307
  • 17
  • 350
  • 455
  • I guess I would never get to this solution myself, thank you very much – e271p314 Dec 18 '17 at 13:02
  • http://www.boost.org/doc/libs/1_60_0/libs/preprocessor/doc/ref/limit_tuple.html After LIST is longer than 64 the code does not compile – e271p314 Dec 20 '17 at 16:50
  • @e271p314 Do you have control over the format of `LIST`? If so, you could make it follow the format of a Boost.Preprocessor sequence (`#define LIST (0)(1)(2)(3)`), those have a limit of 256. But it's true there's only so much you can do with the preprocessor anyway. If you need more, a more powerful code generation technique (outside C++ itself) may be in order. – Angew is no longer proud of SO Dec 21 '17 at 08:27
  • I can control the LIST format, when I tried you suggestion my code didn't compile. The reason is that the first sub set of values (0, 2, 4...) is placed in an enum structure (`enum Vals { MACRO(0) };`) and the compiler doesn't like the parenthesis inside enum (`enum Vals { (A), (B) };` doesn't compile while `enum Vals { A, B };` does) – e271p314 Dec 21 '17 at 09:31
  • I also tried to replace the value of BOOST_PP_LIMIT_TUPLE to 128, this also didn't help. Pretty stuck now, but I like your idea anyway :-) – e271p314 Dec 21 '17 at 09:35
  • @e271p314 For the limit: the value of the macro provides information, it does not control anything. It just tells you that the internal mechanism of PP macros is limited to 64-element tuples. There might be a way to increase that limit when configuring Boost, not sure. – Angew is no longer proud of SO Dec 21 '17 at 09:38
  • @e271p314 The output of `MACRO` shouldn't change regardless of whether its original input is a tuple or sequence (assuming you replace `BOOST_PP_TUPLE_TO_SEQ((LIST))` with just `LIST` in such case); what exactly did you do that it produced the parens in output? – Angew is no longer proud of SO Dec 21 '17 at 09:40
  • you are hack of a programmer! I replaced `BOOST_PP_TUPLE_TO_SEQ((LIST))` with `(LIST)`, because the I got only the compilation error message I guessed what was the problem, you solved the problem without seeing the code or the compilation error. RESPECT! – e271p314 Dec 21 '17 at 09:54
  • @e271p314 I will edit the answer to incorporate some of this. – Angew is no longer proud of SO Dec 21 '17 at 09:55