deriving a class - extending enum

C

Christopher

I have run into this dilemma enough to ask about it now.

Say I have a base class:

class BaseClass
{
public:
enum BaseClassEnumType
{
firstEnum,
secondEnum,
num_enumtypes
}
....
BaseClassEnumType m_something;
};

Down the road I make a derived class:

class DerivedClass : public BaseClass
{
....
};

I also have some routine(s) somewhere:

void Foo(BaseClass * ptr)
{
if( ptr->m_something == BaseClass::secondEnum)
{
// do something
}
...
}

Now the situation arises, when I make my derived class, where I want
to "extend" the enumerations in the base class. In this example, I
want to add the value "thirdEnum". In the Real World, perhaps I need
to add more error codes specific to derived class in an error type
enumeration that was part of the base class, or something similar. How
do you accomplish that whether it be through redesign or some existing
concept I am not familiar with?

Of course, the thought of breaking the enumeration out of BaseClass
occured to me, but it has the same result: Every time you need a new
value in the enumeration, you have to edit preexisting code that
"lives" in the BaseClass, in order to accomplish something that is
specific to the derived class. This is especially problematic to me
when the BaseClass and the enumeration are part of a separate library.

This must be a common problem. Any thoughts?
 
A

Alf P. Steinbach

* Christopher:
I have run into this dilemma enough to ask about it now.

Say I have a base class:

class BaseClass
{
public:
enum BaseClassEnumType
{
firstEnum,
secondEnum,
num_enumtypes
}
...
BaseClassEnumType m_something;
};

Down the road I make a derived class:

class DerivedClass : public BaseClass
{
...
};

I also have some routine(s) somewhere:

void Foo(BaseClass * ptr)
{
if( ptr->m_something == BaseClass::secondEnum)
{
// do something
}
...
}

Now the situation arises, when I make my derived class, where I want
to "extend" the enumerations in the base class. In this example, I
want to add the value "thirdEnum". In the Real World, perhaps I need
to add more error codes specific to derived class in an error type
enumeration that was part of the base class, or something similar. How
do you accomplish that whether it be through redesign or some existing
concept I am not familiar with?

Represent errors as a hierarchy of class types.

Use exceptions instead of error codes.

Don't use enums.


Cheers, & hth.,

- Alf
 
W

werasm

Read Alf's response. I totally agree with him on his suggestions.

I've read his response, hence my question (Never, or never for
the particular problem). The question still remains.

Regards,

Werner
 
A

Andrey Tarasevich

werasm said:
Never?
...

Forget it. _DO_ use enums. The hierarchy of exception classes is a good
idea, but _not_ as a replacement for enum constants as exception codes.
An attempt to introduce a separate exception class for every concrete
exception type will only lead to a disaster. In the exception hierarchy
the classes themselves are supposed to serve for a rough division of
various exception types into relatively large groups or..., well...
"classes" of exceptions. The concrete exception type within the class is
still best described with a enum constant.

Your original idea of "extending" the enum in the derived class can be
easily implemented as follows

class BaseException {
public:
enum Type {
firstEnum,
secondEnum,
next_enum_,
};

int m_something;
...
};

class DerivedException : public BaseException {
typedef BaseException BASE;
public:
enum Type {
oneMoreEnum = BASE::next_enum_,
yetMoreEnum,
stillMoreEnum,
next_enum_
};
...
};

class DerivedDerivedException : public DerivedException {
typedef DerivedException BASE;
public:
enum Type {
oneAdditionalEnum = BASE::next_enum_,
anotherAdditionalEnum,
moreAdditionalEnum,
next_enum_
};
...
};

// And so on...

Note how the 'next_enum_' name is used in the above example and how
(together with the 'BASE' typedef) it makes all derived exception
classes to have uniform definitions.

Of course, in this case you are not really "extending" the base enum
type, but rather create another enum type with sequence of constants
that continue the inherited sequence. Of course, each enum is a separate
type, but that shouldn't worry you at all: simply disregard the enum
types entirely and use an 'int' value to describe the concrete exception
instead.
 
H

Howard

Andrey Tarasevich said:
Forget it. _DO_ use enums. The hierarchy of exception classes is a good
idea, but _not_ as a replacement for enum constants as exception codes.
An attempt to introduce a separate exception class for every concrete
exception type will only lead to a disaster. In the exception hierarchy
the classes themselves are supposed to serve for a rough division of
various exception types into relatively large groups or..., well...
"classes" of exceptions. The concrete exception type within the class is
still best described with a enum constant.

Your original idea of "extending" the enum in the derived class can be
easily implemented as follows

class BaseException {
public:
enum Type {
firstEnum,
secondEnum,
next_enum_,
};

int m_something;
...
};

class DerivedException : public BaseException {
typedef BaseException BASE;
public:
enum Type {
oneMoreEnum = BASE::next_enum_,
yetMoreEnum,
stillMoreEnum,
next_enum_
};
...
};

class DerivedDerivedException : public DerivedException {
typedef DerivedException BASE;
public:
enum Type {
oneAdditionalEnum = BASE::next_enum_,
anotherAdditionalEnum,
moreAdditionalEnum,
next_enum_
};
...
};

// And so on...

There's a potential problem with that: what if at some level of the
hierarchy, you have two or more derived classes, such as DerivedException1
and DerivedException2, both derived from BaseException? Then you'll have
multiple enum values with the same integer value. Of course, that may not
be any problem at all... it depends how you use the enums, I guess.
-Howard
 
A

Andrey Tarasevich

Howard said:
...
There's a potential problem with that: what if at some level of the
hierarchy, you have two or more derived classes, such as DerivedException1
and DerivedException2, both derived from BaseException? Then you'll have
multiple enum values with the same integer value. Of course, that may not
be any problem at all... it depends how you use the enums, I guess.
...

The idea is that the concrete exception is supposed to be fully
identified by the pair "class type+enum code", not by enum code alone.
In the scenario you describe the identical enum values would belong to
completely different "classes" (or "scopes") of exceptions. As such,
they are not supposed to cause any conflicts.
 
A

anon

werasm said:
I've read his response, hence my question (Never, or never for
the particular problem). The question still remains.

I think they said never use enums to check errors. Or, have I misunderstood?

You can make a base error class, from which all other error classes
inherits. Then it is easy to add error codes by just adding classes
together with derived classes. When error occurs, throw an error class
 
M

ManicQin

I think they said never use enums to check errors. Or, have I misunderstood?

You can make a base error class, from which all other error classes
inherits. Then it is easy to add error codes by just adding classes
together with derived classes. When error occurs, throw an error class

But is'nt throwing an error class is much much heavier than returning
an enum?
I dont see any usability/readability benefits in throwing exceptions
over returning error codes.
 
A

anon

ManicQin said:
But is'nt throwing an error class is much much heavier than returning
an enum?

Yes, but only in a case when an error happens.
I dont see any usability/readability benefits in throwing exceptions
over returning error codes.

You do not have to check error codes whenever you call functions,
therefore code is much cleaner
 
J

James Kanze

But isn't throwing an error class is much much heavier than
returning an enum?

That's not really the question. Depending on the type of error,
throwing an exception might not really be appropriate.

None of which has anything to do with the poster's original
question, of course.
I dont see any usability/readability benefits in throwing
exceptions over returning error codes.

If the error can't be handled locally, an exception will
propagate it up. If it can be handled locally, of course, an
exception is just a means of making it more awkward for the
user. As a general rule, a function should use a return code
unless there is no reasonable chance of handling the error
locally.
 
J

James Kanze

I have run into this dilemma enough to ask about it now.
Say I have a base class:
class BaseClass
{
public:
enum BaseClassEnumType
{
firstEnum,
secondEnum,
num_enumtypes
}
...
BaseClassEnumType m_something;
};
Down the road I make a derived class:
class DerivedClass : public BaseClass
{
...
};
I also have some routine(s) somewhere:
void Foo(BaseClass * ptr)
{
if( ptr->m_something == BaseClass::secondEnum)
{
// do something
}
...
}
Now the situation arises, when I make my derived class, where
I want to "extend" the enumerations in the base class.

You can't. In this case (and in general), the compiler needs to
know all of the enum values in order to determine the size of
the enum. Any place which could see BaseClassEnumType, and
declare a variable of that type, may end up with a variable too
small to hold any additional values. This would break the C++
object model. Seriously.

You probably don't want to. If the value is visible
to the outside (even indirectly, e.g. as state which affects
which functions might be called), then you can't add to it
without violating the LSP. If it's not, then there's no problem
defining your own enum, and using it in the derived class.

If you want to define a contract such that the derived class can
add to the enum, in an organized manner, it's possible, by
reserving some range of values for the derived class, but the
derived class will still have to define its values in a somewhat
special way:

class BaseClass
{
public:
enum BaseClassEnumType
{
first,
second,
derivedState = 0x80
} ;
} ;

class Derived
{
static BaseClassEnumType const
third
= static_cast< BaseClassEnumType >( derivedState | 1 ) ;
static BaseClassEnumType const
fourth
= static_cast< BaseClassEnumType >( derivedState | 2 ) ;
// ...
} ;
In this example, I want to add the value "thirdEnum". In the
Real World, perhaps I need to add more error codes specific to
derived class in an error type enumeration that was part of
the base class, or something similar. How do you accomplish
that whether it be through redesign or some existing concept I
am not familiar with?

I've mainly encountered this when a class explicitly uses some
other class: my RegularExpression class explicitly uses my
CharacterClass class to handle things like "[...]".
(Explicitly, in the sense that the documentation of
RegularExpression refers to the documentation of CharacterClass
for such elements.) In such cases, I've done more or less as
above:

// Status:
// =======
//
//!@brief
//! The various states that can result after construction.
//!
//! This status is an attribute of the class, which can be
//! tested anytime after the object has been constructed, and
//! which should be tested immediately after construction,
//! before any attempt to use the object.
//!
//! The last entry is used to report errors detected in
//! #CharacterClass, and is or'ed with the results of the
//! constructor of this class.
//
-----------------------------------------------------------------------
enum Status
{
//! Success, the object was correctly constructed.
//
-------------------------------------------------------------------
ok = 0,

//! Empty expression. This is the status of a
RegularExpression
//! constructed by the default constructor.
//
-------------------------------------------------------------------
emptyExpr,

//! The delimiter specified in the constructor was a
//! meta-character.
//
-------------------------------------------------------------------
illegalDelimiter,

//! End of file without encountering the delimiter
//! (delimiter specified).
//
-------------------------------------------------------------------
unexpectedEOF,

//! Closing parentheses without opening parentheses, or
//! vice versa.
//
-------------------------------------------------------------------
mismatchedParen,

//! Additional characters at the end of the expression.
//! (I don't think that this can actually happen.)
//
-------------------------------------------------------------------
garbageAtEnd,

//! An error in the specification of a
//! Gabi::CharacterClass. This value is in fact a set
//! of values; the declared value represents a high order
//! bit, which signals an error of this type, and the
//! exact error returned by Gabi::CharacterClass is on
//! the low order bits.
//
-------------------------------------------------------------------
illegalCharClass = 0x80
} ;
Of course, the thought of breaking the enumeration out of
BaseClass occured to me, but it has the same result: Every
time you need a new value in the enumeration, you have to edit
preexisting code that "lives" in the BaseClass, in order to
accomplish something that is specific to the derived class.
This is especially problematic to me when the BaseClass and
the enumeration are part of a separate library.

The user of base class must know what to expect. If you define
an enum with three values in the base class, a user of the base
class might write a switch with those three values, assured that
he had covered all cases. Or a user of the base class may use
the enum value to index into an array with num_enumtypes
entries. The derived class cannot add to it without breaking
his code (and thus violating the LSP).

If you clearly announce up front that there are special values
which will be defined by the derived class, of course, it is
different. Anyone using RegularExpression::Status, above, knows
that he will need special handling if (status & 0x80) != 0.
(Also, the presence of a value 0x80 in the enum guarantees that
the enum type can contain values up to 0xFF, according to the
standard.)
This must be a common problem. Any thoughts?

I suspect that it's a lot less common than you think. The whole
point of having a base class is that the user can use it without
knowing about the derived class. For your example with error
codes, for example, this would only be the case if the base
class explicitly provided for the possibility. Such cases do
exist, e.g. my RegularExpression class, but they are very, very
rare. Most of the time, what you'll want is sub-states: the
derived class defines an enum with additional state information,
and the state in the derived class would be a pair of enums.
Users of the base class only see the base class enum; users of
the derived class can use both, effectively seeing something
like secondEnum.a, secondEnum.b, etc.
 
R

Roland Pibinger

Now the situation arises, when I make my derived class, where I want
to "extend" the enumerations in the base class. In this example, I
want to add the value "thirdEnum". In the Real World, perhaps I need
to add more error codes specific to derived class in an error type
enumeration that was part of the base class, or something similar. How
do you accomplish that whether it be through redesign or some existing
concept I am not familiar with?

You could experiment with the folowing approach:
http://www.codeproject.com/KB/cpp/InheritEnum.aspx
 

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,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top