Enumerations

  • Thread starter Christopher Benson-Manica
  • Start date
C

Christopher Benson-Manica

I'll try to explain what I want to do:

I have foo.h and foo.cpp. Units that include foo.h will define an
enumeration bar:

enum bar { typeNone, typeBaz, typeQuux, ... , count };

A method defined in foo.cpp needs to return typeNone. Is using

static_cast< bar >( 0 )

acceptable?

Also, methods in foo.cpp need to return typeBaz, typeQuux, etc. Is
there any way to specify that these are values of the bar enumeration
without actually defining bar (since units that include foo.h will
take care of that)?
 
S

Steve

I'll try to explain what I want to do:

I have foo.h and foo.cpp. Units that include foo.h will define an
enumeration bar:

enum bar { typeNone, typeBaz, typeQuux, ... , count };

A method defined in foo.cpp needs to return typeNone. Is using

static_cast< bar >( 0 )

acceptable?


Am I missing something obvious? Why not just return typeNone?

Also, methods in foo.cpp need to return typeBaz, typeQuux, etc. Is
there any way to specify that these are values of the bar enumeration
without actually defining bar (since units that include foo.h will
take care of that)?


Is foo.cpp #including foo.h?


Steve.
 
S

Steve

Am I missing something obvious? Why not just return typeNone?




Is foo.cpp #including foo.h?


Steve.


Oops, sorry, let me read the OP again more closely...

OK, let me rephrase my answer...

Why not put the enum declaration in foo.h?


Steve.
 
C

Christopher Benson-Manica

Steve said:
Am I missing something obvious? Why not just return typeNone?

My intent is that neither foo.h nor foo.cpp will actually define bar,
perhaps something like

foo.h:

enum bar;

foo.cpp

#include "foo.h"

/* whatever

my_special_unit.cpp:

#include "foo.h"

enum bar { typeNone, typeBaz, count };

some_other_unit.cpp:

#include "foo.h"

enum bar { typeNone, typeQuux, typeCharlie, count };

Presumably this won't work, since the bar enumeration is never fleshed
out for foo.cpp. Basically, I want to make the symbols that the
enumeration will use available to foo.cpp without actually defining
the enumeration there. Does that make sense? Is it possible?
 
P

Peter van Merkerk

Christopher said:
My intent is that neither foo.h nor foo.cpp will actually define bar,
perhaps something like

foo.h:

enum bar;

foo.cpp

#include "foo.h"

/* whatever

my_special_unit.cpp:

#include "foo.h"

enum bar { typeNone, typeBaz, count };

some_other_unit.cpp:

#include "foo.h"

enum bar { typeNone, typeQuux, typeCharlie, count };

Presumably this won't work, since the bar enumeration is never fleshed
out for foo.cpp. Basically, I want to make the symbols that the
enumeration will use available to foo.cpp without actually defining
the enumeration there. Does that make sense? Is it possible?

If you need to use the enum as a return type and the function must be
declared in the .h file (which is the case if I understood the original
question correctly) the answer is no. Only if the enum is used only
inside function in the .cpp file but never as return type or argument
the enum can be declared inside the .cpp file.

In case of a class you might consider putting the enum definition in the
private section of the class. That way it is clear that the enum is
intended for internal use only, and isn't accessible to the outside
world. Another advantage is that it is defined within the scope of the
class, so potential name clashes are avoided.
 
C

Christopher Benson-Manica

Peter van Merkerk said:
In case of a class you might consider putting the enum definition in the
private section of the class. That way it is clear that the enum is
intended for internal use only, and isn't accessible to the outside
world. Another advantage is that it is defined within the scope of the
class, so potential name clashes are avoided.

Well, here's another question: If I put the enum definition in the
class, can a subclass override that definition?
 
J

JKop

Christopher Benson-Manica posted:
No, please do! Seriously - otherwise, how do I learn how to improve? :)

First I'm going to talk about enums. They're pretty much useless. For
instance:

enum Month
{
Jan = 1,
Feb = 2,
Mar = 3,
Apr = 4,
May = 5,
Jun = 6,
Jul = 7,
Aug = 8,
Sep = 9,
Oct = 10,
Nov = 11,
Dec = 12
};

int main()
{
Month my_super_duper_month(13);
}


So why are they useful? They're only merit is with switch statements:


switch (some_month)
{
case Jan:
//blah
case Feb:

//blah

}


The compiler can warn you when you leave one out of the switch statement.
Yipee!

Anyway, I'd suggest you return an "unsigned char" from this function. If you
want to give certain numbers special names, then by all means:

namespace ChocolateValues
{
const unsigned char no_error = 0;
const unsigned char multiple_errors = 1;
};

unsigned char SomeFunction()
{
return ChocolateValues::no_error;
}




-JKop
 
M

Mark A. Gibbs

JKop said:
First I'm going to talk about enums. They're pretty much useless. For
instance:

you have some serious misconceptions about enumerations.
enum Month
{
Jan = 1,
Feb = 2,
Mar = 3,
Apr = 4,
May = 5,
Jun = 6,
Jul = 7,
Aug = 8,
Sep = 9,
Oct = 10,
Nov = 11,
Dec = 12
};

int main()
{
Month my_super_duper_month(13);
}

compiler error - illegal implicit conversion to int.

mind you:

Month m(static_cast<Month>(13));

would work. but that is a case of directly and maliciously circumventing
the enumeration.
So why are they useful? They're only merit is with switch statements:


switch (some_month)
{
case Jan:
//blah
case Feb:

//blah

}


The compiler can warn you when you leave one out of the switch statement.
Yipee!

what compiler? i would find it quite bizarre if a compiler barked that
warning at me, unless it had some crazy super verbose warning setting.
it is not an error, or even a mistake, to only switch on certain
enumerators.
Anyway, I'd suggest you return an "unsigned char" from this function. If you
want to give certain numbers special names, then by all means:

namespace ChocolateValues
{
const unsigned char no_error = 0;
const unsigned char multiple_errors = 1;
};

unsigned char SomeFunction()
{
return ChocolateValues::no_error;
}

and this has to be the wackiest suggestion of all, considering that you
write off enums for having an implicit conversion to int above (which
they don't, as i pointed out).

in fact, the example above is LESS robust than if it had used enums.
consider this:

////////////////////////////////////////////////////////

namespace error {
const unsigned char no_error = 0;
const unsigned char out_of_memory = 1;
const unsigned char cannot_open_file = 2;
}

enum type {
no_error,
out_of_memory,
cannot_open_file
};

void print_error(unsigned char c) {
// print error message based on error code
switch (c) {
case error::no_error: // whatever
case error::eek:ut_of_memory: // whatever
case error::cannot_open_file: // whatever
// ...
}
}

// nothing seems wrong with this...
unsigned char oops = 3;

// ...but
print_error(oops); // <--- what should this do?

////////////////////////////////////////////////////////

as opposed to:

////////////////////////////////////////////////////////

namespace error {

enum type {
no_error,
out_of_memory,
cannot_open_file
};

}

void print_error(error::type c) {
// print error message based on error code
switch (c) {
case error::no_error: // whatever
case error::eek:ut_of_memory: // whatever
case error::cannot_open_file: // whatever
// ...
}
}

error::type nope = 3; // won't compile

// this is explicit and obvious - the programmer knew what (s)he
// was doing, and should be able to explain him/herself
error::type iffy = static_cast<error::type>(3);

error::type ok = error::eek:ut_of_memory; // obviously ok

////////////////////////////////////////////////////////

enumerations are not perfect - personally, i was looking for a better
solution just last week, and i was not satisfied with the options - but
until the standard committee gives the thumbs up to the new super enums,
we're stuck with them. despite all their shortcomings though, i'd have
to say they're still a better choice than simple ints (or unsigned
char's for that matter).

indi
 
P

Peter van Merkerk

Christopher said:
Well, here's another question: If I put the enum definition in the
class, can a subclass override that definition?

I'm afraid not.
 
J

JKop

Mark A. Gibbs posted:
would work. but that is a case of directly and maliciously
circumventing the enumeration.


Month blah = Month(13);

error::type nope = 3; // won't compile

= EnumName(3); //*will* compile, regardless if the enum
contains a 3.

-JKOp
 
T

tom_usenet

I'll try to explain what I want to do:

I have foo.h and foo.cpp. Units that include foo.h will define an
enumeration bar:

enum bar { typeNone, typeBaz, typeQuux, ... , count };

A method defined in foo.cpp needs to return typeNone. Is using

static_cast< bar >( 0 )

acceptable?

Also, methods in foo.cpp need to return typeBaz, typeQuux, etc. Is
there any way to specify that these are values of the bar enumeration
without actually defining bar (since units that include foo.h will
take care of that)?

All definitions of bar must be token for token identical according to
the one definition rule. If they aren't, you've got undefined
behaviour.

In your case you might just want to return some named constants, and
have other TUs cast them to whatever enum type they want (and
presumably a different one for each TU).

Tom
 
J

JKop

Christopher Benson-Manica posted:
Granted, but why on earth would you want to do *that*?

Because it can be done. If you write a function that takes
a month, then you've to check that it's >= 1 and <= 12.
Therefore, what use has an enum? Why not just use constant
variables?

namespace Month{
const unsigned char jan = 1,
feb = 2,
mar = 3,
apr = 4,
may = 5,
jun = 6,
//and so on


-JKop
 
A

Andre Kostur

JKop said:
Christopher Benson-Manica posted:


Because it can be done. If you write a function that takes

So can dereferencing a NULL pointer, and reinterpret_casting stuff between
incompatable types. Doesn't mean that it's defined behaviour.
a month, then you've to check that it's >= 1 and <= 12.
Therefore, what use has an enum? Why not just use constant
variables?

No you don't. Your function simply takes a Month as a parameter. No need
to check for range. Your caller would have to invoke undefined behaviour
to break your function then.
 
J

Jeff Flinn

Christopher Benson-Manica said:
Granted, but why on earth would you want to do *that*?

I think he means, how would you keep a user from inadvertently doing that
via a compiler error.

Jeff F
 
O

Old Wolf

[snip - debate over whether to use 'enum' for month names]
No you don't. Your function simply takes a Month as a parameter. No need
to check for range. Your caller would have to invoke undefined behaviour
to break your function then.

If an enum contains values 1...11, then it can also hold values 12...15
without causing undefined behaviour. This language rule exists to allow
'flag' enums, eg:

enum flags
{
Foo = 0x80,
Bar = 0x40,
Quz = 0x20,
};

flags f = Foo | Quz; // 0xA0 is not an enum member, but is valid

So you still have to check range (> 0 as well as <= 12)
 
A

Andre Kostur

(e-mail address removed) (Old Wolf) wrote in
[snip - debate over whether to use 'enum' for month names]
No you don't. Your function simply takes a Month as a parameter. No
need to check for range. Your caller would have to invoke undefined
behaviour to break your function then.

If an enum contains values 1...11, then it can also hold values
12...15 without causing undefined behaviour. This language rule exists
to allow 'flag' enums, eg:

I stand corrected. The enum may contain values beyond the specified ones.

I guess, for me, that would leave it in the realm of "bad programming
practice". One would need to explicitly do something to put the enum out
of range (explicit construction with an out of range value, or do some sort
of bitwise or arithmetic operations on the enums)
 

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,755
Messages
2,569,539
Members
45,024
Latest member
ARDU_PROgrammER

Latest Threads

Top