"Reusable" operator overloading for enum?

N

nick

I'm sort of rusty at C++, so this may be silly, but here's my
situation:

I have an enum like this:


enum ParserMode
{
PM_NONE = 0,
PM_READY = 1, // ready for next col or row, or EOF
PM_EMPTY = 2, // empty cell
PM_CELL = 4, // cell
PM_QCELL = 8, // quoted cell
PM_HEAD = 16, // header cell
};


And elsewhere I want to write something like:


void DoStuff(ParserMode m)
{
if (m & PM_HEAD)
...
else if (m & PM_QCELL)
...
}
...
DoStuff(PM_CELL | PM_EMPTY | PM_HEAD);


But this will not work, because the compiler complains about not being
able to cast between int and ParserMode. So some operator overloading
clears that up:


inline ParserMode operator|(ParserMode p1, ParserMode p2)
{
return (ParserMode)((int)p1 | (int)p2);
};
inline ParserMode operator&(ParserMode p1, ParserMode p2)
{
return (ParserMode)((int)p1 & (int)p2);
};
inline ParserMode operator~(ParserMode p1)
{
return (ParserMode)(~(int)p1);
};
inline ParserMode& operator&=(ParserMode& p1, ParserMode p2) {
p1 = p1 & p2;
return p1;
}
...


....And everything works fine. But, now I need to add another enum, and
I don't want to copy all of these overloaded operators for this other
enum. Is there some way I can share one set of operator overloads
between many enums using templates, macros, or some other trick, or am
I just going about this wrong altogether?

Thanks in advance for any help.

-- Nick
 
M

Marcel Müller

Hi,
inline ParserMode operator|(ParserMode p1, ParserMode p2)
{
return (ParserMode)((int)p1 | (int)p2);
};
...

you won't come around to use macros in this case.

E.g.

#define FLAGSATTRIBUTE(T) \
inline static T operator|(T l, T r) \
{ return (T)((unsigned)l|r); } \
inline static T operator&(T l, T r) \
{ return (T)((unsigned)l&r); } \
inline static T& operator|=(T& l, T r) \
{ return l = (T)((unsigned)l|r); } \
inline static T& operator&=(T& l, T r) \
{ return l = (T)((unsigned)l&r); } \
inline static T operator*(bool l, T r) \
{ return (T)(l*(unsigned)r); } \
inline static T operator*(T l, bool r) \
{ return (T)((unsigned)l*r); } \
inline static T operator~(T a) \
{ return (T)~(unsigned)a; }

and possibly

#define CLASSFLAGSATTRIBUTE(T) \
inline friend T operator|(T l, T r) \
{ return (T)((unsigned)l|r); } \
inline friend T operator&(T l, T r) \
{ return (T)((unsigned)l&r); } \
inline friend T& operator|=(T& l, T r) \
{ return l = (T)((unsigned)l|r); } \
inline friend T& operator&=(T& l, T r) \
{ return l = (T)((unsigned)l&r); } \
inline friend T operator*(bool l, T r) \
{ return (T)(l*(unsigned)r); } \
inline friend T operator*(T l, bool r) \
{ return (T)((unsigned)l*r); } \
inline friend T operator~(T a) \
{ return (T)~(unsigned)a; }

for private or protected nested enums.

enum ParserMode
{
PM_NONE = 0,
PM_READY = 1, // ready for next col or row, or EOF
PM_EMPTY = 2, // empty cell
PM_CELL = 4, // cell
PM_QCELL = 8, // quoted cell
PM_HEAD = 16, // header cell
};
FLAGSATTRIBUTE(ParserMode)

The result is similar than the FlagsAttribute class in DotNET.


Marcel
 
S

Saeed Amrollahi

I'm sort of rusty at C++, so this may be silly, but here's my
situation:

I have an enum like this:

  enum ParserMode
  {
    PM_NONE  = 0,
    PM_READY = 1,     // ready for next col or row, or EOF
    PM_EMPTY = 2,     // empty cell
    PM_CELL =  4,     // cell
    PM_QCELL = 8,     // quoted cell
    PM_HEAD = 16,     // header cell
  };

And elsewhere I want to write something like:

  void DoStuff(ParserMode m)
  {
    if (m & PM_HEAD)
      ...
    else if (m & PM_QCELL)
      ...
  }
  ...
  DoStuff(PM_CELL | PM_EMPTY | PM_HEAD);

But this will not work, because the compiler complains about not being
able to cast between int and ParserMode. So some operator overloading
clears that up:

  inline ParserMode operator|(ParserMode p1, ParserMode p2)
  {
    return (ParserMode)((int)p1 | (int)p2);
  };
  inline ParserMode operator&(ParserMode p1, ParserMode p2)
  {
    return (ParserMode)((int)p1 & (int)p2);
  };
  inline ParserMode operator~(ParserMode p1)
  {
    return (ParserMode)(~(int)p1);
  };
  inline ParserMode& operator&=(ParserMode& p1, ParserMode p2) {
    p1 = p1 & p2;
    return p1;
  }
  ...

...And everything works fine. But, now I need to add another enum, and
I don't want to copy all of these overloaded operators for this other
enum. Is there some way I can share one set of operator overloads
between many enums using templates, macros, or some other trick, or am
I just going about this wrong altogether?

Thanks in advance for any help.

-- Nick

Hi Nick

I think the following code helps you

enum ParserMode {
PM_NONE = 0,
PM_READY = 1, // ready for next col or row, or EOF
PM_EMPTY = 2, // empty cell
PM_CELL = 4, // cell
PM_QCELL = 8, // quoted cell
PM_HEAD = 16, // header cell
};

enum ScannerMode { // another enum
SM_NONE = 0,
SM_READY = 1,
SM_EMPTY = 2,
SM_CELL = 4,
SM_QCELL = 8,
SM_HEAD = 16,
};

template<class ENUM_TYPE>
ENUM_TYPE operator|(ENUM_TYPE e1, ENUM_TYPE e2)
{
int i1 = e1; // implicit type conversion
int i2 = e2;

return ENUM_TYPE(i1 | i2);
}

template<class ENUM_TYPE>
ENUM_TYPE operator&(ENUM_TYPE e1, ENUM_TYPE e2)
{
int i1 = e1; // implicit type conversion
int i2 = e2;

return ENUM_TYPE(i1 & i2);
}

template<class ENUM_TYPE>
ENUM_TYPE operator~(ENUM_TYPE e)
{
int i = e; // implicit type conversion
return ENUM_TYPE(~i);
}

int main()
{
ScannerMode s1 = SM_NONE, s2 = SM_HEAD;
s1 = s1 | s2;

ParserMode p1 = PM_NONE, p2 = p1;
p1 = ~p1;
p2 = ~(p2 & p1);

return 0;
}

Regards,
-- Saeed Amrollahi
 
B

Balog Pal

nick said:
enum ParserMode
DoStuff(PM_CELL | PM_EMPTY | PM_HEAD);

But this will not work, because the compiler complains about not being
able to cast between int and ParserMode. So some operator overloading
clears that up:
inline ParserMode operator|(ParserMode p1, ParserMode p2)
inline ParserMode operator&(ParserMode p1, ParserMode p2)
inline ParserMode operator~(ParserMode p1)
inline ParserMode& operator&=(ParserMode& p1, ParserMode p2) {
...
...And everything works fine. But, now I need to add another enum, and
I don't want to copy all of these overloaded operators for this other
enum.

Yeah, it is a common "problem" too bad the language did not look for that
when removing the int->enum conversion; the listed operations currently
promote the enum to int (or bigger) create the result and it stays that
type. Instead of vorking natively on the enum and produce that type. :(
At least for | and &.

I never used ~ that way, as it may not work. The rule is that the
implementation type for enum must represent all the enumerators and their |.
so the value of your ~ is not predictable (unless you can force the compiler
to use some fixed type.

To create the missing operators I use a macro that takes enum name as param
and expands to text similar to yours. And just add it right after the enum
definition if it is used that way, saves redundancy and provides kinda
documantation of purpose too.
 
B

Balog Pal

template<class ENUM_TYPE>
ENUM_TYPE operator|(ENUM_TYPE e1, ENUM_TYPE e2)
{
int i1 = e1; // implicit type conversion
int i2 = e2;

return ENUM_TYPE(i1 | i2);
}
<<

There is nothing to restrict the arguments here, so this template may pick
up unwanted stuff. (And use of the old-style cast in return instead of
static_cast makes it even more dangerous).
 
J

Johannes Schaub (litb)

Balog said:
template<class ENUM_TYPE>
ENUM_TYPE operator|(ENUM_TYPE e1, ENUM_TYPE e2)
{
int i1 = e1; // implicit type conversion
int i2 = e2;

return ENUM_TYPE(i1 | i2);
}
<<

There is nothing to restrict the arguments here, so this template may pick
up unwanted stuff. (And use of the old-style cast in return instead of
static_cast makes it even more dangerous).

One may use is_enum<ENUM_TYPE>::value to restrict it.
 
S

Saeed Amrollahi

"Saeed Amrollahi" <[email protected]>



template<class ENUM_TYPE>
ENUM_TYPE operator|(ENUM_TYPE e1, ENUM_TYPE e2)
{
  int i1 = e1; // implicit type conversion
  int i2 = e2;

  return ENUM_TYPE(i1 | i2);}

<<

There is nothing to restrict the arguments here, so this template may pick
up unwanted stuff. (And use of the old-style cast in return instead of
static_cast makes it even more dangerous).

I see what you mean. The code by Nick had the problem too. From point
of C++ Design and Implmentation,
he wanted to share common code between two different enums. For well-
known reasons, I prefer template
over macros. I am not sure, but if I'm not mistaken, in C++0x, the
Scoped enums don't allow the implicit
conversion from enum to int.

regards,
-- Saeed Amrollahi
 
N

nick

Thanks for all the great replies!

Marcel, your solution works perfectly. I like how it 'documents' the
enum as being a flag set. I'm using this for now with a few small
additions, but now I'm curious why people seem generally opposed to
macros...

Anyway, I have a few other small questions if you guys are still
interested. Balog, you mentioned the ~ operator would not be
predictable, and it sounds like it's because the way enums are stored
in memory is not predictable?

This makes me wonder if I should cast to unsigned as in Marcel's
example, or signed as in Saeed's example and as I was doing before. Of
course I'll only want to use positive numbers for my flag sets, but
enums seem to handle negative numbers just fine, so I'm not sure what
the right thing to do is here.

So, I guess my question is: is there a way to explicitly store an enum
as an int, or an unsigned int, or whatever, either using a language
construct or some kind of compiler instrucion? I'm using GCC 4.3.

The template stuff may be a bit over my head, but here's how I
understand it: Saeed's solution will work, but it will overload those
operators for all (classes? enums?), not just the flag sets I need
those operators for. Johannes says I can use is_enum<ENUM_TYPE>::value
to fix it so it only affects (enums?) ... is that about right? If so
what are the advantages of that over Marcel's method, other than macro
definitions looking ugly in the source?

Thanks for the help!

-- Nick
 
B

BGB / cr88192

nick said:
I'm sort of rusty at C++, so this may be silly, but here's my
situation:

I have an enum like this:


enum ParserMode
{
PM_NONE = 0,
PM_READY = 1, // ready for next col or row, or EOF
PM_EMPTY = 2, // empty cell
PM_CELL = 4, // cell
PM_QCELL = 8, // quoted cell
PM_HEAD = 16, // header cell
};


And elsewhere I want to write something like:


void DoStuff(ParserMode m)
{
if (m & PM_HEAD)
...
else if (m & PM_QCELL)
...
}
...
DoStuff(PM_CELL | PM_EMPTY | PM_HEAD);


But this will not work, because the compiler complains about not being
able to cast between int and ParserMode. So some operator overloading
clears that up:


inline ParserMode operator|(ParserMode p1, ParserMode p2)
{
return (ParserMode)((int)p1 | (int)p2);
};
inline ParserMode operator&(ParserMode p1, ParserMode p2)
{
return (ParserMode)((int)p1 & (int)p2);
};
inline ParserMode operator~(ParserMode p1)
{
return (ParserMode)(~(int)p1);
};
inline ParserMode& operator&=(ParserMode& p1, ParserMode p2) {
p1 = p1 & p2;
return p1;
}
...


...And everything works fine. But, now I need to add another enum, and
I don't want to copy all of these overloaded operators for this other
enum. Is there some way I can share one set of operator overloads
between many enums using templates, macros, or some other trick, or am
I just going about this wrong altogether?

Thanks in advance for any help.

errm, if they are flags why not just use '#define'?...
 
B

Balog Pal

template said:
ENUM_TYPE operator|(ENUM_TYPE e1, ENUM_TYPE e2)
I see what you mean. The code by Nick had the problem too. From point
of C++ Design and Implmentation,
he wanted to share common code between two different enums. For well-
known reasons, I prefer template
over macros.

Me too. But for this case I stand with macros. Global templated operator --
that is way more evil than macros :)

And for this case I want certain operators only for a couple of enums. (bit
operators for those that has masks, increment, addition that I use in state
machine, etc...)

I am not sure, but if I'm not mistaken, in C++0x, the
Scoped enums don't allow the implicit
conversion from enum to int.

Yeah, there are good improvements, class enums do not convert to integrals
implicitly, also you can set a fixed implementation type -- and if you did,
you can forward-declare the enum, and define its enumerators at multiple
places. (IIRC)
 
B

Balog Pal

nick said:
Marcel, your solution works perfectly. I like how it 'documents' the
enum as being a flag set. I'm using this for now with a few small
additions, but now I'm curious why people seem generally opposed to
macros...

You will find it in all accepted standard/guideline books. The most serious
roblem is that macros do not respect scope. And for many applications there
are way better tools -- constants/enums, inline functions, templates...
Though not for everything.
Anyway, I have a few other small questions if you guys are still
interested. Balog, you mentioned the ~ operator would not be
predictable, and it sounds like it's because the way enums are stored
in memory is not predictable?

Yes, if you have enumerators 0, 1 -- the used type may be char, unsigned
char, short, int, possibly others. Certain use approach can go without
problems, but I'd avoid it unless absolutely needed. If you want it to
remove bits combined with &, rather define a named function doing right
that.
This makes me wonder if I should cast to unsigned as in Marcel's
example, or signed as in Saeed's example and as I was doing before. Of
course I'll only want to use positive numbers for my flag sets, but
enums seem to handle negative numbers just fine, so I'm not sure what
the right thing to do is here.

For bit-masks I stick to unsigned operations.
So, I guess my question is: is there a way to explicitly store an enum
as an int, or an unsigned int, or whatever, either using a language
construct or some kind of compiler instrucion? I'm using GCC 4.3.

Probably gcc has a compiler switch to 'treat enums as ints' or like, though
for all of them. Look dox for attribute, rthough don't hold your breath. For
language level control wait C++0x.
The template stuff may be a bit over my head, but here's how I
understand it: Saeed's solution will work, but it will overload those
operators for all (classes? enums?), not just the flag sets I need
those operators for. Johannes says I can use is_enum<ENUM_TYPE>::value
to fix it so it only affects (enums?) ... is that about right?

AFAIK is_enum is some boost magic.
 
N

nick

Thanks, Balog. I did away with the ~ operator, but instead of
replacing it with a named function I figured I'd use the - and -=
operators for unsetting the flag. Here's what I ended up with, maybe
this will be useful for someone else.

#define FLAGS_(T) \
inline static T operator*(bool l, T r) \
{ return (T)(l*(unsigned)r); } \
inline static T operator*(T l, bool r) \
{ return (T)((unsigned)l*r); } \
inline static T operator|(T l, T r) \
{ return (T)((unsigned)l|r); } \
inline static T operator&(T l, T r) \
{ return (T)((unsigned)l&r); } \
inline static T operator+(T l, T r) \
{ return l|r; } \
inline static T operator-(T l, T r) \
{ return l&r?(T)((unsigned)l-(unsigned)r):l; } \
inline static T& operator|=(T& l, T r) \
{ return l = l|r; } \
inline static T& operator&=(T& l, T r) \
{ return l = l&r; } \
inline static T& operator+=(T& l, T r) \
{ return l = l+r; } \
inline static T& operator-=(T& l, T r) \
{ return l = l-r; }
 
J

Johannes Schaub (litb)

Balog said:
You will find it in all accepted standard/guideline books. The most
serious
roblem is that macros do not respect scope. And for many applications
there are way better tools -- constants/enums, inline functions,
templates... Though not for everything.

You first have to make sure that the type is not an integral type (just
compare it with each and every integral type), and then make sure it's not a
class type, and then test if it converts to one integral type. If that'S the
case, it must be an enum. The book "C++ templates, the complete guide" shows
a possible implementation. I'm trying to remember it here:

// don't let class types convert to the integral types
template<typename T>
struct SlurpUDC {
operator T();
};

typedef char yes[1];
typedef char no[2];

yes &int_(int);
yes &int_(unsigned int);
yes &int_(long);
yes &int_(unsigned long);
no & int_(...);
// if your compiler has it, then long long, unsigned long long

template<typename> struct intt { static bool const value = false; };
#define INTT(X) template<> struct intt<X> { static bool const value = true;
}

INTT(char);
INTT(signed char);
INTT(unsigned char);
INTT(int);
INTT(unsigned int);
INTT(long);
INTT(unsigned long);
INTT(wchar_t);
INTT(bool);
// if your compiler has it, then long long, unsigned long long, and hoping
// that i've not missed one.

C++0x introduces implementation defined integral types that may not match
any of these, but then it also has is_enum, so you don't need to write this
at all, anyway.

template<typename T>
struct is_enum {
static bool const value = sizeof int_(SlurpUDC<T>()) == sizeof(yes)
&& !intt<T>::value;
};

Notice that an enumeration type will promote to one integral type out of the
4 above. That's all i think. Now you can write

template<typename A>
typename enable_if<is_enum<A>::value, A>::type operator|(A a, A b) { ... }

Hope it helps...
 
J

Johannes Schaub (litb)

Johannes said:
Balog said:
You will find it in all accepted standard/guideline books. The most
serious
roblem is that macros do not respect scope. And for many applications
there are way better tools -- constants/enums, inline functions,
templates... Though not for everything.

You first have to make sure that the type is not an integral type (just
compare it with each and every integral type), and then make sure it's not
a class type, and then test if it converts to one integral type. If that'S
the case, it must be an enum. The book "C++ templates, the complete guide"
shows a possible implementation. I'm trying to remember it here:

// don't let class types convert to the integral types
template<typename T>
struct SlurpUDC {
operator T();
};

typedef char yes[1];
typedef char no[2];

yes &int_(int);
yes &int_(unsigned int);
yes &int_(long);
yes &int_(unsigned long);
no & int_(...);
// if your compiler has it, then long long, unsigned long long

template<typename> struct intt { static bool const value = false; };
#define INTT(X) template<> struct intt<X> { static bool const value =
#true;
}

INTT(char);
INTT(signed char);
INTT(unsigned char);
INTT(int);
INTT(unsigned int);
INTT(long);
INTT(unsigned long);
INTT(wchar_t);
INTT(bool);
// if your compiler has it, then long long, unsigned long long, and hoping
// that i've not missed one.

C++0x introduces implementation defined integral types that may not match
any of these, but then it also has is_enum, so you don't need to write
this at all, anyway.

template<typename T>
struct is_enum {
static bool const value = sizeof int_(SlurpUDC<T>()) == sizeof(yes)
&& !intt<T>::value;
};

Notice that an enumeration type will promote to one integral type out of
the 4 above. That's all i think. Now you can write

template<typename A>
typename enable_if<is_enum<A>::value, A>::type operator|(A a, A b) { ... }

Hope it helps...

You may need to introduce special cases in "SlurpUDC" for void, function and
array types. For each of these, there are traits though that can do the work
for you, though. Alternatively, you can instead return "operator T&()",
which i believe will only need a specialization for void.

template<typename T> struct SlurpUDC { operator T&(); };
// definitely won't convert to one of the integral types
template<> struct SlurpUDC<void> { };
template<> struct SlurpUDC<void const> { };
template<> struct SlurpUDC<void volatile> { };
template<> struct SlurpUDC<void const volatile> { };

Put a INTT(short); INTT(unsigned short); and in my tests with GCC, the above
codes work fine for regognizing an enum type.
 
B

Balog Pal

Johannes Schaub (litb) said:
You first have to make sure that the type is not an integral type (just
compare it with each and every integral type), and then make sure it's not
a
class type, and then test if it converts to one integral type.

Then "every integral type" is the weak point -- extensions like long long or
__int64 are widespread, but their knowledge is tied to compiler/version
info...

So until official tr1:: support I don;t consider it a stable point, and
avoid unless very much need some functionality and accept limitation to some
compilers.
C++0x introduces implementation defined integral types that may not match
any of these, but then it also has is_enum, so you don't need to write
this
at all, anyway.

Well, native type info is a thing we needed much. At least that remains with
the concepts scrapped. :(
 
J

Johannes Schaub (litb)

Balog said:
Then "every integral type" is the weak point -- extensions like long long
or __int64 are widespread, but their knowledge is tied to compiler/version
info...

So until official tr1:: support I don;t consider it a stable point, and
avoid unless very much need some functionality and accept limitation to
some compilers.
I agree with you in principle. But I think we could test after is_enum
signals true (and remove our integral test) whether "T()" converts to
"void*": If it does, it must be an integral type, since T() then is a null
pointer constant. We will have to make sure T is not a floating point type
before.

Then i think the limitation is moved to knowing the three guaranteed
floating point types. (i think i forgot about them in is_enum).
 
J

James Kanze

[...]
Marcel, your solution works perfectly. I like how it
'documents' the enum as being a flag set. I'm using this for
now with a few small additions, but now I'm curious why people
seem generally opposed to macros...

Because they don't obey scope. In this case, all of the other
solutions have worse problems, so macros are the way to go,
Anyway, I have a few other small questions if you guys are
still interested. Balog, you mentioned the ~ operator would
not be predictable, and it sounds like it's because the way
enums are stored in memory is not predictable?

No. It's because formally, the results may be a value which is
not in the legal range of the enum (which in your case is
effectively the or of all of the values). In practice, there
won't be any problem, so I wouldn't worry about it (assuming, of
course, that you do convert the value back to the target type).
This makes me wonder if I should cast to unsigned as in
Marcel's example, or signed as in Saeed's example and as I was
doing before. Of course I'll only want to use positive numbers
for my flag sets, but enums seem to handle negative numbers
just fine, so I'm not sure what the right thing to do is here.

The safest solution is probably just to use unary + for the
conversion, e.g.:

MyEnum
operator|( MyEnum lhs, MyEnum rhs )
{
return static_cast< MyEnum >( +lhs | +rhs );
}

(The unary + operator is an arithmetic operator, so forces the
conversion of the enum into the underlying type, just as -
would.) If this looks too weird or exotic, there's always
"+ 0" instead.
So, I guess my question is: is there a way to explicitly store
an enum as an int, or an unsigned int, or whatever, either
using a language construct or some kind of compiler
instrucion? I'm using GCC 4.3.

I have some template metacode somewhere which evaluates the
underlying type, but IIRC, it's a bit hairy. Most of the time,
you don't need variables of the underlying type, however, and
the unary + trick is sufficient.
 
J

Johannes Schaub (litb)

nick said:
Thanks for all the great replies!

Marcel, your solution works perfectly. I like how it 'documents' the
enum as being a flag set. I'm using this for now with a few small
additions, but now I'm curious why people seem generally opposed to
macros...

Anyway, I have a few other small questions if you guys are still
interested. Balog, you mentioned the ~ operator would not be
predictable, and it sounds like it's because the way enums are stored
in memory is not predictable?

This makes me wonder if I should cast to unsigned as in Marcel's
example, or signed as in Saeed's example and as I was doing before. Of
course I'll only want to use positive numbers for my flag sets, but
enums seem to handle negative numbers just fine, so I'm not sure what
the right thing to do is here.

So, I guess my question is: is there a way to explicitly store an enum
as an int, or an unsigned int, or whatever, either using a language
construct or some kind of compiler instrucion? I'm using GCC 4.3.

This one shows how you can find the right type:
http://stackoverflow.com/questions/1528374/how-can-i-extend-a-lexical-cast-
to-support-enumerated-types/1528436#1528436 . Doing "get_etype<sizeof
find_etype(EnumType())>::type" should give the type that can store all the
enum's values.

That's not the underlying type though - so its sizeof and the one of the
enum may differ, but I think that doesn't matter as long as you can store
all values.
 
M

Marcel Müller

Hi!
Thanks, Balog. I did away with the ~ operator, but instead of
replacing it with a named function I figured I'd use the - and -=
operators for unsetting the flag. Here's what I ended up with, maybe
this will be useful for someone else.

inline static T operator-(T l, T r) \
{ return l&r?(T)((unsigned)l-(unsigned)r):l; } \

I cannot recommend this, since it produces undefined results in case you
try to remove more than one flag at once. Furthermore it creates a
reasonable runtime overhead because of the conditional expression.

I would prefer the use of l&~r, although ~r is not well defined by the
standard so far. In practice there is most likely not even one existing
implementation that breaks this code. And it is very unlikely that one
will ever exist, too, since in C++0x already has defined behavior
because you can specify the underlying type of the enum.

It is much more likely that future versions of the standard address this
kind of use case of enumeration types in some way. This will make the
above code superfluous.


By the way. There is another clean solution without using macros and
undefined behavior. Write your own enumeration class. You could use a
template base class to define the required operators.

template <typename I>
class EnumBase
{protected:
I Value;
EnumBase(I value) : Value(value) {}
};

template <typename I, typename T>
class EnumFlagsBase : EnumBase<I>
{protected:
EnumFlagsBase(I value) : EnumBase(value) {}

friend T operator|(T l, T r)
{ l.Value |= r.Value;
return l;
}
//...
};

class MyEnum : public EnumFlagsBase<unsigned, MyEnum>
{
MyEnum(unsigned value) : EnumFlagsBase(value) {}

public:
static const MyEnum Flag1;
static const MyEnum Flag2;
};

const MyEnum MyEnum::Flag1 = MyEnum(1);
const MyEnum MyEnum::Flag2 = MyEnum(2);


int main()
{ MyEnum val = MyEnum::Flag1|MyEnum::Flag2;
return 0;
}


The resulting object has the memory footprint of the underlying integral
type full type safety and no runtime overhead.

Strictly speaking the not fully specialized base class EnumBase could be
merged into EnumFlagsBase. But if you have some common base functions or
you need enumeration types that are no flags and you do not want the
implicit conversion to int, the additional type might be useful to avoid
unwanted code redundancies in the executable.


Marcel
 
N

nick

I cannot recommend this, since it produces undefined results in case you
try to remove more than one flag at once. Furthermore it creates a
reasonable runtime overhead because of the conditional expression.

I would prefer the use of l&~r, although ~r is not well defined by the
standard so far. In practice there is most likely not even one existing
implementation that breaks this code. And it is very unlikely that one
will ever exist, too, since in C++0x already has defined behavior
because you can specify the underlying type of the enum.

Ouch, you're right, I don't know what I was thinking here, it should
have been a simple l-=(l&r) ... must have been late. ;)
By the way. There is another clean solution without using macros and
undefined behavior. Write your own enumeration class. You could use a
template base class to define the required operators.

template <typename I>
class EnumBase
{protected:
   I Value;
   EnumBase(I value) : Value(value) {}

};

template <typename I, typename T>
class EnumFlagsBase : EnumBase<I>
{protected:
   EnumFlagsBase(I value) : EnumBase(value) {}

   friend T operator|(T l, T r)
   { l.Value |= r.Value;
     return l;
   }
   //...

};

class MyEnum : public EnumFlagsBase<unsigned, MyEnum>
{
   MyEnum(unsigned value) : EnumFlagsBase(value) {}

  public:
   static const MyEnum Flag1;
   static const MyEnum Flag2;

};

const MyEnum MyEnum::Flag1 = MyEnum(1);
const MyEnum MyEnum::Flag2 = MyEnum(2);

int main()
{ MyEnum val = MyEnum::Flag1|MyEnum::Flag2;
   return 0;

}

The resulting object has the memory footprint of the underlying integral
type full type safety and no runtime overhead.

Strictly speaking the not fully specialized base class EnumBase could be
merged into EnumFlagsBase. But if you have some common base functions or
you need enumeration types that are no flags and you do not want the
implicit conversion to int, the additional type might be useful to avoid
unwanted code redundancies in the executable.

Marcel

Now that looks cool, but the problem I have with it is the way the
enums are written... instead of being able to do something like this:

EnumFlagsBase Foobars
{
foo = 1,
bar = 2
}

I have to write:

class Foobars : public EnumFlagsBase<unsigned, Foobars>
{
Foobars(unsigned value) : EnumFlagsBase(value) {}

public:
static const Foobars Foo;
static const Foobars Bar;

};

const Foobars Foobars::Foo = Foobars(1);
const Foobars Foobars::Bar = Foobars(2);

....which seems a bit unwieldy. Thanks for explaining how it would
look, though. I wonder if there's a way to wrangle that into a more
concise format without using macros?
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,768
Messages
2,569,574
Members
45,048
Latest member
verona

Latest Threads

Top