 |
|
|
 |
|
|
 |
|
 |
C++ does not have a direct facility to allow one enum type to be extended by inheritance but here is a simple workaround: In the derived class definition I add const members Apple and Pear of the enum type Fruit. In constructor mem-initializer-list I put the identifiers Apple and Pear. In the expression-list of the identifiers I take the last enum value of Fruit plus 1 and cast it to Fruit. All enum extentions made in the derived class become member of enum Fruit. So there is no problem to call eat en consume with the new members. Because the base class is not designed for the new members, the programmer can override parts with wrappers in the derived class.
#include "stdafx.h"
#include <ostream.h>
class base { public: enum Fruit { Orange, Mango, Banana }; int eat(enum Fruit fruit); };
int base::eat(enum Fruit fruit) { return fruit; }
class derived : public base { public: derived(); int consume(enum Fruit fruit); const Fruit Apple, Pear; };
derived::derived() : Apple(Fruit(Banana + 1)), Pear(Fruit(Apple + 1)) { }
int derived::consume(enum Fruit fruit) { return fruit; }
int main(int argc, char* argv[]) { derived o;
cout << "Orange = " << o.eat(o.Orange) << endl; cout << "Mango = " << o.eat(o.Mango) << endl; cout << "Banana = " << o.eat(o.Banana) << endl; cout << "Apple = " << o.eat(o.Apple) << endl; cout << "Pear = " << o.eat(o.Pear) << endl; cout << "Orange = " << o.consume(o.Orange) << endl; cout << "Mango = " << o.consume(o.Mango) << endl; cout << "Banana = " << o.consume(o.Banana) << endl; cout << "Apple = " << o.consume(o.Apple) << endl; cout << "Pear = " << o.consume(o.Pear) << endl; return 0; }
modified on Wednesday, January 7, 2009 5:50 AM
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
 |
Is this code free to use for all purposes?
i.e. // Permission to use, copy, modify, distribute, and sell this software for any // purpose is hereby granted without fee, provided that the above copyright // notice appear in all copies and that both that copyright notice and this // permission notice appear in supporting documentation. // The author makes no representations about the suitability of this software // for any purpose. It is provided "as is" without express or implied // warranty.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Yes it is free for all purposes,
warlock6x3 wrote: provided that the above copyright // notice appear in all copies and that both that copyright notice and this // permission notice appear in supporting documentation.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
say
Fruits.h ---------- enum Fruit { orange, grape, pear };
#include "Fruits.h"
enum MyFruit { apple = pear, <----- last element of Fruit. otherfruits };
etc, etc.
-- modified at 3:07 Tuesday 7th November, 2006
-Prakash
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
|
 |
|
 |
One thing to note with simulating enum inheritance is that, unlike normal C++ inheritance where one can use the derived class when a base class is required. i.e
class Base {}; class Derived: public Base {};
void foo(Base); void bar() { foo(Base()); foo(Derived()); }
In enum enheritance however, the usage works the the other way round. That is why eat(Orange) should compile as Orange is one of Fruit, but eat(Apple) should not compile as Apple is in the NewFruit enums that eat() does not know about.
Cheers, Fhulu
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
 |
... it doesn't work. Try e.g.
void test() { MyFruit orange (Orange), apple (Apple); if (orange == apple) { cout << "oranges are apples" << endl; } } Output:
oranges are apples
Looking at the code operator EnumT() seems to be the problem. Your solution is promising but needs some enhancements.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
I see what you mean, and I've already made an update on the article with notes highlighting this. The manual update on Code Project takes a while, maybe because of timezone difference.
It is important to ensure that the values on the two enum types don't overlap. There is no way I know of finding out the last enum value in an enum list. If anyone knows a way, please let me know. In the meantime, a workaround in the updated version looks like this:
enum Fruit { Orange, Mango, Banana, FruitCount };
and MyFruit becomes
enum MyFruit { Apple = FruitCount, Pear, MyFruitCount };
Thanks for the feedback.
Fhulu
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Lidzhade Fhulu wrote: It is important to ensure that the values on the two enum types don't overlap. There is no way I know of finding out the last enum value in an enum list.
Maybe by naming convention but not directly.
Lidzhade Fhulu wrote: In the meantime, a workaround in the updated version looks like this: enum Fruit { Orange, Mango, Banana, FruitCount }; and MyFruit becomes enum MyFruit { Apple = FruitCount, Pear, MyFruitCount };
This works only for one 'derived' enum. The users of your template need to define ranges for their enum values upfront.
Another constraint - from Stroustrup p.77: "The range of an enumeration holds all the enumerator's enumerator values rounded up to the nearest larger binary power minus 1. The range goes down to 0 if the smallest enumerator is non-negative and to the nearest lesser negative binary power if the smallest enumerator is negative."
Have you considered operator int() instead of operator EnumT()? I'm not sure which is better though.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Roland Pibinger wrote: This works only for one 'derived' enum. The users of your template need to define ranges for their enum values upfront.
Good point - and it is really a constraint on enum types in general. The FruitCount trick helps to a certain extent. If one need to derive from an existing enum type, they must at least know the last value in the base enum type. As for the first value in the base range, I don't think it is important as it is very unlikely that one uses enum values until they wrap over to the first value.
Roland Pibinger wrote: Another constraint - from Stroustrup p.77: "The range of an enumeration holds all the enumerator's enumerator values rounded up to the nearest larger binary power minus 1. The range goes down to 0 if the smallest enumerator is non-negative and to the nearest lesser negative binary power if the smallest enumerator is negative."
True, though it is unlikely that one will exhaust all values in the integer range. One can still quickly wrap over to 0 if values used are closer to INT_MAX or UINT_MAX. So maybe the FruitCount trick must be changed to FirstInvalidFruit and it won't matter when values wrap over to 0 as the will still be unique.
Roland Pibinger wrote: Have you considered operator int() instead of operator EnumT()? I'm not sure which is better though.
Good idea - I don't think it is a matter of which is better, we need them both. 1. operator EnumT() is required as InheritEnum is really just a wrapper that should be interchangeable with EnumT. Hence the constructor accepting EnumT is not explicit. It also does not have any ill side-effect associated with conversion operators like if std::string had operator const char*().
2. operator int() must be there as all C++ enums can implicitly decay to ints. The constructor accepting an int must be explicit as we don't want to inadvertedly convert to InheritEnum.
Thanks again for the valuable feedback. I will make not of your points when updating the article.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Lidzhade Fhulu wrote: 2. operator int() must be there as all C++ enums can implicitly decay to ints. The constructor accepting an int must be explicit as we don't want to inadvertedly convert to InheritEnum.
Your InheritEnum template already 'decays' to an int (via operator EnumT()). Currently your template converts both EnumT and BaseEnumT to EnumT. Maybe I have misunderstood your intentions and that's the point of your implementation.  (BTW, if you have more the one conversion operator conversion might be ambiguous in some cases.)
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Roland Pibinger wrote: Your InheritEnum template already 'decays' to an int (via operator EnumT()).
True, didn't realise that. So I will just leave it at operator EnumT() then and hence no more two conversion operators.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Lidzhade Fhulu wrote: It is important to ensure that the values on the two enum types don't overlap. There is no way I know of finding out the last enum value in an enum list. If anyone knows a way, please let me know. In the meantime, a workaround in the updated version looks like this:
enum Fruit { Orange, Mango, Banana, FruitCount };
and MyFruit becomes
enum MyFruit { Apple = FruitCount, Pear, MyFruitCount };
Hmmm. FruitCount shouldn't really be part of the enum, since eat(FruitCount) then becomes technically valid but almost certainly bad. Also, the problem assumes you can't change the Fruit enum.
Let's assume you know which fruit are in Fruit but don't want to rely on their integer representations. You can generate a number which is guaranteed to be bigger than the maximum value (ignoring possible but unlikely problems with INT_MAX):
enum MyFruit { Apple = (Orange|Mango|Banana)+1, Pear };
The bitwise or'ing may result in some gaps - in this case it doesn't, but if you repeat the process with another extended enum based on MyFruit the integer representation would be 0,1,2,3,4,8,... That shouldn't matter with true enumerated types (and you wouldn't be using this technique with bitmasks disguised as enums).
HTH.
Elv
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
 | Cool  maihem | 14:16 30 Oct '06 |
|
|
 |
|
|
 |