Disclaimer:
What I’m showing here is what I know. I’ve used it, it’s been helpful to me and I want to share it. I’m not a master programmer. I may not know everything. I’m not trying to outsmart other programmers. I’m writing this so that either other programmers may learn from it, or other programmers may hit me back telling me a better way to do it or give me a better idea so I may learn from them.

When making code that will be used by other programmers in C++, I always find it hard to enforce a type for a certain set of values like days of the week, planets of the solar system, event types, etc. Its built in enum type is just not enough and is prone to error. Let’s say we are making an enum for a fantasy world race types:

// C++ enum
enum Race
{
  HUMAN,
  ELF,
  ORC,
  TREANT,
  ELEMENTAL
};

I don’t like this type of enum for a lot of reasons. One, when used as a parameter, it accepts int as a valid value.

// say this is a method of some class
void setRace(const Race& race);

// some usage
instance->setRace(HUMAN); // ok
instance->setRace(4); // huh? is that an ELEMENTAL? fine...
instance->setRace(5); // kaboom!!!

Two, I won’t be able to tell the type of the enum by any one of its value. I have to jump to the source code and see for myself.

// If this is the first time you've seen the code,
// would you know the enum type of HUMAN?
instance->killAll(HUMAN);

Three, I won’t be able to use the same enum value for different enum types.

enum Race
{
  HUMAN,
  ELF,
  ORC,
  TREANT,
  ELEMENTAL
};

enum Activity
{
  // flags an error ("enum value already used",
  // something along those lines)
  HUMAN,
  DIVINE,
  MECHANICAL
};

My solution is to these problems is to use class enums. Let me show you how to make and use one.

I’m not really sure if it’s called class enums. They are simply immutable class pointers. The pointer value itself acts as the enum value. This is the same mechanism used by Java enums. So basically, I’m just emulating the Java way in C++.

// Race.h
class Race
{
public:
  // the different values for the enum
  static const Race* HUMAN;
  static const Race* ELF;
  static const Race* ORC;
  static const Race* TREANT;
  static const Race* ELEMENTAL;

private:
  Race()
  {
    // should not be instantiated elsewhere
  }

  ~Race()
  {
  }
};

// Race.cpp
#include "Race.h"

const Race* Race::HUMAN = new Race();
const Race* Race::ELF = new Race();
const Race* Race::ORC = new Race();
const Race* Race::TREANT = new Race();
const Race* Race::ELEMENTAL = new Race();

Let’s revisit the problems I showed before.

One, when used as a parameter, it accepts int as a valid value.
Since the enum is now a class, it has a defined type and is not castable to int. Therefore, int values cannot be used as enum values for this style of enum types.

// say this is a method of some class
// note that it's always const *
// for this approach of enums
void setRace(const Race* race);

// some usage
instance->setRace(Race::HUMAN); // ok
instance->setRace(4); // not so fast, compiler won't allow you

Two, I won’t be able to tell the type of the enum by any one of its value. I have to jump to the source code and see for myself.
For this approach of enum type, an enum value will always be prepended by the name of the enum type. So I’d know right away the type of a certain enum value without jumping to the source code.

// let me ask you again
// If this is the first time you've seen the code,
// would you know the enum type of HUMAN?
instance->killAll(Race::HUMAN); // you can see it right away, right?

Three, I won’t be able to use the same enum value for different enum types.
With this one, I can.

// header file
class Activity
{
public:
  static const Activity* HUMAN;
  static const Activity* DIVINE;
  static const Activity* MECHANICAL;

private:
  Activity()
  {
  }

  ~Activity()
  {
  }
};

// implementation file
#include "Activity.h"

const Activity* Activity::HUMAN = new Activity();
const Activity* Activity::DIVINE = new Activity();
const Activity* Activity::MECHANICAL = new Activity();

// some usage
instance->setRace(Race::HUMAN);
instance->setActivity(Activity::HUMAN);

There are many more benefits in using this type of enum like adding variables and operations unique to each enum value. But I’d rather discuss it in another post.

What do you think?

1sdfsdfsdfsdf 2sdfsdfsdfsdf
Advertisements