svenevs
svenevs

Reputation: 863

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...

Upvotes: 2

Views: 668

Answers (1)

H Walters
H Walters

Reputation: 2674

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

Upvotes: 2

Related Questions