Boost logo

Boost Users :

From: Paul Mensonides (yg-boost-users_at_[hidden])
Date: 2002-12-09 18:09:04


"Jeff Flinn" <midnight2silverlake_at_[hidden]> wrote in message
news:000c01c29f84$ae37b0e0$ea02a8c0_at_adi.com...
> ----- Original Message -----
> From: "Paul Mensonides" <yg-boost-users_at_[hidden]>
>
> > "midnight2silverlake" <midnight2silverlake_at_[hidden]> wrote in message
> > news:asq7h1+r192_at_eGroups.com...
> > > I have what looks like a perfect application for Boost/Preprocessor.
> > > I have 192 entries in a Factory Map a portion of which are show
> > > below. I'd like to generate the permutations of the three macro
> > > inputs, but can't figure out where to start. Any help would be
> > > appreciated.
> > >
> > > #define MAP_ENTRY_MCR( aLang, aType, aAcc ) \
> > > \
> > > mMap[ CKey( aLang, aType, aAcc ) ] \
> > > = CVarX< aLang, aType, aAcc >::Make;
> > >
> > > MAP_ENTRY_MCR( DCTlanguageC, DCT_DOUBLE , DCT_NONE );
> > > MAP_ENTRY_MCR( DCTlanguageC, DCT_DOUBLE , DCT_INIT );
> > > MAP_ENTRY_MCR( DCTlanguageC, DCT_DOUBLE , DCT_ANYTIME );
> > >
> > > MAP_ENTRY_MCR( DCTlanguageC, DCT_FLOAT , DCT_NONE );
> > > ...
> > >
> > > Thanks, Jeff Flinn
> >
> > Hi Jeff,
> >
> > I want to make certain that I'm clear about what you're trying to do.
You
> > have 192 consecutive "MAP_ENTRY_MCR( ... )" with different permutions
each
> > time, right? How many different elements for each "field" do you have?
> > (i.e. how many DCT_DOUBLE, DCT_FLOAT, etc. and how many DCT_NONE,
> DCT_INIT,
> > etc.)
>
> aLang(4) => DCTlanguageAda, DCTlanguageAdsim, DCTlanguageC
> , DCTlanguageFortran
> aType(8)=> DCT_DOUBLE, DCT_FLOAT
> , DCT_INT_16, DCT_INT_32, DCT_INT_8
> , DCT_UINT_16, DCT_UINT_32, DCT_UINT_8
> aAcc(3)=> DCT_INIT, DCT_ANYTIME, DCT_NONE
>
> This accounts for 96 MAP_ENTRY_MCR's. There will actually be a 4th macro
> argument:
>
> aDim(2)=> DCT_Vector, DCT_Scalar
>
> Which accounts for the 192 entries.

Okay, so what we have here is a full-scale permutation of these four groups
of values. It doesn't matter if they are sequential or not, because we'll
deal with them by name rather than by value. (To address the topic of this
thread, you _could_ use BOOST_PP_REPEAT, but only for three dimensions, and
it wouldn't be a good idea for other reasons.)

Generally speaking, there are three good options. Only one of them is a
good option if you plan to use an EDG front-end (such as Comeau or Intel)
because the sheer size of such a cartesian product will take a while to
generate.

option #1: lists --------------->

#include <boost/preprocessor/list/for_each_product.hpp>

#define A_LANG \
    (DCTlanguageAda, (DCTlanguageAdsim, (DCTlanguageC, \
        (DCTlanguageFortran, BOOST_PP_NIL)))) \
    /**/

#define A_TYPE \
    (DCT_DOUBLE, (DCT_FLOAT, (DCT_INT_16, (DCT_INT_32, \
        (DCT_INT_8, (DCT_UINT_16, (DCT_UINT_32, \
            (DCT_UINT_8, BOOST_PP_NIL)))))))) \
    /**/

#define A_ACC \
    (DCT_INIT, (DCT_ANYTIME, (DCT_NONE, BOOST_PP_NIL))) \
    /**/

#define A_DIM (DCT_Vector, (DCT_Scalar, BOOST_PP_NIL))

#define PRODUCT(r, product) MAP_ENTRY_MCR product

#define MAP_ENTRY_MCR( aLang, aType, aAcc, aDim ) \
    mMap[ CKey( aLang, aType, aAcc ) ] \
        = CVarX< aLang, aType, aAcc >::Make; \
    /* aDim? */

BOOST_PP_LIST_FOR_EACH_PRODUCT(
    PRODUCT, 4, ( A_LANG, A_TYPE, A_ACC, A_DIM )
)

#undef A_LANG
#undef A_TYPE
#undef A_ACC
#undef A_DIM
#undef PRODUCT
#undef MAP_ENTRY_MCR

-------------------------

The second option requires the latest CVS sources of the PP Lib (i.e.
post-1.29 release). It is a variant of the above, but with a preprocessor
data type called a "sequence" that is a little simpler and more efficient
that a "list."

option #2: sequences --------------->

#include <boost/preprocessor/seq/elem.hpp>
#include <boost/preprocessor/seq/for_each_product.hpp>

#define A_LANG \
    (DCTlanguageAda)(DCTlanguageAdsim) \
    (DCTlanguageC)(DCTlanguageFortran) \
    /**/

#define A_TYPE \
    (DCT_DOUBLE)(DCT_FLOAT)(DCT_INT_16)(DCT_INT_32) \
    (DCT_INT_8)(DCT_UINT_16)(DCT_UINT_32)(DCT_UINT_8) \
    /**/

#define A_ACC \
    (DCT_INIT)(DCT_ANYTIME)(DCT_NONE) \
    /**/

#define A_DIM \
    (DCT_Vector)(DCT_Scalar) \
    /**/

#define PRODUCT(r, seq) \
    MAP_ENTRY_MCR( \
        BOOST_PP_SEQ_ELEM(0, seq), BOOST_PP_SEQ_ELEM(1, seq), \
        BOOST_PP_SEQ_ELEM(2, seq), BOOST_PP_SEQ_ELEM(3, seq) \
    ) \
    /**/

#define MAP_ENTRY_MCR( aLang, aType, aAcc, aDim ) \
    mMap[ CKey( aLang, aType, aAcc ) ] \
        = CVarX< aLang, aType, aAcc >::Make; \
    /* aDim? */

BOOST_PP_SEQ_FOR_EACH_PRODUCT(
    PRODUCT,
    (A_LANG)(A_TYPE)(A_ACC)(A_DIM)
)

#undef A_LANG
#undef A_TYPE
#undef A_ACC
#undef A_DIM
#undef PRODUCT
#undef MAP_ENTRY_MCR

-------------------------

Both of the two options above use library primitives that are built for
cartesian products such as this. However, they don't scale to this size
well on slow preprocessors (e.g. EDG and maybe GCC). Luckily, we have a
third alternative. However, this alternative is much more "invasive." It
is a technique which I call "file iteration." In effect, it iterates over a
file (or portion of a file) repeatedly with a different macro state each
iteration. Furthermore, file iteration is supported up to five
dimensions--we only have four here. I'll presume this stuff is in a header
file called "map_entry_mcr.h". This psuedo-code roughly describes the
implementation:

for (int i = 0; i < aLang.size(); ++i) {
    for (int j = 0; j < aType.size(); ++j) {
        for (int k = 0; k < aAcc.size(); ++k) {
            for (int l = 0; l < aDim.size(); ++l) {
                MAP_ENTRY_MCR(i, j, k, l);
            }
        }
    }
}

option #3: file iteration --------------->

#if !BOOST_PP_IS_ITERATING

#ifndef MAP_ENTRY_MCR_H // normal include guard
#define MAP_ENTRY_MCR_H

    #include <boost/preprocessor/array/elem.hpp>
    #include <boost/preprocessor/array/size.hpp>
    #include <boost/preprocessor/iteration/iterate.hpp>

    // ----- normal file contents go here ----- //

    #define A_LANG \
        (4, (DCTlanguageAda, DCTlanguageAdsim, \
            DCTlanguageC, DCTlanguageFortran))

    #define A_TYPE \
        (8, ( \
            DCT_DOUBLE, DCT_FLOAT, DCT_INT_16, DCT_INT_32, \
            DCT_INT_8, DCT_UINT_16, DCT_UINT_32, DCT_UINT_8 \
        ))

    #define A_ACC \
        (3, (DCT_INIT, DCT_ANYTIME, DCT_NONE))

    #define A_DIM \
        (2, (DCT_Vector, DCT_Scalar))

    #define ACCESS(array, frame) \
        BOOST_PP_ARRAY_ELEM( \
            BOOST_PP_FRAME_ITERATION(frame), \
            array \
        ) \
        /**/

    #define aLang ACCESS(A_LANG, 1)
    #define aType ACCESS(A_TYPE, 2)
    #define aAcc ACCESS(A_ACC, 3)
    #define aDim ACCESS(A_DIM, 4)

    // iterate over elements in "A_LANG"
    #define BOOST_PP_ITERATION_PARAMS_1 \
        (3, (0, BOOST_PP_ARRAY_SIZE(A_LANG) - 1, "map_entry_mcr.h"))
    #include BOOST_PP_ITERATE()

    #undef A_LANG
    #undef A_TYPE
    #undef A_ACC
    #undef A_DIM
    #undef ACCESS

    #undef aLang
    #undef aType
    #undef aAcc
    #undef aDim

    // ----- other normal file contents go here ----- //

#endif // EOF

#elif BOOST_PP_ITERATION_DEPTH() == 1

    // iterate over elements in "A_TYPE"
    #define BOOST_PP_ITERATION_PARAMS_2 \
        (3, (0, BOOST_PP_ARRAY_SIZE(A_TYPE) - 1, "map_entry_mcr.h"))
    #include BOOST_PP_ITERATE()

#elif BOOST_PP_ITERATION_DEPTH() == 2

    // iterate over elements in "A_ACC"
    #define BOOST_PP_ITERATION_PARAMS_3 \
        (3, (0, BOOST_PP_ARRAY_SIZE(A_ACC) - 1, "map_entry_mcr.h"))
    #include BOOST_PP_ITERATE()

#elif BOOST_PP_ITERATION_DEPTH() == 3

    // iterate over elements in "A_DIM"
    #define BOOST_PP_ITERATION_PARAMS_4 \
        (3, (0, BOOST_PP_ARRAY_SIZE(A_DIM) - 1, "map_entry_mcr.h"))
    #include BOOST_PP_ITERATE()

#else

    mMap[ CKey( aLang, aType, aAcc ) ]
        = CVarX< aLang, aType, aAcc >::Make;

        ? aDim

#endif

-------------------------

As you can see, this method is much more elaborate. The mechanism is
primarily intended for repeating large swathes of code rather than cartesian
products. Nevertheless, it works and is fairly fast--even on EDG. Make
sure that you understand what is happening if you use this last example
(maybe read the docs on file-iteration). Also, note that you can put all
this iteration stuff in a separate file and merely include it where you
want. The file iteration mechanism is a powerhouse, and this usage barely
touches on its potential (which is partially why the implementation above is
as obtuse as it is).

The strengths of option #1 and option #2 is the locality of code. Option #3
is a lot more involved. The weaknesses of option #1 and option #2 is that
it is really slow on EDG preprocessors and that all the code gets expanded
on a single line and is completely undebuggable. Option #3 excels in this
area--though in this case, the debuggability aspect is negligible for such a
small snippet of source code.

In any case, I urge you to read the documentation on each library primitive
used and also the topic on file iteration. If you find anything unclear,
then you can tell me, and I can improve the docs.

> The DCT_ constants are a combination of enums and integer defines. aLang
and
> aDim are enum entries, with sequential values starting with 1. aType and
> aAcc are integer defines. aType is non-sequential, aAcc is sequential
> starting with 1. It is possible for any of these to become non-sequential
in
>
> the future.
>
> >
> > There are several ways to do this, so let me know if I'm clear on what
> > you're doing, and I'll help you.
> >
> > Paul Mensonides
>
> Thanks for the help Paul
>
> Jeff Flinn
> Applied Dynamics, International

Regards,
Paul Mensonides


Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net