sjm324 sjm324 - 21 days ago 7
C++ Question

BOOST_PP_SEQ_ELEM with BOOST_PP_SEQ_ADD in nested macro?

I'm confident that you can use

BOOST_PP_SEQ_ELEM(BOOST_PP_ADD(n,1),sequence)
, but I can't quite seem to pinpoint why the
EXTRACT
macro below is unable to compile with "too few arguments provided to function like macro".

#include <boost/preprocessor/seq/elem.hpp>
#include <boost/preprocessor/repetition/repeat.hpp>
#include <boost/preprocessor/seq/size.hpp>
#include <boost/preprocessor/stringize.hpp>
#include <boost/preprocessor/control/iif.hpp>
#include <boost/preprocessor/arithmetic/mod.hpp>
#include <boost/preprocessor/logical/not.hpp>
#include <boost/preprocessor/arithmetic/add.hpp>

#include <iostream>

// attempt 2.7 beta
#define EXTRACT(z, n, args) \
BOOST_PP_IIF(\
/* on every third index */ \
BOOST_PP_NOT(BOOST_PP_MOD(n,3)),\
/* check the flag */ \
BOOST_PP_IIF(BOOST_PP_SEQ_ELEM(n,args),\
/*BOOST_PP_SEQ_ELEM(n,args),*/ \
BOOST_PP_SEQ_ELEM(BOOST_PP_ADD(n,1),args),\
"narp"\
),\
)

// absurd wrapper for extract
#define ALL_ARGS(args) BOOST_PP_REPEAT(BOOST_PP_SEQ_SIZE(args),EXTRACT,args)

// every third element is a "flag"
// v v
#define MY_SEQUENCE (1)(int)(z)(0)(float)(y)

int main(int argc, const char **argv) {
std::cout <<
BOOST_PP_STRINGIZE(ALL_ARGS(MY_SEQUENCE))
<< std::endl;
}


The example is far from what I want to do with this, but right now I'm just trying to figure out how to get the actual
int
and
z
or
float
and
y
.

If it matters, the actual goal is instantiating templates. I have a sequence of template classes, but am unable to figure out how to instantiate

// vvvvvvvvvvvv
template <class X> void foo(SomeThing<X> varName);


So the flag here is letting me know if
SomeThing
needs
<X>
or not. Perhaps there is an easier way to approach this? I'm already inside a
BOOST_PP_SEQ_FOR_EACH
for the classes in the template, so the only thing I could figure out how to do was pass this ugly arguments sequence along. FWIW I know
int<X>
isn't valid, this is just testing...

Answer Source

Diagnostic hint

In general preprocessor macros are easier to debug using the preprocessor than they are "tiny programs". For example, what you have here requires a full compilation and launching to see an issue; but if you comment out #include <iostream> and #include <boost/preprocessor/stringize.hpp>, and replace the entire main function with just ALL_ARGS(MY_SEQUENCE), then you can simply launch your preprocessor and see its output directly; no compilation/running necessary.

Not only is this quicker, but you get to play on a token level to produce things that can help you, without having to worry about producing things that will compile.

Tracing the issue

Using the transformation above I performed a single expansion of ALL_ARGS (after reproducing the problem) by changing the argument EXTRACT to EXTRACT_. This iterated just fine. Next I changed ALL_ARGS to the output of the expansion, breaking each EXTRACT_ on separate lines and adding an artificial label, then changed EXTRACT_ back to EXTRACT; e.g.:

0_  EXTRACT(2, 0, (1)(int)(z)(0)(float)(y))
1_  EXTRACT(2, 1, (1)(int)(z)(0)(float)(y))
...

Running this through the preprocessor again showed that all expansions were fine, except for the sixth one:

5_  EXTRACT(2, 5, (1)(int)(z)(0)(float)(y))

This in mind it was easy to spot. The problem is indeed with this part:

BOOST_PP_SEQ_ELEM(BOOST_PP_ADD(n,1),args)

...when EXTRACT is run with n=5, this amounts to BOOST_PP_SEQ_ELEM(6, (1)(int)(z)(0)(float)(y)). There's no element offset 6 here, so the macro crumbles. Mind you, BOOST_PP_NOT(BOOST_PP_MOD(5,3)) is 0, so the outer BOOST_PP_IIF doesn't select the inner one, but it still must evaluate it.

Alternate approach

Perhaps there is an easier way to approach this?

Absolutely. Use a different data structure. Here, your strategy is to use a sequence of size 3*n for some n, then pick out every 3 items in the sequence.

With some fiddling you can surely do this, but the entire reason you need to is because you don't really have a series of items... rather, you have a series of sets of 3 items. If you change your data structure to a sequence of 3-tuples, this becomes much much easier; all you need is an IIF, a FOR_EACH, and a couple of worker macros:

#include <boost/preprocessor/control/iif.hpp>
#include <boost/preprocessor/seq/for_each.hpp>
#define MY_SEQUENCE ((1,int,z))((0,float,y))
#define APPLY_EXTRACT(r,data,elem) EXTRACT elem
#define EXTRACT(FLAG_,TYPE_,PNAME_) BOOST_PP_IIF(FLAG_, TYPE_, "narp")
#define ALL_ARGS(args) BOOST_PP_SEQ_FOR_EACH(APPLY_EXTRACT, _, MY_SEQUENCE)

ALL_ARGS(MY_SEQUENCE)

See this on stacked-crooked