a concerned citizen a concerned citizen - 1 month ago 6
C++ Question

C++ Combine enum and string into a map-like variable, able to be used with both int and string

Suppose I have an

enum
:

enum Types
{
TYPE_ASCENDING,
TYPE_DESCENDING
};


and I use it to it... anywhere in the code. Say
if(bla < TYPE_ASCENDING)
, or with a
switch/case
. The real
enum
is much larger.

Whatever the results of the checks (or anything else), it needs to be
std::cout
in a prettier way to let the user know what happened. So, continuing the
if()
example, it might be something like this:

if(bla < TYPE_ASCENDING)
std::cout << "Ascending.\n";


All these happen inside a class. My question: is there a way to define some type of variable/STL/anything that would allow storing both
enum
-like and
std::string
-like variables, but would also let me use, separately, both types?

One thought was a
namespace
, but it seems it can't be used inside a class. To exemplify, here's what it would have looked like:

namespace Type
{
enum Types
{
ASCENDING,
DESCENDING
};
std::string s[2] {"Ascending", "Descending"};
};


and it would have been called as
Type::ASCENDING
for the
if()
, and
Type::s[0]
for the string. But, no
namespace
inside a class, so it's not a solution.

Using
std::map
only lets me use
int
as the index, so I can only use this:

std::map<Types, std::string> m {{TYPE_ASCENDING, "Ascending}, {TYPE_DESCENDING, "Descending"}};


as
m[0]
, or
m[TYPE_ASCENDING]
, but I can't call it for it's index to be used inside the
if()
. For that I have to call the
enum
, separately, which means I have both an
enum
and a
map
, two variables. I need one, unified, to avoid chasing variable names all over the code.

If I use a
struct
, I can't access directly
Struct::TYPE_DESENDING
, I need to create an object.

I can use an
enum
and a
std::string
array/vector, but that means that, again, I have to call two variables, separately, and I'd like them to be unified.

Is what I want possible?

Answer

You don't really have that mechanism in native C++. You can write a map / mapper function.

enum class E
{
    ONE,
    TWO
};

std::unordered_map<E,std::string> eStrings { {E::ONE,"ONE"},{E::TWO,"two"}};

While this is C++11 you can do the same for older C++ versions

Then you can use this like

std::cout << eStrings[E::ONE];

The issue here is you have to maintain this manually. So when you add a new value to the enum you have to manually add a new entry to the map.

The same would be true for writing a class or functions to have this behavior. You always have to duplicate the code of enum declaration and the mapping to the string.

A solution here would be to use some tool to generate these.

You can define in some file your enum (this is just some random format and only intended for explaining this. Chose whatever you want in your own defenition file)

E
- ONE
- TWO

And then generate the C++ enum and Map in a header and/or cpp file.

enum class <name>
{
    <foreach:> <value>,
};

std::unordered_map< <name> ,std::string> eStrings 
{ 
    <foreach:> {<name>::<value>,"<value>"},
};

If you don't like having a map this approach is pretty flexible. You can also generate a switch case statement if you like

std::string getString(<name> e)
{
    switch(e)
    {
        <foreach:> case <name>::<value>: return "<value>";
    }
}

The syntax here is no standard for anything just some "pseudocode" to visualize the concept. There are several ways to generate c++ code out there. You can choose whatever you want or write your own program for this.

Note:

This is also just a general concept. You can wrap this functioniality / map etc into another class, make it static etc. for optimizations and not put it in global scope.


If you need something more fancy than just a map to lookup the string you can create a class with this concept or another map which does just the reverse lookup. It's more about the fact that you most likely have to generate the code by an external tool.

Comments