Boost logo

Boost :

From: Paul Mensonides (pmenso57_at_[hidden])
Date: 2005-05-16 16:25:15


> -----Original Message-----
> From: boost-bounces_at_[hidden]
> [mailto:boost-bounces_at_[hidden]] On Behalf Of Loïc Joly

Hi Loïc.

Okay, I have a little more time now.

> I'm trying to use the boost preprocessor library to allow the
> user to define "rich" enums, for instance enums that cannot
> be converted to int, whose values can be displayed in human
> readable form on a stream...

You might already know that there are several fancy enum implementations around.
However, I'm not sure if any them fit what you're looking for. Specifically,
enumerators that cannot be converted to integers. What you want, AFAICT, is a
set of symbols only that share a common type. You may want to look for existing
implementations first.

> I think I can come up with several possible writing :
>
> 1/
> RICH_ENUM(Type, (Val1)(Val2)(Val3));

I personally would choose this one.

> With 1/ and 2/, all the class declaration is on the same
> line, which could decrease debugability. With 3/ and 4/, I
> can write it one several lines.

Regarding debugging... It really depends on the complexity of what you are
generating. The process of preprocessor metaprogramming starts with deciding on
what you want, and then getting the preprocessor to write it for you. E.g. say
that I want this...

#include <ostream>

enum color { red, green, blue };

template<class T, class traits>
std::basic_ostream<T, traits>& operator<<(std::basic_ostream<T, traits>& o,
color c) {
    switch (c) {
        case red:
            return o << "red";
        case green:
            return o << "green";
        case blue:
            return o << "blue";
        default:
            return o << "color(" << static_cast<int>(c) << ')';
    }
}

...then the code is not complex enough to warrant extra debuggability support
(which makes the preprocessor generation more complex). Instead, I'd go with:

#include <ostream>

#include <boost/preprocessor/seq/enum.hpp>
#include <boost/preprocessor/seq/for_each.hpp>
#include <boost/preprocessor/stringize.hpp>

#define ENUM(type, seq) \
    enum type { BOOST_PP_SEQ_ENUM(seq) }; \
    template<class gensym_T, class gensym_traits> \
        std::basic_ostream<gensym_T, gensym_traits>& \
            operator<<(std::basic_ostream<gensym_T, gensym_traits>& gensym_o,
type gensym_c) { \
        switch (gensym_c) { \
            BOOST_PP_SEQ_FOR_EACH(ENUM_INTERNAL, ~, seq) \
            default: \
                return gensym_o << BOOST_PP_STRINGIZE(type) "(" \
                    << static_cast<int>(gensym_c) << ')'; \
        } \
    } \
    /**/
#define ENUM_INTERNAL(s, _, elem) \
    case elem: \
        return gensym_o << BOOST_PP_STRINGIZE(elem); \
    /**/

ENUM(color, (red)(green)(blue))

In other words, the importance of debuggability greatly depends on what you're
generating--i.e. what you want the preprocessor to write for you. If it is
something complex, particularly if it can fail inside the specific generated
code, then debuggability is more important.

> And now, the questions :
> Which writing do you think would be best ? Why ? Are there
> other possibilities I overlooked ? Is is possible to have a
> syntax like 1/ and 2/ with good debugability ?

To the last, yes. But the point at which it is debugged is different. Instead,
when you write something like the above, you debug the preprocessor metaprogram
to make sure it is generating what you want, and then you debug the *pattern*
that it is generating by creating instances of the pattern (like color above),
and making sure that it works correctly. Once that is done, you shouldn't have
to debug it again.

Regards,
Paul Mensonides


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk