The Artima Developer Community
Sponsored Link

Weblogs Forum
Typesafe Enums in C++

7 replies on 1 page. Most recent reply: Jun 21, 2005 11:31 AM by Christopher Diggins

Welcome Guest
  Sign In

Go back to the topic listing  Back to Topic List Click to reply to this topic  Reply to this Topic Click to search messages in this forum  Search Forum Click for a threaded view of the topic  Threaded View   
Previous Topic   Next Topic
Flat View: This topic has 7 replies on 1 page
Christopher Diggins

Posts: 1215
Nickname: cdiggins
Registered: Feb, 2004

Typesafe Enums in C++ (View in Weblogs)
Posted: Jun 17, 2005 2:20 PM
Reply to this message Reply
Summary
Recently Maksym Motornyy proposed on the Boost mailing list, a macro library for typesafe enums. This is not the first time someone has suggested macros for enhancing macros. I wish to demonstrate that you don't have to resort to macros for typesafe enums.
Advertisement
C++ enums are well-known to be not-typesafe. They look like type declarations, but are in fact simply integer constants. Their existance in C++ is vestigial, and they should not be used. On the other hand true enumerated types are themselves very useful. Here is a technique for implementing true enumerations in C++:
template<typename T, int N>
struct enumerator {
  const static int value = N;
  typedef enumerator<T, N + 1> next;
};

struct weekday
{
  // self typedef
  typedef weekday self;

  // 'ctors
  template<int N>
  weekday(const enumerator<self, N>& x) : value(N) { }
  weekday(const self& x) : value(x.value) { }

  // enumerations
  typedef enumerator<self, 0> mon;
  typedef mon::next tue;
  typedef tue::next wed;
  typedef wed::next thu;
  typedef thu::next fri;
  typedef fri::next sat;
  typedef sat::next sun;
  typedef sun::next end_day;

  // operators
  self& operator++(int) { ++value; return *this; }
  self& operator--(int) { --value; return *this; }
  self operator++() { self tmp = *this; ++value; return tmp; }
  self operator--() { self tmp = *this; --value; return tmp; }
  friend bool operator==(const weekday& x, const weekday& y) { return x.value == y.value; }
  friend bool operator!=(const weekday& x, const weekday& y) { return x.value != y.value; }
  operator int() const { return value; }

  // static functions
  static mon begin() { return mon(); }
  static end_day end() { return end_day(); }

  // fields
  int value;
};

void weekday_reaction(const weekday& x) {
  switch (x)
  {
    case (weekday::mon::value) :
      std::cout << "doh! monday" << std::endl;
      break;
    case (weekday::tue::value) :
      std::cout << "doh! tuesday" << std::endl;
      break;
    case (weekday::wed::value) :
      std::cout << "doh! hump-day" << std::endl;
      break;
    case (weekday::thu::value) :
      std::cout << "woohoo! day before friday" << std::endl;
      break;
    case (weekday::fri::value) :
      std::cout << "woohoo! friday" << std::endl;
      break;
    case (weekday::sat::value) :
      std::cout << "woohoo! saturday" << std::endl;
      break;
    case (weekday::sun::value) :
      std::cout << "doh! church!" << std::endl;
      break;
    default:
      std::cout << "whatcha talkin' 'bout willis?" << std::endl;
      break;
  }
}


int main()
{
  weekday_reaction(weekday::sat());
  // weekday_reaction(42); does not compile
  weekday_reaction(weekday::end());

  for (weekday wd = weekday::begin(); wd != weekday::end(); wd++) {
    weekday_reaction(wd);
  }

  system("pause");
  return 0;
}


Balbir Singh

Posts: 3
Nickname: bsingh
Registered: Jun, 2005

Re: Typesafe Enums in C++ Posted: Jun 18, 2005 2:35 PM
Reply to this message Reply
Hi, Christopher,

I think I like the implementation, but I am not sure about the space required to implement them. From your example they seemed a bit expensive in terms of space requirements.

Christopher Diggins

Posts: 1215
Nickname: cdiggins
Registered: Feb, 2004

Re: Typesafe Enums in C++ Posted: Jun 19, 2005 12:07 PM
Reply to this message Reply
> Hi, Christopher,
>
> I think I like the implementation, but I am not sure about
> the space required to implement them. From your example
> they seemed a bit expensive in terms of space requirements.

Hi Balbir,

Thanks. When you space required for implementation are you referring to lines of code, the executable size, or the size of the object itself? I know that the object size required is only the size of a single int.

Balbir Singh

Posts: 3
Nickname: bsingh
Registered: Jun, 2005

Re: Typesafe Enums in C++ Posted: Jun 20, 2005 11:48 PM
Reply to this message Reply
Thanks for answering.

Good point, I checked the sizeof (weekday) it comes to 4 on my machine.

The other factors in size like you mentioned are

1. Size of the executable (is high)
2. Size of the symbol table due to 7 added typedefs, but that is what provides the type safety.

Each type has a value associated with it and the size should be an additional 7x4 bytes.

Those were my concerns

Keith Ray

Posts: 658
Nickname: keithray
Registered: May, 2003

Re: Typesafe Enums in C++ Posted: Jun 21, 2005 10:24 AM
Reply to this message Reply
Another way to get some of the same benefits is a "range-checking" template class. The enum constants are still integers, but the variables you create are of a template class that checks the values coming into the initialization-constructor, copy-constructor, and assignment operators, and coming out of the type-cast operator.

If a bad value is passed in, or the variable is somehow changed without going through a constructor or assignment operator, you can be notified through an assertion or exception.

enum FooType { fee, fi, fo, fum, foo };

CheckedEnum< FooType, fee, foo > aCheckedFooEnumVar;

aCheckedFooEnumVar = (FooType) 12; // assertion or exception thrown

I leave the template class code to the reader.

Gregg Wonderly

Posts: 317
Nickname: greggwon
Registered: Apr, 2003

Re: Typesafe Enums in C++ Posted: Jun 21, 2005 10:36 AM
Reply to this message Reply
In Java, prior to JDK1.5 one way of doing this was something like:
public class MyEnumerator extends Number {
    private static int inc = 0;
    private final  int val;
    public MyEnumerator() { val = inc++; }
    public boolean equals( Object o ) {
    	// Support non-type safe equality check
    	if( o instanceof Number == false )
    		return false;
    	return( ((Number)o).intValue() == val );
    }
    public int hashCode() {
    	// Provide hashing equality for maps
    	return val;
    }
    public double doubleValue() { return (double)val; }
    public float floatValue() { return (float)val; }
    public long longValue() { return val; }
    public int intValue() { return val; }
}


This provides the numeric useage of the enumeration value into contexts where the enumeration value is more useful than the type safety. There are of course some simpler, object based implementation as well.

Can you tell more about why you used a more struct-like approach instead of an object for the enumeration? The use of the enumeration typedef is an interesting practice. It just seems like there are a number of things that you included in your implementation that might not be needed in all cases. Please talk more about whether you think this is a general solution, or one targeted at computationally intensive uses. What about a use that is more about enumerating the instances and less about using a numeric value.

My Java example is targeted at the computational solution. An instance oriented solution would not include the subclassing of Number.

Christopher Diggins

Posts: 1215
Nickname: cdiggins
Registered: Feb, 2004

Re: Typesafe Enums in C++ Posted: Jun 21, 2005 11:08 AM
Reply to this message Reply
> Another way to get some of the same benefits is a
> "range-checking" template class. The enum constants are
> still integers, but the variables you create are of a
> template class that checks the values coming into the
> initialization-constructor, copy-constructor, and
> assignment operators, and coming out of the type-cast
> operator.
>
> If a bad value is passed in, or the variable is somehow
> changed without going through a constructor or assignment
> operator, you can be notified through an assertion or
> exception.
>
> enum FooType { fee, fi, fo, fum, foo };
>
> CheckedEnum< FooType, fee, foo > aCheckedFooEnumVar;
>
> aCheckedFooEnumVar = (FooType) 12; // assertion or
> exception thrown
>
> I leave the template class code to the reader.

This is a good idea, and I actually have a class which could be used in such a way, which I just posted to my blog at http://www.artima.com/weblogs/viewpost.jsp?thread=115788

It is policy based so the behaviour (e.g. assertion or exception) can be defined by the user.

Christopher Diggins

Posts: 1215
Nickname: cdiggins
Registered: Feb, 2004

Re: Typesafe Enums in C++ Posted: Jun 21, 2005 11:31 AM
Reply to this message Reply
> In Java, prior to JDK1.5 one way of doing this was
> something like:

[snip]

> This provides the numeric useage of the enumeration value
> into contexts where the enumeration value is more useful
> than the type safety. There are of course some simpler,
> object based implementation as well.
>
> Can you tell more about why you used a more struct-like
> approach instead of an object for the enumeration?

Efficiency, programmers often use enumerations because they expect int-like performance.

> The
> use of the enumeration typedef is an interesting practice.
> It just seems like there are a number of things that you
> u included in your implementation that might not be needed
> in all cases.

Yes you are correct.

> Please talk more about whether you think
> this is a general solution, or one targeted at
> computationally intensive uses.

I think this is more targetd towards computationally intensive uses. The one requirement that I wanted to satisfy which significantly affected the design was the usage in switch statments. This is harder than it looks in C++ because the case statement must contain a constant value, and what C++ considers a constant value is somewhat limited and arbitrarilly constrained.

> What about a use that is
> more about enumerating the instances and less about using
> a numeric value.
>
> My Java example is targeted at the computational solution.
> An instance oriented solution would not include the
> e subclassing of Number.

This is a good idea as well. We would have to (AFAIK) give up the ability to use in case statements in C++, but the code would become more elegant.

class enumerator {
  public:
    enumerator(const int n) : value(n) { }
    const int value;
    operator int() { return value; } 
};
 
class weekday : public enumerator {
  public:
    weekday(const weekday& x) : enumerator(x.value) { }
    static const weekday monday;
    static const weekday friday;
  private:
    weekday(const int n) : enumerator(n) { }
};
 
const weekday weekday::monday(0);
const weekday weekday::friday(4);


A weekday can only be constructed from another weekday in this context. This also supports automatic casting to an integer, it simply prevents one from silently casting from an int to a weekday.

Flat View: This topic has 7 replies on 1 page
Topic: We Need A New Information-Sharing Model for the Internet Previous Topic   Next Topic Topic: Debuggers are a wasteful Timesink


Sponsored Links



Google
  Web Artima.com   

Copyright © 1996-2014 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use - Advertise with Us