Stupid C++ Tricks #2: Better Enums

So much for the new year’s resolution to write some sort of an update every week. That went out the window pretty quickly. Especially now that I’ve taken over the Inner Product column for Game Developer Magazine and that’s taking away some of my writing time (check out the May issue for my first column!).

It turns out that Charles’ old article Stupid C++ Tricks: Adventures in Assert is one of our most viewed entries, even after all this time. So I figured I’d follow it up with a really, really simple C++ trick. It’s almost trivial, really, but I’ve totally fallen in love with it. At first, when Charles introduced me to it, I was kind of lukewarm. But now I’m finding myself going through refactoring rampages in the code changing things to be this way. Intrigued? Read on.
C++ enums are great in practice, but they’re a bit lacking once you really start flexing them. Don’t get me wrong, they’re definitely a step up from #defines, but you can’t help but wish for more. Type safety is only so-so (enums get promoted to ints silently), you can’t forward declare them so you’re forced to have extra includes, you can’t split up a declaration of enums across multiple files (the compiler would have to do more work, but it would be awesome to be able to create uids for different classes in their own header files instead of a global one). But the most grating of design decisions is their scoping rules.

Take for example a set of enums describing the game flow:

enum GameFlowType
{
    Run,
    Exit,
    Restart,
    Restore,
};

Now functions can return a GameFlowType to indicate that the main loop should do. So far so good.

Except that, look at how it’s used:

GameFlowType DoMainLoop(bool render)
{
    //...
    if (someCondition)
        return Exit;
    return Run;
}

Ouch! We’ve managed to pollute the global namespace with totally generic keywords such as Run and Exit. What if I want to have an enum that controls what action an AI is going to take:

enum AIAction
{
    Enter,
    Exit,
    Stop,
    Walk,
    Run,
};

That won’t even compile because the symbols conflict with the GameFlowType ones. Even worse, it will compile just fine if they don’t happen to be included in the same compilation unit. So you might be fine all along until you write some AI code that has control over the game flow. Oops!

OK, so you can prefix all your enums like this:

enum GameFlowType
{
    GameFlowTypeRun,
    GameFlowTypeExit,
    GameFlowTypeRestart,
    GameFlowTypeRestore,
};

and

enum AIAction{
    AIActionEnter,
    AIActionExit,
    AIActionStop,
    AIActionWalk,
    AIActionRun,
};

That will technically fix the problem, but it’s a bit ugly, and we’re still polluting the global namespace (what if I wanted to have a class called AIActionEnter?).

Another potential solution is to score the enum inside the class that is using them. So we could have:

class AIAction{
public:
    enum AIActionType
    {
        Enter,
        Exit,
        Stop,
        Walk,
        Run,
    };
};

Much cleaner, do doubt, but also with its share of problems: First of all, it forces you to associate a set of enums with a class, which is not something you always want to do. But the biggest problem is that as soon as you have more than one set of enums per class, they all get promoted to the same scope:

class AIAction
{
public:
    enum AIActionBehavior
    {
        Enter,
        Exit,
        Stop,
        Walk,
        Run,
    };

    enum AIActionGroup
    {
        None,
        Self,
        Team,
        All,
    };
};

When we see one of those enums in code referred to as AIAction::Self, we really have no idea that it’s referring to that group the action is applied to, as opposed to AIAction::Enter, which is the type of action.

Our solution? Move all enums into a descriptive namespace.

namespace GameFlowType
{
    enum Enum
    {
        Invalid,
        Run,
        Exit,
        Restart,
        Restore,
    };
}

When you have a variable of that enum type, it’s listed as GameFlowType::Enum type, which clearly indicates what it does. And its values are referred to as GameFLowType::Exit. You get the ideal scoping, you’re not tied to any particular class, things are explicit but not verbose, you’re no polluting the global namespace, and it’s doesn’t affect the runtime or compilation times.

Sometimes the best solutions are the simplest ones.

  • http://games.greggman.com greggman

    I do this. I know some of you won’t like it but the pragmatic ones will.

    #undef GAMEFLOWTYPE_OP
    #define GAMEFLOWTYPES \
    GAMEFLOWTYPE_OP(Invalid)
    GAMEFLOWTYPE_OP(Run)
    GAMEFLOWTYPE_OP(Exit)
    GAMEFLOWTYPE_OP(Restart)
    GAMEFLOWTYPE_OP(Restore)

    That allows me to do things like.

    #undef GAMEFLOWTYPE_OP
    #define GAMEFLOWTYPE_OP(name) name,

    namespace GameFlowType { enum Enum {
    GAMEFLOWTYPES
    }; }

    const char* GetGameFlowTypeName(GameFlowType::Enum type) {
    #undef GAMEFLOWTYPE_OP
    #define GAMEFLOWTYPE_OP(name) #name,
    static const char* names[] = {
    GAMEFLOWTYPES,
    };
    return names[type];
    }

    Etc. Things never get out of sync and I don’t need some extra tools added to my build that will cause other people pain when they attempt to integrate. It’s 100% standard C/C++.

    More examples:

    // predeclare functions to handle each game flow type
    #undef GAMEFLOWTYPE_OP
    #define GAMEFLOWTYPE_OP(name) void #name ## __handler(GameState& gameState);

    GAMEFLOWTYPES;

    // Make a function dispatacher based on the game flow type
    typedef void (*GameFlowFuncPtr)(GameState& gameState);

    void CallGameFlowFunction(GameFlowType::Enum type, GameState& gameState) {
    #undef GAMEFLOWTYPE_OP
    #define GAMEFLOWTYPE_OP(name) #name ## __handler,
    static GameFlowFuncPtr funcTable[] = {
    GAMEFLOWTYEPS
    };
    funcTable[type](gameState);
    }

    All that’s left is to define the actual functions. If I want to add a new one it’s a 1 line change.

  • http://www.gamesfromwithin.com Noel

    Ah yes, we call those fruit lists around here. I kind of hate them, but I accept them (and use them) to do wat you suggested there and generate strings and enums. Anything more than that, and it usually becomes a horrible nightmare of un-debuggable code.

  • http://games.greggman.com greggman

    They are really only useful for low-level small snippets of code that should only have to be debuggged once and then left alone (like the function dispatcher above). They certainly are not useful for anything much bigger. But, being hard to debug, since they should only have to be debugged once or twice, is a not so expensive a price to pay vs the odds of causing an error by not using them (ie, things getting out of sync) and by the productivity benefits they supply. In the example above, without them, 4 places in the code would have to edited for each thing added vs with them only 1 place has to be edited. For dispatch code like that above that’s a big win.

  • JoeProgrammer

    what about doing this?

    typedef enum _eSampleEnum {
    E_SE_ONE,
    E_SE_TWO,

    E_SE_MAX
    } eSampleEnum;

    Don’t you get better type checking this way and doesn’t it minimize naming collisions?

    I got in the habit of typedef’ing all my enums. But under the covers of CPP, I’m not clear on all the advantages or disadvantages of doing so.

  • http://gamecreator.myopenid.com/ SasQ

    But now you have `GameFlowType::Enum` for the type name instead of simply `GameFlowType`. And yes, I hate it too. It’d be better if C++ use the same scoping rules for enums as for other namespaces & classes, and simply use `GameFlowType::Exit` by default, without the need for use of some tricks.

  • Florian Mätschke

    In case of using this for an AI, i wouldn’t do that.

    You should decide to use a clear event state based system for the ai, so you don’t mess it up at a later stage.

  • JW

    John Lakos recommended nesting enums inside structs in his 1996 book.

  • http://www.powerof2games.com JW

    JW, you’re absolutely correct. I should correct Noel slightly and say that we were motivated by the Enum usage _syntax_ in C#, which drove me to look around. I read Lakos years and years ago, which is no doubt where I picked up the trick, even though I don’t consciously remember it. :)

    It’s amazing how much of the spirit of that book holds true now, even though many of the techniques are geared towards much earlier compilers.

    -chas

  • charles

    i have no idea what just happened there in that comment. I’m not impersonating this JW person, i promise. I swear I typed ‘chas’ in the “your name” field :-/ Sorry buddy.

    -c

  • thiagodp

    A useful trick:

    #define STR_ME( X ) ( # X )
    
    namespace GameFlowType
    {
        enum Enum
        {
    	Invalid,
    	Run,
    	Exit,
    	Restart,
    	Restore
        };
    
        const char* ToStr[] = {
          STR_ME( Invalid ),
          STR_ME( Run ),
          STR_ME( Exit ),
          STR_ME( Restart ),
          STR_ME( Restore )
          };
    };
    

    So, you can use this way:

    cout < < GameFlowType::ToStr[ GameFlowType::Invalid ] << endl;

    One more thing:
    Most of enum problems will be solved in C++0x with enum class. See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2347.pdf

  • Rory

    I use this technique too. Often for tools I need to be able to convert a string to an enum so it’s nice to be able to put those kinds of functions in the enum namespace rather than the global namespace.

    e.g.

    namespace GameFlowType
    {
        enum Enum
        {
            Invalid,
            Run,
            Exit,
            Restart,
            Restore,
        };
    
        Enum FromString(const char* name);
    }
    
  • http://brett-hall.blogspot.com/ BrettHall

    When I returned from the Java world a few years ago I started putting my enums in their own struct since the Java way of scoping is one of the few things that I think Java does better.

  • noel

    Yeah, this approach is clearly inspired by C#, which we use at Pow2 quite a bit for tools.

  • thothonegan

    I use a combination of the above : namespacing along with putting the type within the enum.

    namespace GameFlow
    {
      enum FlowType
      {
        FLOWTYPE_RUN,
        FLOWTYPE_START
      }
    }
    

    That way it doesn’t pollute the global namespace, and still allows multiple objects to have the same name. Tis a bit more typing, but whatever :P.

  • Jordan

    Glad to see a new entry. I was excited back when you announced your new years resolution, as a lot of your articles are quite interesting to read. I had actually just checked back here for the explicit purpose of saying something harmlessly snarky regarding your frequency of posting :)

    Anyway, nice post. Although I occasionally run into enum collisions at work, I never had time to really sit down and think of a “better way.”

  • kmapsrule

    I got so sick of dealing with C++, mainly with having to maintains a separate array for the string representations
    of the enum’s that I built an enum class.
    This is my plain vanilla C++ version of this, also have one of these that uses QT’s map and QStringList so that a comboBox or list box
    can be easily populated with the enum strings.

    Basically I define all my enums in an XML file like thus:

    
    		<enumType name="AnExample"&>
    	               <enum pos="-2">EXAMPLE_1</enum>
    	               <enum pos="1">EXAMPLE_1</enum>
    	               <enum pos="2">EXAMPLE_2</enum>
    	               <enum pos="3">EXAMPLE_3</enum>
                           <default>EXAMPLE_1</default>
    		</enumType>
    

    and I auto generate all the enum classes, this works really well for GUI apps, I haven't profiled this to see if
    there is too much of an overhead for more performance critical code.

    class AnExample_API AnExample
    {

    public:
    enum AnExampleEnum
    {
    EXAMPLE_1 = -2,
    EXAMPLE_1 = 1,
    EXAMPLE_2 = 2,
    EXAMPLE_3 = 3,

    INVALID_ENUM
    };
    AnExample();
    AnExample(int &value);
    AnExample(std::string &value);
    AnExample(char * value);
    AnExample(AnExampleEnum &1value);

    virtual ~AnExample();
    std::string toString();
    char * toCharArray();
    int toInt();
    AnExampleEnum toEnum();
    AnExampleEnum defaultVal();

    AnExample& operator=(AnExample::AnExampleEnum & value);
    AnExample& operator=(char * value);
    AnExample& operator=(std::string &value);
    bool operator==(AnExample value);


    private:

    AnExampleEnum m_Enum; ///<
    static bool mapInitialized;
    static std::map<AnExample::AnExampleEnum,std::string> m_Map; ///< The Fast lookup map for finding the image from the position
    static const int m_MapSize;
    static AnExample::AnExampleEnum findEnum(std::string &value);
    static void init();

    }; //AnExample

  • http://msinilo.pl yarpen

    Good luck with using that on consoles.

  • DrewBoo

    I use the same namespace technique. And I anxiously await the strongly typed enums in C++0x.

  • http://www.hootwag.com Dan G

    I would forward declare enums in VC 7.1. I never considered it before it’s but probably a microsoft extension.

    cheers
    DanG

  • http://programmerjoe.com Joe Ludwig

    The trouble with wrapping enums in a class is that it prevents you from forward declaring the enum. One of the other comments seems to imply that forward declarations for enums isn’t a standard C++ thing, which may make this moot, but I forward declare enums all the time to avoid including the enum’s own header file in every header.

    You can do this:
    namespace Foo { enum Bar; }

    You can’t do this:
    class Foo;
    enum Foo::Bar;

    Or this:
    struct Foo;
    enum Foo::Bar;

  • chas

    That’s interesting, I hadn’t considered it before.  It’s a little odd that you can instantiate your enum-struct, though.  I know you can make a private and unimplemented default constructor to revoke instantiation, but that’s a lot of legwork.  I guess it’s not a huge deal either way, you have an instantiable struct vs an open namespace.  We do respect Lakos whenever possible/relevant, so that brings a lot of street cred :).

    Anyway, Thanks for the insight!  I definitely wouldn’t have considered putting it in a struct.

  • BrianL

    I prefer using structs:

    struct GameFlowType
    {
    enum Enum
    {
    Invalid
    , Run
    , Exit
    , Restart
    , Restore
    };
    };

    The problem with namespaces is that they can be reopened (ie new contents added) in different files. Structs insure that the associated code is grouped within a single physical module. Other than the ‘inability to reopen’, this can be used almost identical to your namespace case. I prefer putting static functions visible cross module in a struct for the game reason.

    I picked this up from a Lakos presentation, though I don’t have it on it on hand.

  • DrewBoo

    That is a Microsoft-specific extension.

    Forward-declaring enums is not currently part of C++ because the actual definition of the enum can affect the enum’s size.