Enum problem

H

hectorchu

typedef enum { A, B } E;

int main()
{
E e = A;
e |= B;
return 0;
}

g++ complains with:
test.cpp:6: error: invalid conversion from 'int' to 'E'

My question is, how do I do in C++ what C would do here?
The definition of E cannot be changed.
Any solution that works with g++ is fine, including compile flags.

Hector
 
I

Ian Collins

typedef enum { A, B } E;

int main()
{
E e = A;
e |= B;
return 0;
}

g++ complains with:
test.cpp:6: error: invalid conversion from 'int' to 'E'
That's correct, C++ does not let you assign any old value to an enum,
unlike C which does not care.
My question is, how do I do in C++ what C would do here?

e = static_cast<E>(e|B);

Horrible, but so is what you are doing.
 
H

hectorchu

That's correct, C++ does not let you assign any old value to an enum,
unlike C which does not care.


e = static_cast<E>(e|B);

Horrible, but so is what you are doing.

Brilliant, thanks for the help!
 
A

Alf P. Steinbach

* (e-mail address removed):
typedef enum { A, B } E;

int main()
{
E e = A;
e |= B;
return 0;
}

g++ complains with:
test.cpp:6: error: invalid conversion from 'int' to 'E'

My question is, how do I do in C++ what C would do here?
The definition of E cannot be changed.
Any solution that works with g++ is fine, including compile flags.

enum E{ A, B }; // Note: rewritten to C++ style.

inline E operator|( E lhs, E rhs )
{
return E( int(lhs) | int(rhs) );
}

int main()
{
E e = A;
e = e | B;
}


Cheers, & hth.,

- Alf
 
A

Andrey Tarasevich

typedef enum { A, B } E;

int main()
{
E e = A;
e |= B;
return 0;
}

g++ complains with:
test.cpp:6: error: invalid conversion from 'int' to 'E'

My question is, how do I do in C++ what C would do here?

From the pedantic point of view, it is impossible in general case. In
C++ the underlying type of the enum can be arbitrarily small. It is
basically guaranteed to only have enough range to store all values in
the enum-constant list and value 0 (see 7.2. for the precise
definition). This means that an attempt to force an 'A | B' value into
an object of type 'E' can result in undefined behavior simply because it
doesn't fit into the range.

Of course, there's no such danger for A=0 and B=1 (as it is in your
code), but I assume that your code sample is artificial.

Also in my opinion once you start generating values that are potentially
not included in the list of enum constants, the use of enum type for the
object that stores the value is no longer unjustified. In _both_ C and
C++, BTW. What you are trying to do calls for an explicit integral type

int e = A;
e |= B;
 
A

Andrey Tarasevich

Andrey said:
...
From the pedantic point of view, it is impossible in general case. In
C++ the underlying type of the enum can be arbitrarily small. It is
basically guaranteed to only have enough range to store all values in
the enum-constant list and value 0 (see 7.2. for the precise
definition). This means that an attempt to force an 'A | B' value into
an object of type 'E' can result in undefined behavior simply because it
doesn't fit into the range.
...

On the second thought, I'm wrong here. The precise definition given in
7.2 actually ensures that 'A | B' will fit into the range. 'A + B' might
not.
 
J

James Kanze

That's correct, C++ does not let you assign any old value to
an enum, unlike C which does not care.

In C, A and B in the above have type int, and not type E. Not
allowing assigning an int to an E would make enum's pretty
unusable.
e = static_cast<E>(e|B);
Horrible, but so is what you are doing.

As Alf has pointed out, the correct solution is overloading the
operator|.

In general, for a variety of historical reasons, enums play
several different roles in C++. When you design the enum, if
its role is that of bitmasks, then you overload the |, & and ~
operators. If it's role is that of an enumerated type, then you
might overload ++ and -- (depending on the semantics of the
enumerated type).
 
I

Ian Collins

James said:
As Alf has pointed out, the correct solution is overloading the
operator|.
I deliberately provided a horrible solution, because the action was
horrible. If e|B yields a value not in E, then you have "broken" the
enum variable.
In general, for a variety of historical reasons, enums play
several different roles in C++. When you design the enum, if
its role is that of bitmasks, then you overload the |, & and ~
operators. If it's role is that of an enumerated type, then you
might overload ++ and -- (depending on the semantics of the
enumerated type).
I'd still be very cautious overloading the | operator. When enums are
used as bitmasks, the result of a logical operation is seldom stored
back in an enum variable. The target is much more likely to be an
integer type used as a mask, where overloading is not required.

I consider saving the result of a logical operation on enum values in an
enum type unjustifiable "enum abuse".
 
A

Alf P. Steinbach

* Ian Collins:
I consider saving the result of a logical operation on enum values in an
enum type unjustifiable "enum abuse".

It depends on what the type-system is used for in the particular case. If you
had introduced the enum type in order to ensure a given small set of enumerated
values, then yes, abuse. If on the other hand the enum type is used to indicate
the proper interpretation of a set of bits, then it's good use (but in this case
the value 1<<k where k is the highest bit-position supported, should be included
among the enumerated values in order to ensure a large enough underlying
integral type -- at least until C++0x, where IIRC it can be specified).

Cheers, & hth.,

- Alf
 
J

James Kanze

I deliberately provided a horrible solution, because the action was
horrible. If e|B yields a value not in E, then you have "broken" the
enum variable.

Not at all.
I'd still be very cautious overloading the | operator. When
enums are used as bitmasks, the result of a logical operation
is seldom stored back in an enum variable.

I'd say that it almost always is used as the enum type.
The target is much more likely to be an integer type used as a
mask, where overloading is not required.

Which defeats the type system.
I consider saving the result of a logical operation on enum
values in an enum type unjustifiable "enum abuse".

Consider it rather one of the "standard" uses of C++ enum types.
As I said, the type supports two distinct roles. Intentionally.
Ideally, perhaps, we'd have two different types, but we don't.
As a general rule, the enum declaration defines clearly in which
role the enum is being used: if you see value specifications on
all of the enumerators, it's almost certainly being used as a
bitmap; if you see no value specifications at all, or at most
one or two for special cases, it's almost certainly being used
as an enumerated type. (My own program which automatically
generates the overloaded operators refused to do so unless all
of the enumerators have initialization values.)
 
I

Ian Collins

James said:
I'd say that it almost always is used as the enum type.
I've never seen that.
Which defeats the type system.
And setting an enum variable to a value not defined in that enum doesn't?

I must admit to a strong bias against setting an enum variable to a
value not defined in that enum which originated in a C project long ago.
One programmer was doing this as then passing the results to
functions with the enum type as a parameter. A good number of those
used the value in a switch statements and he tended to omit the default.
I spent weeks tracking them all down. So you could say I suffer form
post enum abuse stress!
 
J

James Kanze

I've never seen that.

std::ios_base::fmtflags? It's pretty much an established C++
idiom, in the cases where you need such bitmasks---typically,
they're most frequent in low level code, and application level
code won't use them at all.
And setting an enum variable to a value not defined in that
enum doesn't?

Not as the C++ type system is defined. The definition of enum
type is very carefully crafted to support this.
I must admit to a strong bias against setting an enum variable
to a value not defined in that enum which originated in a C
project long ago.
One programmer was doing this as then passing the results to
functions with the enum type as a parameter. A good number of those
used the value in a switch statements and he tended to omit the default.
I spent weeks tracking them all down. So you could say I suffer form
post enum abuse stress!

And how would using int instead have helped here. The question
is one of semantics, independent of the type.

As I said, the enum type in C++ is designed to fulfill two very
distinct roles. Arguably, there should be two different types,
but for various historic reasons, there aren't. In well written
code, it should always be very clear which role is being used
(although there are some special cases where the roles are a bit
mixed, e.g. std::ios_base::floatfield); if for any reason, it is
not clear, the code should clearly document it. If the enum is
used as an enumerated type, of course, you definitely shouldn't
overload operator| et al., and you shouldn't assign a value not
in the enum declaration to the enum. If the enumerated type
being used for bitfields, on the other hand, you should never
write a switch on it.
 

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,780
Messages
2,569,611
Members
45,277
Latest member
VytoKetoReview

Latest Threads

Top