0

In a c++ class Defs I have a public enum like enum services {1st_drink = 101, 2nd_drink = 105, seafood_outside = 200,......}. I have about 200 keywords and each one is with a value. Now in another class sometest I need to get specific keyword value. The keyword is like a variable in my code and I can only know the keyword after some processing. So what I want to do is like:

.......
std::string keyword = string1 + "_" + string2;
unsigned int a = Defs::keyword;
.......

But now when I try to do this, I get error "error C2039: 'keyword': is not a member of 'Defs'" and "error C2440: '=': cannot convert from 'std::string' to 'const unsigned int '".

Now I try to fix the problem. I noticed that somebody asked a similar question before Get enum value by name but I don't want to use that solution since I have too many keywords. Any good idea to do this?

Community
  • 1
  • 1
gladys0313
  • 2,569
  • 6
  • 27
  • 51
  • Just to make the question clear for myself: You want the String from the number? – Sossenbinder Dec 18 '15 at 13:50
  • The 2nd part of the accepted answer you link to defines the enum values only once. Since you need to define the enum anyway that should work for you. – Kevin Dec 18 '15 at 13:51
  • No, I want the string from other conditions and calculations and I want the number from the string – gladys0313 Dec 18 '15 at 13:56
  • If your compiler supports 200+ arguments per macro (i.e. you are not using Visual C++), take a look at this answer: http://stackoverflow.com/a/31362042/2482998. The library version is [here](https://github.com/aantron/better-enums) (disclaimer: I wrote it). – antron Dec 18 '15 at 16:30

3 Answers3

4

What you need is a std::map<std::string, unsigned int>:

#include <map>
#include <string>

const std::map<std::string, unsigned int> services = {
    { "1st_drink", 101 },
    { "2nd_drink", 200 },
    // ...
};

const std::string keyword = string1 + "_" + string2;
const unsigned int a = services[keyword];
YSC
  • 38,212
  • 9
  • 96
  • 149
  • 2
    I think this is the only correct answer. OP might not want it this way but I don't think there is a similarily easy way next to this. I know C# could do this (so Java is very likely to be able to do it as well), but C++ can't. – Sossenbinder Dec 18 '15 at 13:51
  • @YSC thank you very much for your answer! I want to put the map `services` into the class `Defs` and do `services[keyword]` in class `sometest`. I include the class `Defs` but when I do so, I get error `error C2065: 'services':undeclared identifier`. Why this would happen' – gladys0313 Dec 18 '15 at 14:51
  • @gladys0313 This is another question, why don't you open a new one and post your class' code? we'll be happy to help you. – YSC Dec 18 '15 at 14:52
1

If you happen to be using the Qt application framework you can take advantage of Qt's meta object compiler which stores information about a class for use during runtime. The MOC can recognise enumerations.

class Defs: public QObject {
    Q_OBJECT
    enum  Services { firstDrink = 1, secondDrink = 2, ... };
    Q_ENUMS(Services)
};

When moc runs on the above and sees Q_OBJECT it adds a staticMetaObject member of type QMetaObject. This QMetaObject instance has an indexOfEnumerator and an enumerator member functions that make it possible to access the QMetaEnum representing the Defs::Services enum.

The code to access the QMetaEnum member looks something like the following:

const QMetaObject &mo = Defuns::staticMetaObject;
int index = mo.indexOfEnumerator("Services");
QMetaEnum metaEnum = mo.enumerator(index);

We can then use the QMetaEnum object as follows:

// first, let's convert from an enum value to a string
Services s = Defs::firstDrink;
QByteArray str = metaEnum.valueToKey(s);
// str now contains "firstDrink"

// second, let's convert from a string to an enum value:
int value = metaEnum.keyToValue("firstDrink");
// value now contains the integer 1

I hope this helps.

rfcoder89
  • 184
  • 10
0

As a reusable generic solution, you can create enum parser template like this when declaring enums:

template <typename T>
class EnumParser
{
    map <string, T> enumMap;
    T defaultValue;
public:
    EnumParser(){};

    T Parse(const string &value)
    { 
        map <string, T>::const_iterator iValue = enumMap.find(value);
        if (iValue  == enumMap.end())
            return defaultValue;
        return iValue->second;
    }       
};

You can use instantiate it like this:

enum class Services
{
    DeFault,
    First_drink = 101,
    Second_drink = 105,
    seafood_outside = 200,
};

EnumParser<Services>::EnumParser() : defaultValue(Services::DeFault)
{
    enumMap["DeFault"] = Services::DeFault;
    enumMap["First_drink"] = Services::First_drink;
    enumMap["Second_drink"] = Services::Second_drink;
    enumMap["seafood_outside"] = Services::seafood_outside;
}

Then use it like this:

int value= (int)EnumParser<Services>().Parse(keyword);
Baldrick
  • 11,712
  • 2
  • 31
  • 35