Enumerations with Strings

Since getting the strings of enum variables is a bit tedious when done using a *switch*, the boost preprocessor can be used to create enums with an *automatic* *toString* function.

Since getting the strings of enum variables is a bit tedious when done using a switch, the boost preprocessor can be used to create enums with an automatic toString function:

#include <boost/preprocessor.hpp>
#include <boost/preprocessor/wstringize.hpp>

namespace input
{
	// use boost to define enums with strings

	// this first macro is used internally by the second one
#define ENUM_TO_STRING(r, data, elem)    \
    case elem : return BOOST_PP_WSTRINGIZE(elem);

	// this second macro first generates the enumeration and then generates a ToString function
	// that takes an object of that type and returns the enumerator name as a string
	// obviously this implementation requires that the enumerators map to unique values)
#define ENUM_WITH_STRING(name, enumerators)                \
    enum name {                                                               \
        BOOST_PP_SEQ_ENUM(enumerators)                                        \
    };                                                                        \
                                                                              \
    inline const wchar_t* getGameCommandAsString(name v)				      \
    {                                                                         \
        switch (v)                                                            \
        {                                                                     \
            BOOST_PP_SEQ_FOR_EACH(                                            \
                ENUM_TO_STRING,                                               \
                name,                                                         \
                enumerators                                                   \
            )                                                                 \
            default: return "[Unknown " BOOST_PP_WSTRINGIZE(name) "]";        \
        }                                                                     \
    }

	// define the game commands
	ENUM_WITH_STRING(GameCommands,	                    (Select)\
														(Back)\
														(ShowFPS)\
														(MoveLeft)\
														(MoveRight)\
														(MoveUp)\
														(MoveDown)\
														(Shift)\
														(Preview)\
														(nGameCommands))

	// define application events
	ENUM_WITH_STRING(Events,	                (StartApplication)\
												(PauseApplication)\
												(ResumeApplication)\
												(QuitApplication)\
												(SwitchFullscreen)\
												(WindowChanged)\
												(ChangeResolution)\
                                                (nApplicationEvents))
}



This might look weird and it obviously is a bit more work than just defining an enum, but the ease of use easily beats the added implementation time.

The first macro ENUM_TO_STRING is internally used by the second one. The second macro ENUM_WITH_STRING first generates the enumeration, then generates a toString function called getGameCommandAsString that takes a GameCommand as input and returns the enumerator name as a string, using the first macro to stringify enumeration elements into wide chars. Note that this implementation obviously requires the enumerators to map to unique values.

Defining the actual enumerations is easy enough, as you can see, but the real power comes from the ease of use:

First notice that I like to add a count at the end of each enum, for example nGameCommands to be able to easily loop through the enum:

// create action text layouts
	for (unsigned int i = input::GameCommands::Select; i < input::GameCommands::nGameCommands; i++)
		if (!addTextToActionTextLayoutList((input::GameCommands)i).wasSuccessful())
			return std::runtime_error("Critical error: Unable to create header action text layouts!");



And retrieving the string values of an enum is now as easy as it gets:

util::Expected<void> KeyMapMenuState::addTextToActionTextLayoutList(input::GameCommands gameCommand)
{
	std::wstring text = input::getGameCommandAsString(gameCommand);
				
	Microsoft::WRL::ComPtr<IDWriteTextLayout4> textLayout;
	util::Expected<void> result = d2d->createTextLayoutFromWString(&text, textFormat.Get(), (float)dxApp->getCurrentWidth(), 100, textLayout);
	if (!result.isValid())
		return result;
	actionTextLayouts.push_back(textLayout);

	// return success
	return { };
}



Just have fun with the new enums! Thanks boost!

Comments