Boost logo

Boost Users :

From: Paul Mensonides (pmenso57_at_[hidden])
Date: 2005-10-12 16:08:25


> -----Original Message-----
> From: boost-users-bounces_at_[hidden]
> [mailto:boost-users-bounces_at_[hidden]] On Behalf Of Rush Manbert

> >Macros are not evil. Some uses of them are evil; some are not.
> >
> >
> >
> I actually agree with you. This was posted to
> comp.lang.c++.moderated, and they point you to the C++ FAQ
> first. It states more than once that "Macros are Evil", so I
> thought I'd avoid getting flamed if I mentioned that. ;-)

Yeah, I know how that goes.

> That's pretty cool. I'm going to impose on your good nature
> to ask a follow up question here. In the real code, each CODE
> macro contains more entries, like so:
> #define CODE_TABLE \
> CODE(FooCodeA, "FooCodeA short description",
> "SummarySelectorFooCodeA", "DetailSelectorFooCodeA") \
> CODE(FooCodeB, "FooCodeB short description",
> "SummarySelectorFooCodeB", "DetailSelectorFooCodeB")
>
> And the structure is defined as:
> typedef struct
> {
> CodesT theCode;
> const char *pTheCodeAsString;
> const char *pShortDescription;
> const char *pSummarySelector;
> const char *pDetailSelector;
> } CodeLookupTableT;
>
> I have done no programming with the preprocessor lib, so I
> only have a vague idea how to extend what you did to cover
> this case. (I think CODE needs to define either an array or a
> list or a sequence or a tuple, but that's as far as I have
> gotten...) Would
> you be willing to give it a go?

Certainly. You just need a have a data structure of data structures. You don't
really need CODE at all.

--------------------
foo.h
--------------------
#include <boost/preprocessor/punctuation/comma_if.hpp>
#include <boost/preprocessor/seq/for_each_i.hpp>
#include <boost/preprocessor/tuple/elem.hpp>

#define CODE_TABLE \
    ( (FooCodeA, "description", "summary", "detail") ) \
    ( (FooCodeB, "description", "summary", "detail") ) \
    /**/

#define ENUM(r, _, i, tuple) \
    BOOST_PP_COMMA_IF(i) BOOST_PP_TUPLE_ELEM(4, 0, tuple) \
    /**/

class foo
{
public:
    typedef enum { BOOST_PP_SEQ_FOR_EACH_I(ENUM, ~, CODE_TABLE) };
// ...
};

#undef ENUM

--------------------
foo.cpp
--------------------
#include "foo.h"

#include <boost/preprocessor/punctuation/comma_if.hpp>
#include <boost/preprocessor/seq/for_each_i.hpp>
#include <boost/preprocessor/stringize.hpp>

#define ENTRY(r, _, i, tuple) \
    BOOST_PP_COMMA_IF(i) ENTRY_II tuple \
    /**/
#define ENTRY_II(code, desc, summary, detail) \
    { code, BOOST_PP_STRINGIZE(code), desc, summary, detail } \
    /**/

foo::CodeTableLookupT foo::m_lookupTable[]
    = { BOOST_PP_SEQ_FOR_EACH_I(ENTRY, ~, CODE_TABLE) }

#undef ENTRY
#undef ENTRY_II

Note that I'm using an extra ENTRY_II macro here to avoid five invocations of
BOOST_PP_TUPLE_ELEM. With variadic macros (from C99 and hopefully C++0x), it is
possible to make the whole thing cleaner. E.g. this is how I might do it in
Chaos:

#include <chaos/preprocessor/punctuation/comma_if.h>
#include <chaos/preprocessor/seq/auto/for_each_i.h>

#define CODE_TABLE \
    (FooCodeA, "descriptionA", "summaryA", "detailA") \
    (FooCodeB, "descriptionB", "summaryB", "detailB") \
    /**/

#define ENTRY(s, i, code, desc, summary, detail) \
    CHAOS_PP_COMMA_IF(i) { code, #code, desc, summary, detail } \
    /**/ // ^^^^^ Chaos guarantees that this will
                              // work; the Boost pp-lib does not.

foo::CodeTableLookupT foo::m_lookupTable[]
    = { CHAOS_PP_SEQ_AUTO_FOR_EACH_I(ENTRY, CODE_TABLE) };

In other words, it is using variadic parameter sets natively. Of course, this
is in the not yet realized world of C++ with variadic macros. In real C++
today, you'd have to do something like the sequence of tuples above. (Of
course, doing so in Chaos--even without variadics--is still better as it allows
argument remapping.)

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