• entries
    707
  • comments
    1173
  • views
    434122

Enums

Sign in to follow this  

173 views

I'm sure I've shown my enum system before, but in-case not, here it is again (no, I don't actually have this enum, it's just an easy example to make.)

namespace Booleans
{
enum Value
{
Unknown = -1,
False = 0,
True,
};
string ToString(Value);
Value Parse(string);
} typedef Booleans::Value Boolean;



Well, I read over the article posted here a little while ago on "Stringizing Enums" (clicky.) And I really didn't like the system that it used. However, I really liked the results.

So, I took my usual course of action and tried to come up with a system that I liked more, but to my dismay, it wasn't possible. So, I returned to the above article, downloaded the source, and looked through it several times. Then, I proceeded to write my own version. My first run through produced very different, hacky, and (most importantly) non-working code. So, I tried again...and failed again lol. Eventually, I ended up writing code that looks pretty much exactly like his (really just boils down to different names and such.) I had to modify the system slightly due to my enums residing in namespaces.

For convenience, I added a macro that produces inline ToString/Parse functions that call my Enum::ToString() and Enum::Parse() functions.

Then I went ahead and coded up an Enumerator class that works with the original code. I did this because I have used it on several occasions (especially when designing user interface layout editors and developing a certain user interface control.) However, to do this I had to switch from a map container to a vector container (to ensure they come out in the same order they're registered.)

Anyway, the resulting system isn't as 'pretty' as I'd like it, but that only affects coding the actual enum; it looks decent when using it. Here's my working example.
PropertyTypes enum:

namespace PropertyTypes
{
enum Value
{
Null = 0,
Integer,
FloatingPoint,
Double,
Character,
String,
Boolean,
};
} typedef PropertyTypes::Value PropertyType;

dbeals_StartEnum(PropertyTypes::Value)
{
dbeals_EnumValue(PropertyTypes, Null);
dbeals_EnumValue(PropertyTypes, Integer);
dbeals_EnumValue(PropertyTypes, FloatingPoint);
dbeals_EnumValue(PropertyTypes, Double);
dbeals_EnumValue(PropertyTypes, Character);
dbeals_EnumValue(PropertyTypes, String);
dbeals_EnumValue(PropertyTypes, Boolean);
}
dbeals_EndEnum;
dbeals_InlineEnumToString(PropertyTypes); // This adds ToString() to PropertyTypes
dbeals_InlineEnumParse(PropertyTypes); // This adds Parse() to PropertyTypes
dbeals_TypedefEnumerator(PropertyTypes); // this typedefs Enumerator and Enumerator::Node in PropertyTypes
// so, in the end PropertyTypes ends up with ToString(), Parse(), Enumerator, and EnumeratorNode.



And my test:

int main()
{
dbeals::PropertyTypes::Enumerator Enumerator;
while(Enumerator.Next())
{
dbeals::PropertyTypes::EnumeratorNode Node = *Enumerator;
std::cout<<"Value => "<" | Name => "< }
return 0;
}



You could also use it like so:

int main()
{
dbeals::Enumerator Enumerator;
while(Enumerator.Next())
{
dbeals::Enumerator::Node Node = *Enumerator;
std::cout<<"Value => "<" | Name => "< }
return 0;
}



The output looks like so:

Value => 0 | Name => Null
Value => 1 | Name => Integer
Value => 2 | Name => FloatingPoint
Value => 3 | Name => Double
Value => 4 | Name => Character
Value => 5 | Name => String
Value => 6 | Name => Boolean


I've decided for now to not allow multiple strings (where two strings have the same value.) I don't really see any reason to support that at the moment.

If anybody is interested in the code, here she be:

#ifndef dbeals_Code_Base_Enum_h
#define dbeals_Code_Base_Enum_h

/*
I'd like to note here that a lot of credit goes to Francis Xavier for his article "Stringizing C++ Enums" on gamedev.net (https://www.gamedev.net/reference/snippets/features/cppstringizing/).
I learned how to do the original system from his code and then modified the code to support what I wanted and to resemble my coding style.

Also, note that my code is designed to work with how I write enums; Xavier's code will work out of the box for any normal enums AFAIK (it'll even work with a system like mine with very little modification.)
*/


#include
#include
#include

#include

#define dbeals_StartEnum(EnumTypeM) template <> struct Enum : public BaseEnum<Enum, EnumTypeM> { static void RegisterValues()
#define dbeals_EnumValue(ContainerM, ValueNameM) RegisterValue(ContainerM::ValueNameM, #ValueNameM)
#define dbeals_EndEnum };

#define dbeals_InlineEnumToString(ContainerM) namespace ContainerM { inline const std::string ToString(ContainerM::Value Value) { return dbeals::Enum::ToString(Value); } }
#define dbeals_InlineEnumParse(ContainerM) namespace ContainerM { inline ContainerM::Value Parse(const std::string &String) { return dbeals::Enum::Parse(String); } }
#define dbeals_TypedefEnumerator(ContainerM) namespace ContainerM { typedef dbeals::Enumerator Enumerator; typedef dbeals::Enumerator::Node EnumeratorNode; }

namespace dbeals
{
template
class Enumerator;

template
class BaseEnum
{
friend class Enumerator;
protected:
typedef std::vector > ContainerType;

private:
BaseEnum(const BaseEnum &);
const BaseEnum &operator =(const BaseEnum &);

static ContainerType &GetContainer()
{
static ContainerType Container;
static bool FirstRetrieval = true;
if(FirstRetrieval)
{
FirstRetrieval = false;
DerivedTypeT::RegisterValues();
}
return Container;
}

protected:
explicit BaseEnum();
~BaseEnum();

static void RegisterValue(EnumTypeT Value, const std::string &String)
{
for(ContainerType::iterator Iterator = GetContainer().begin(); Iterator != GetContainer().end(); ++Iterator)
{
if(Iterator->first == Value)
// You aren't allowed to have duplicate values.
__debugbreak();

if(Iterator->second == String)
// You aren't allowed to have duplicate strings.
__debugbreak();
}
GetContainer().push_back(std::make_pair(Value, String));
}
public:
static const unsigned int Unknown = INT_MIN;

static const std::string &ToString(EnumTypeT Value)
{
for(ContainerType::iterator Iterator = GetContainer().begin(); Iterator != GetContainer().end(); ++Iterator)
{
if(Iterator->first == Value)
return Iterator->second;
}
static const std::string Unknown = "Unknown";
return Unknown;
}

static EnumTypeT Parse(const std::string &String)
{
for(ContainerType::iterator Iterator = GetContainer().begin(); Iterator != GetContainer().end(); ++Iterator)
{
if(Iterator->second == String)
return Iterator->first;
}

return (EnumTypeT)BaseEnum::Unknown;
}
};

template <class EnumTypeT>
struct Enum : public BaseEnum<Enum, EnumTypeT>
{
static void RegisterValues();
};

template <class EnumTypeT>
class Enumerator
{
public:
struct Node
{
const EnumTypeT Value;
const std::string Name;

Node(std::pair MapNode) : Value(MapNode.first), Name(MapNode.second) { }
Node(const Node &Node) : Value(Node.Value), Name(Node.Name) { }
};

private:
typedef typename Enum EnumerationType;
typedef typename EnumerationType::ContainerType EnumerationContainerType;

typename EnumerationContainerType::const_iterator Current;
bool Move;
public:
Enumerator()
{
Current = EnumerationType::GetContainer().begin();
Move = false;
}

bool Next()
{
if(!Move)
Move = true;
else
++Current;
return Current != EnumerationType::GetContainer().end();
}

void Reset()
{
Current = EnumerationType::GetContainer().begin();
Move = false;
}

Node operator *() const
{
return Node(*Current);
}
};
}

/*
I'd like to note here that a lot of credit goes to Francis Xavier for his article "Stringizing C++ Enums" on gamedev.net (https://www.gamedev.net/reference/snippets/features/cppstringizing/).
I learned how to do the original system from his code and then modified the code to support what I wanted and to resemble my coding style.

Also, note that my code is designed to work with how I write enums; Xavier's code will work out of the box for any normal enums AFAIK (it'll even work with a system like mine with very little modification.)
*/


#endif


Note that this is the first draft; I'll probably change it a lot as I go and after I've used it more than a few tests. It hasn't been optimized, probably isn't completely const-correct, and could probably use some stuff that I missed.

Any comments/suggestions are welcome, as always.

Also, a big thanks to Francis Xavier for his article and code!
Sign in to follow this  


4 Comments


Recommended Comments

Eww?

Seems like a lot of work to get something that could be done just as easily with a map and a few methods. Not spectacular or elegant, but if you wanted spectacular or elegant you'd use any number of languages that provide this sort of thing out of the box (or at least reflection to make it suck less). Or at least start by not iterating a container every method call...

Share this comment


Link to comment
Quote:
Original post by Telastyn
Eww?

Seems like a lot of work to get something that could be done just as easily with a map and a few methods. Not spectacular or elegant, but if you wanted spectacular or elegant you'd use any number of languages that provide this sort of thing out of the box (or at least reflection to make it suck less). Or at least start by not iterating a container every method call...


Lol, that's pretty much the response I was expecting.

It does pretty much boil down to a map and a few methods (well, a vector and a few methods.)

I know that I could switch to another language to gain this feature (specifically C#), but I'm not going to switch languages to gain one simple feature. I've chose this method instead of a 'simpler' method (like my old system which every type and string was hardcoded) because it is easy to use, does exactly what I need it to, makes it easier when implementing enums, and makes it much easier to add/remove values.

I'm not really worried about iterating a container every method call because the methods should only be used in 3 situations: saving, loading, or debugging.

Share this comment


Link to comment
Quote:
Original post by Telastyn
Eww?

Seems like a lot of work to get something that could be done just as easily with a map and a few methods. Not spectacular or elegant, but if you wanted spectacular or elegant you'd use any number of languages that provide this sort of thing out of the box (or at least reflection to make it suck less). Or at least start by not iterating a container every method call...


Lol, that's pretty much the response I was expecting.

It does pretty much boil down to a map and a few methods (well, a vector and a few methods.)

I know that I could switch to another language to gain this feature (specifically C#), but I'm not going to switch languages to gain one simple feature. I've chose this method instead of a 'simpler' method (like my old system which every type and string was hardcoded) because it is easy to use, does exactly what I need it to, makes it easier when implementing enums, and makes it much easier to add/remove values.

I'm not really worried about iterating a container every method call because the methods should only be used in 3 situations: saving, loading, or debugging.

Share this comment


Link to comment

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now