Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C++ > Re: Strongly-typed non-scoped enums in C++11?

Reply
Thread Tools

Re: Strongly-typed non-scoped enums in C++11?

 
 
Öö Tiib
Guest
Posts: n/a
 
      08-23-2013
On Friday, 23 August 2013 10:28:33 UTC+3, Paavo Helde wrote:
> Background: I would like to have strongly-typed enums in order to find
> out and fix suspicious comparisons and conversions to integers. However,
> only one of our build machines has a compiler version which supports C++
> 11 enums, a legacy enum is needed for other builds. For the diagnostics
> point of view it is fine if it works only in one build, so I thought
> doing something like:
>
> #ifdef HAS_C11_STRONGLY_TYPED_ENUMS
> enum class abc {
> #else
> enum abc {
> #endif
> val1,
> val2,
> ....
> };
>
> Alas, now all the codebase must be changed to use the names abc::val1,
> abc::val2 in the C11 branch, and must not use the prefix in the legacy
> branch. This seems to kill the whole benefit of this approach. Any ideas?


The idiomatic enum usage was something like that:

// Old code
enum abc { abc_val1, abc_val2, abc_val3 };

With C++11 we want that:

// New code
enum class abc { val1, val2, val3 };

For transition time we may want to have that:

// Support to legacy enumerators
static abc const abc_val1 = abc::val1;
static abc const abc_val2 = abc::val2;
static abc const abc_val3 = abc::val3;

I don't think that primitive C preprocessor can somehow construct all
above in elegant manner. It might be easier with C++98 type safe enum
toy:

template<typename def, typename inner = typename def::type>
class safe_enum : public def
{
typedef typename def::type type;
inner val;
public:
safe_enum(type v) : val(v) {}
inner underlying() const { return val; }
friend bool operator == (const safe_enum & lhs, const safe_enum & rhs) { return lhs.val == rhs.val; }
friend bool operator != (const safe_enum & lhs, const safe_enum & rhs) { return lhs.val != rhs.val; }
friend bool operator < (const safe_enum & lhs, const safe_enum & rhs) { return lhs.val < rhs.val; }
friend bool operator <= (const safe_enum & lhs, const safe_enum & rhs) { return lhs.val <= rhs.val; }
friend bool operator > (const safe_enum & lhs, const safe_enum & rhs) { return lhs.val > rhs.val; }
friend bool operator >= (const safe_enum & lhs, const safe_enum & rhs) { return lhs.val >= rhs.val; }
};

Then we can have something that compiles on old compilers and looks more
preprocessable:

// Old type safe enum usage
struct abc_def {
enum type { val1, val2, val3 };
};
typedef safe_enum<abc_def> abc;

// Support to legacy enumerators
static abc const abc_val1 = abc::val1;
static abc const abc_val2 = abc::val2;
static abc const abc_val3 = abc::val3;

Unfortunately that "safe thing" may cause legal problems if used like
aggregate (that it is not) and also compilers may fail to optimize its
usage well enough.

Pick your poison.


 
Reply With Quote
 
 
 
 
Gerhard Fiedler
Guest
Posts: n/a
 
      08-23-2013
Öö Tiib wrote:

> On Friday, 23 August 2013 10:28:33 UTC+3, Paavo Helde wrote:
>> Background: I would like to have strongly-typed enums in order to find
>> out and fix suspicious comparisons and conversions to integers. However,
>> only one of our build machines has a compiler version which supports C++
>> 11 enums, a legacy enum is needed for other builds. For the diagnostics
>> point of view it is fine if it works only in one build, so I thought
>> doing something like:
>>
>> #ifdef HAS_C11_STRONGLY_TYPED_ENUMS
>> enum class abc {
>> #else
>> enum abc {
>> #endif
>> val1,
>> val2,
>> ....
>> };
>>
>> Alas, now all the codebase must be changed to use the names abc::val1,
>> abc::val2 in the C11 branch, and must not use the prefix in the legacy
>> branch. This seems to kill the whole benefit of this approach. Any ideas?

>
> The idiomatic enum usage was something like that:
>
> // Old code
> enum abc { abc_val1, abc_val2, abc_val3 };
>
> With C++11 we want that:
>
> // New code
> enum class abc { val1, val2, val3 };
>
> For transition time we may want to have that:
>
> // Support to legacy enumerators
> static abc const abc_val1 = abc::val1;
> static abc const abc_val2 = abc::val2;
> static abc const abc_val3 = abc::val3;
>
> I don't think that primitive C preprocessor can somehow construct all
> above in elegant manner.


With the Boost preprocessor lib it should be possible to define this for
example like this:

DOUBLE_PURPOSE_ENUM( abc, (val1)(val2)(val3) );

I think that with a suitable defined DOUBLE_PURPOSE_ENUM you can get
exactly the code you wrote above. (See the type 'sequence' in the
library documentation.)

Gerhard
 
Reply With Quote
 
 
 
 
Öö Tiib
Guest
Posts: n/a
 
      08-23-2013
On Friday, 23 August 2013 20:26:42 UTC+3, Gerhard Fiedler wrote:
> Öö Tiib wrote:
> > The idiomatic enum usage was something like that:
> >
> > // Old code
> > enum abc { abc_val1, abc_val2, abc_val3 };
> >
> > With C++11 we want that:
> >
> > // New code
> > enum class abc { val1, val2, val3 };
> >
> > For transition time we may want to have that:
> >
> > // Support to legacy enumerators
> > static abc const abc_val1 = abc::val1;
> > static abc const abc_val2 = abc::val2;
> > static abc const abc_val3 = abc::val3;
> >
> > I don't think that primitive C preprocessor can somehow construct all
> > above in elegant manner.

>
> With the Boost preprocessor lib it should be possible to define this for
> example like this:
>
> DOUBLE_PURPOSE_ENUM( abc, (val1)(val2)(val3) );
>
> I think that with a suitable defined DOUBLE_PURPOSE_ENUM you can get
> exactly the code you wrote above. (See the type 'sequence' in the
> library documentation.)


Haven't studied it. Would you seriously recommend it? Typically
such things come with toys working but actual lists run into some
insanely low limits (say 256 elements max) or cause serious slow-down
when lists are longer than 8 elements.
 
Reply With Quote
 
Gerhard Fiedler
Guest
Posts: n/a
 
      08-24-2013
Öö Tiib wrote:

> On Friday, 23 August 2013 20:26:42 UTC+3, Gerhard Fiedler wrote:
>> Öö Tiib wrote:
>>> The idiomatic enum usage was something like that:
>>>
>>> // Old code
>>> enum abc { abc_val1, abc_val2, abc_val3 };
>>>
>>> With C++11 we want that:
>>>
>>> // New code
>>> enum class abc { val1, val2, val3 };
>>>
>>> For transition time we may want to have that:
>>>
>>> // Support to legacy enumerators
>>> static abc const abc_val1 = abc::val1;
>>> static abc const abc_val2 = abc::val2;
>>> static abc const abc_val3 = abc::val3;
>>>
>>> I don't think that primitive C preprocessor can somehow construct all
>>> above in elegant manner.

>>
>> With the Boost preprocessor lib it should be possible to define this for
>> example like this:
>>
>> DOUBLE_PURPOSE_ENUM( abc, (val1)(val2)(val3) );
>>
>> I think that with a suitable defined DOUBLE_PURPOSE_ENUM you can get
>> exactly the code you wrote above. (See the type 'sequence' in the
>> library documentation.)

>
> Haven't studied it. Would you seriously recommend it? Typically such
> things come with toys working but actual lists run into some insanely
> low limits (say 256 elements max) or cause serious slow-down when
> lists are longer than 8 elements.


I have just recently started to use Boost Preprocessor for a few
small-scale things and found it convenient to use. It opens up the range
of things you can do with the preprocessor without a lot of code (of
course not counting the code in the Boost headers .

For larger numbers of items (you talk about 256), I have used the
include trick a few times: the items are defined as argument(s) to a
macro inside a file that only contains these definitions, and the file
is included multiple times, where each time the macro is defined
differently. For just three items, that wouldn't make much sense, but if
it is a longer list (and especially if multiple people may have to add
items to the list without actually being involved with the code behind
it), separating the list itself from the code that it generates in this
way can be quite useful.

In this case, for example, once you can get rid of the support for the
legacy enumerators, you just drop the code in Adc.h that generates them.
The list itself in AbcReg.h doesn't change.

A typical case for this is when there's a need for an enumeration with
text associated with each value.

File AbcReg.h:

ITEM( val1 )
ITEM( val2 )
ITEM( val3 )
//...

File Abc.h:

enum class abc {
#define ITEM( item ) item,
#include AbcReg.h
#undef ITEM
}

#define ITEM( item ) static abc const abc_##item = abc::item;
#include AbcReg.h
#undef ITEM


Gerhard
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Re: New strongly type enum proposal featuring inherited enums Victor Bazarov C++ 6 08-18-2011 12:32 PM
Is strongly typed enums comparable? Qiang Li C++ 5 09-28-2008 12:27 AM
Enums without identifier, enums and typedef =?utf-8?b?QXNiasO4cm4gU8OmYsO4?= C Programming 10 01-20-2007 01:20 AM
Custom Namespace For Strongly Typed DataSet Narayanan Sankaranarayanan ASP .Net 0 12-15-2004 01:28 AM
Strongly Typed dataset Imran ASP .Net 1 01-23-2004 07:42 AM



Advertisments