|
|
|
|
||||||
![]() |
|
|
LinkBack | Outils de la discussion |
|
|
#1 |
|
Messages: n/a
Hébergeur: |
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? |
|
|
|
#2 |
|
Messages: n/a
Hébergeur: |
* 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 -- A: Because it messes up the order in which people normally read text. Q: Why is it such a bad thing? A: Top-posting. Q: What is the most annoying thing on usenet and in e-mail? |
|
|
|
#3 |
|
Messages: n/a
Hébergeur: |
On Dec 5, 10:39 pm, "Alf P. Steinbach" <al...@start.no> wrote:
> Don't use enums. Never? Werner |
|
|
|
#4 |
|
Messages: n/a
Hébergeur: |
werasm wrote:
> On Dec 5, 10:39 pm, "Alf P. Steinbach" <al...@start.no> wrote: > > >> Don't use enums. > > Never? Of course never. |
|
|
|
#5 |
|
Messages: n/a
Hébergeur: |
On Dec 5, 10:53 pm, "Victor Bazarov" <v.Abaza...@comAcast.net> wrote:
> werasm wrote: > > On Dec 5, 10:39 pm, "Alf P. Steinbach" <al...@start.no> wrote: > > >> Don't use enums. > > > Never? > > Of course never. Well, care to give some reasons and alternatives? W |
|
|
|
#6 |
|
Messages: n/a
Hébergeur: |
werasm wrote:
> On Dec 5, 10:53 pm, "Victor Bazarov" <v.Abaza...@comAcast.net> wrote: >> werasm wrote: >>> On Dec 5, 10:39 pm, "Alf P. Steinbach" <al...@start.no> wrote: >> >>>> Don't use enums. >> >>> Never? >> >> Of course never. > > Well, care to give some reasons and alternatives? Read Alf's response. I totally agree with him on his suggestions. V -- Please remove capital 'A's when replying by e-mail I do not respond to top-posted replies, please don't ask |
|
|
|
#7 |
|
Messages: n/a
Hébergeur: |
On Dec 5, 10:59 pm, "Victor Bazarov" <v.Abaza...@comAcast.net> wrote:
> werasm wrote: > > On Dec 5, 10:53 pm, "Victor Bazarov" <v.Abaza...@comAcast.net> wrote: > >> werasm wrote: > >>> On Dec 5, 10:39 pm, "Alf P. Steinbach" <al...@start.no> wrote: > > >>>> Don't use enums. > > >>> Never? > > >> Of course never. > > > Well, care to give some reasons and alternatives? > > 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 |
|
|
|
#8 |
|
Messages: n/a
Hébergeur: |
werasm wrote:
> On Dec 5, 10:39 pm, "Alf P. Steinbach" <al...@start.no> wrote: >> Don't use enums. > > 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. -- Best regards, Andrey Tarasevich |
|
|
|
#9 |
|
Messages: n/a
Hébergeur: |
"Andrey Tarasevich" <andreytarasevich@hotmail.com> wrote in message news:fj79bk$tsu$1@aioe.org... > werasm wrote: >> On Dec 5, 10:39 pm, "Alf P. Steinbach" <al...@start.no> wrote: >>> Don't use enums. >> >> 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... > 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 |
|
|
|
#10 |
|
Messages: n/a
Hébergeur: |
Howard wrote:
> ... > 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. -- Best regards, Andrey Tarasevich |
|
|
|
#11 |
|
Messages: n/a
Hébergeur: |
werasm wrote:
> On Dec 5, 10:59 pm, "Victor Bazarov" <v.Abaza...@comAcast.net> wrote: >> werasm wrote: >>> On Dec 5, 10:53 pm, "Victor Bazarov" <v.Abaza...@comAcast.net> wrote: >>>> werasm wrote: >>>>> On Dec 5, 10:39 pm, "Alf P. Steinbach" <al...@start.no> wrote: >>>>>> Don't use enums. >>>>> Never? >>>> Of course never. >>> Well, care to give some reasons and alternatives? >> 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. 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 |
|
|
|
#12 |
|
Messages: n/a
Hébergeur: |
On Dec 6, 10:44 am, anon <a...@no.no> wrote:
> werasm wrote: > > On Dec 5, 10:59 pm, "Victor Bazarov" <v.Abaza...@comAcast.net> wrote: > >> werasm wrote: > >>> On Dec 5, 10:53 pm, "Victor Bazarov" <v.Abaza...@comAcast.net> wrote: > >>>> werasm wrote: > >>>>> On Dec 5, 10:39 pm, "Alf P. Steinbach" <al...@start.no> wrote: > >>>>>> Don't use enums. > >>>>> Never? > >>>> Of course never. > >>> Well, care to give some reasons and alternatives? > >> 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. > > 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. |
|
|
|
#13 |
|
Messages: n/a
Hébergeur: |
ManicQin wrote:
> On Dec 6, 10:44 am, anon <a...@no.no> wrote: >> werasm wrote: >>> On Dec 5, 10:59 pm, "Victor Bazarov" <v.Abaza...@comAcast.net> wrote: >>>> werasm wrote: >>>>> On Dec 5, 10:53 pm, "Victor Bazarov" <v.Abaza...@comAcast.net> wrote: >>>>>> werasm wrote: >>>>>>> On Dec 5, 10:39 pm, "Alf P. Steinbach" <al...@start.no> wrote: >>>>>>>> Don't use enums. >>>>>>> Never? >>>>>> Of course never. >>>>> Well, care to give some reasons and alternatives? >>>> 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. >> 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? 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 |
|
|
|
#14 |
|
Messages: n/a
Hébergeur: |
On Dec 6, 12:55 pm, ManicQin <Manic...@gmail.com> wrote:
> On Dec 6, 10:44 am, anon <a...@no.no> wrote: > > werasm wrote: > > > On Dec 5, 10:59 pm, "Victor Bazarov" <v.Abaza...@comAcast.net> wrote: > > >> werasm wrote: > > >>> On Dec 5, 10:53 pm, "Victor Bazarov" <v.Abaza...@comAcast.net> wrote: > > >>>> werasm wrote: > > >>>>> On Dec 5, 10:39 pm, "Alf P. Steinbach" <al...@start.no> wrote: > > >>>>>> Don't use enums. > > >>>>> Never? > > >>>> Of course never. > > >>> Well, care to give some reasons and alternatives? > > >> 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. > > 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 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. -- James Kanze (GABI Software) email:james.kanze@gmail.com Conseils en informatique orientée objet/ Beratung in objektorientierter Datenverarbeitung 9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34 |
|
|
|
#15 |
|
Messages: n/a
Hébergeur: |
On Dec 5, 10:24 pm, Christopher <cp...@austin.rr.com> wrote:
> 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. -- James Kanze (GABI Software) email:james.kanze@gmail.com Conseils en informatique orientée objet/ Beratung in objektorientierter Datenverarbeitung 9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34 |
|
|
|
#16 |
|
Messages: n/a
Hébergeur: |
On Wed, 5 Dec 2007 13:24:13 -0800 (PST), Christopher wrote:
>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 -- Roland Pibinger "The best software is simple, elegant, and full of drama" - Grady Booch |
|
![]() |
| Outils de la discussion | |
|
|