[preprocessor] What syntax to present to the user

Hello everybody, 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... I think I have to use macro to ease the writing of such enums, and I decided it was a good occasion to learn boost::preprocessor. My first impression : Wow ! You guys really use magic when programming ! Now, I have to decide what will be the interfaces for such rich enums. My (unreachable) goal would be something like that : RichEnum Type{Val1, Val2, Val3}; A little bit more reasonnable (but still unacheivable, I believe) goal would be : RICH_ENUM(Type, Val1, Val2, Val3); I think I can come up with several possible writing : 1/ RICH_ENUM(Type, (Val1)(Val2)(Val3)); 2/ RICH_ENUM(Type, 3, (Val1,Val2,Val3)); 3/ #define NAME Type #define VALUES (Val1)(Val2)(Val3) #include "RichEnum.h" #undef NAME #undef VALUES 4/ same as 3/, but with tuple syntax for VALUES 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. 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 ? Regards, -- Loïc

-----Original Message----- From: boost-bounces@lists.boost.org [mailto:boost-bounces@lists.boost.org] On Behalf Of Loïc Joly
Now, I have to decide what will be the interfaces for such rich enums. My (unreachable) goal would be something like that : RichEnum Type{Val1, Val2, Val3};
A little bit more reasonnable (but still unacheivable, I believe) goal would be : RICH_ENUM(Type, Val1, Val2, Val3);
Yes, unachievable in current C++ (i.e. without variadic macros from C).
I think I can come up with several possible writing :
1/ RICH_ENUM(Type, (Val1)(Val2)(Val3));
This one is fine, IMO
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 ?
(I have to get back to this part later, I'm out of time for the moment.) Regards, Paul Mensonides

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

Paul Mensonides a écrit :
Hi Loïc.
Okay, I have a little more time now.
Thank you for your help, it helps me clarify some fuzzy points.
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 do this for a personnal project, part of which is to improve my skills, so even if I crave for re-use, not this time. By the way, I had a little check, and no fancy enum I have seen seems to have reached a wide acceptance yet, or to cover my requirements. Maybe I will call my system yafe (yet another fancy enum) :). [snip example]
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.
Ok, I think I can see your point. I guess that my reluctance was due to the fact that since I am not fluent in cpp, I was afraid to create a macro that would span on many lines. Let's go for this syntax then. Maybe I will publish the result in the mailing list files area, if I think "yafe" can be of interrest to other people. Regards, -- Loïc
participants (2)
-
Loïc Joly
-
Paul Mensonides