
-----Original Message----- From: boost-bounces@lists.boost.org [mailto:boost-bounces@lists.boost.org] 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