Click here to Skip to main content
Licence 
First Posted 29 Oct 2006
Views 52,428
Bookmarked 32 times

Inheriting a C++ enum type

By Lidzhade Fhulu | 29 Oct 2006
Allow a C++ enum type to be extended unambigously.

1
2 votes, 9.5%
2
3 votes, 14.3%
3
3 votes, 14.3%
4
13 votes, 61.9%
5
4.46/5 - 21 votes
2 removed
μ 4.31, σa 1.85 [?]

The Problem

C++ does not have a facility to allow one enum type to be extended by inheritance as it does for classes and structures. Sometimes it is important to have this functionality. Suppose you had:

// in Fruit.h
enum Fruit { Orange, Mango, Banana };
// in eat.h
#include "Fruit.h"
void eat(Fruit fruit);

If you wanted to add another fruit type, you could extend Fruit as follows:

enum Fruit { Orange, Mango, Banana, Apple, Pear };

You could also have another function that handles the extra fruit:

void consume(Fruit fruit);

There are two problems with this approach:

  1. Fruit.h may be a library file that you don't necessarily want to change.
  2. void eat(Fruit) is implemented in some library that you can't change and as a result it might not handle 'Apple' properly but will still compile without errors. Your consume(Fruit) function may know about 'Apple' and handle it properly but library users, even though it is not your intention, may still call eat(Apple) with undefined behaviour.

In summary, the results of calls to eat() and consume() are as follows:

  eat( Orange );      // compiles and behaves as expected 
  consume( Orange );  // compiles and behaves as expected 
  eat( Apple );       // compiles with UNDEFINED BEHAVIOUR
  consume( Apple );   // compiles and behaves as expected

The Solution

InheritEnum solves this problem by allowing you to leave the first enum declaration as is and add another enum declaration with new enum types.

Following on from our example, to handle new fruits as well as the first set of fruits, we will then have:

// in -- MyFruit.h --

#include "Fruit.h" 
#include "InheritEnum.h" 

enum NewFruits { Apple, Pear }; 
typedef InheritEnum< NewFruit, Fruit > MyFruit;

Now our consume() declaration becomes:

void consume(MyFruit myfruit); 

Now, our call summary looks as follows:

eat( Orange );       // compiles and behaves as expected
consume( Orange );   // compiles and behaves as expected
eat( Apple );        // does not compile as eat() does not handle NewFruit
consume( Apple );    // compiles and behaves as expected

The Code

// -- InheritEnum.h

template <typename EnumT, typename BaseEnumT>
class InheritEnum
{
public:
  InheritEnum() {}
  InheritEnum(EnumT e)
    : enum_(e)
  {}

  InheritEnum(BaseEnumT e)
    : baseEnum_(e)
  {}

  explicit InheritEnum( int val )
    : enum_(static_cast<EnumT>(val))
  {}

  operator EnumT() const { return enum_; }
private:
  // Note - the value is declared as a union mainly for as a debugging aid. If 
  // the union is undesired and you have other methods of debugging, change it
  // to either of EnumT and do a cast for the constructor that accepts BaseEnumT.
  union
  { 
    EnumT enum_;
    BaseEnumT baseEnum_;
  };
};

Thank you.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

Lidzhade Fhulu

Web Developer

South Africa South Africa

Member


Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board. (secure sign-in)
 
Search this forum  
 FAQ
    Noise  Layout  Per page   
  Refresh
Generalinheriting enums PinmemberAtticus-Atropos22:34 16 Mar '09  
GeneralRe: inheriting enums PinmemberHugo González Castro6:28 2 Apr '09  
GeneralWorkaround to extend an enum in a derived class PinmemberTheo Buys1:11 6 Jan '09  
GeneralA different approach PinmemberHugo González Castro18:46 23 Dec '08  
GeneralLicence Pinmemberwarlock6x32:43 15 Dec '06  
GeneralRe: Licence PinmemberLidzhade Fhulu21:44 17 Dec '06  
GeneralThere could be simpler way of doing it. [modified] PinmemberMr.Prakash21:42 6 Nov '06  
GeneralRe: There could be simpler way of doing it. PinmemberIvan Kolev22:15 6 Nov '06  
GeneralRe: There could be simpler way of doing it. PinmemberMr.Prakash22:23 6 Nov '06  
GeneralRe: There could be simpler way of doing it. PinmemberLidzhade Fhulu0:56 7 Nov '06  
GeneralRe: There could be simpler way of doing it. PinmemberMr.Prakash2:30 7 Nov '06  
GeneralLooks good at first sight but ... PinmemberRoland Pibinger0:00 31 Oct '06  
GeneralRe: Looks good at first sight but ... PinmemberLidzhade Fhulu0:21 31 Oct '06  
GeneralRe: Looks good at first sight but ... PinmemberRoland Pibinger0:51 31 Oct '06  
GeneralRe: Looks good at first sight but ... PinmemberLidzhade Fhulu1:30 31 Oct '06  
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.
 

 

GeneralRe: Looks good at first sight but ... PinmemberRoland Pibinger1:51 31 Oct '06  
GeneralRe: Looks good at first sight but ... PinmemberLidzhade Fhulu2:01 31 Oct '06  
GeneralRe: Looks good at first sight but ... PinmemberElv11:31 5 Feb '07  
GeneralCool Pinmembermaihem14:16 30 Oct '06  
GeneralRe: Cool PinmemberLidzhade Fhulu19:14 30 Oct '06  

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Mobile
Web04 | 2.5.120210.1 | Last Updated 30 Oct 2006
Article Copyright 2006 by Lidzhade Fhulu
Everything else Copyright © CodeProject, 1999-2012
Terms of Use
Layout: fixed | fluid