0
enum class Fruit { apple, orange, pear };
enum class Color { red, green, orange };

template <typename T> struct Traits;
//I have to return the appropriate value(string) of color and fruit in their respective structs.
//I could do this by switch case method but I specifically wanted to know, how do I access an enum class through index
template<>
struct Traits<Fruit>{
    static string name(int i){
        if(i>-1&&i<3){
            return Fruit::static_cast<Fruit>(i);
        }
        else{
            return "unknown";
        }
    }
};
template<>
struct Traits<Color>{
    static string name(int i){
        if(i>-1&&i<3){
            return Color::static_cast<Color>(i);
        }
        else{
            return "unknown";
        }
    }
};

I want to return the appropriate string present in the respective structs at their respective indices. The static_cast is not working and compiler is giving an error that it can't cast. I wonder if it is possible to access enum class through index at all.

Error:

could not convert ‘(Fruit)i’ from ‘Fruit’ to 
‘std::__cxx11::string {aka std::__cxx11::basic_string<char>}’
    return static_cast<Fruit>(i);
wazz
  • 4,953
  • 5
  • 20
  • 34
  • What does the error say? – Galik Aug 06 '18 at 02:36
  • could not convert ‘(Fruit)i’ from ‘Fruit’ to ‘std::__cxx11::string {aka std::__cxx11::basic_string}’ return static_cast(i); ^~~~~~~~~~~~~~~~~~~~~ – Siddharth Pandey Aug 06 '18 at 02:55
  • I personally recommend unsigned int as parameter, you can spare the check for negative values this way. If anyone passes a negative value, it will be converted (on 2's complement machines as a no-op) to a very large value, far out of range of your enum, and you are still safe... (Of course, you can't use if you explicitly specify negative enum values!) – Aconcagua Aug 06 '18 at 03:56

2 Answers2

1

As error says you cannot directly convert enum class into string. But you can convert int into enum class using static_cast
After converting int into enum you can go ahead with enum to string conversion. There are many ways to do and this so answer has good summary of that

static string name(int i) {
    if (i>-1 && i<3) {
        Fruit f = static_cast<Fruit>(i);
        return name(f);
    }
    else {
        // return default value or throw error
        return "";
    }
}

static string name(Fruit f) {
    //convert f and return string
}
miradham
  • 2,285
  • 16
  • 26
0

You could have an array of fruit names in parallel to your enum to have names:

std::string FruitNames[] = { "apple", "orange", "pear" };

Now you can use this directly to get names from. If you are willing to invest a little into x-macros, you can generate both your array and the enum from with one single macro:

#define FRUITS GEN_FRUIT(apple) GEN_FRUIT(orange) GEN_FRUIT(pear)

#define GEN_FRUIT(X) X,
enum class Fruit { FRUITS };
#undef GEN_FRUIT

#define GEN_FRUIT(X) [Fruit::X] = #X,
std::string const FruitNames[] = { FRUITS };
#undef GEN_FRUIT

Admitted, harder to read, on the other hand, you have just one single location to maintain both enum and array...

You could even support explicit values:

#define FRUITS GEN_FRUIT_V(apple, 1) GEN_FRUIT(orange) GEN_FRUIT_V(pear, 4)

#define GEN_FRUIT(X) X,
#define GEN_FRUIT_V(X, Y) X = (Y),
enum class Fruit { FRUITS };
#undef GEN_FRUIT
#undef GEN_FRUIT_V

#define GEN_FRUIT(X) GEN_FRUIT_V(X, Fruit::X)
#define GEN_FRUIT_V(X, Y) std::make_pair(static_cast<Fruit>(Y), std::string(#X)),
std::unordered_map<Fruit, std::string> const FruitNames({ FRUITS });
// or (tree) map, if you prefer
//std::map<Fruit, std::string> const FruitNames({ FRUITS });
#undef GEN_FRUIT
#undef GEN_FRUIT_V

With upcoming C++20 (or a compiler supporting designated initialisers as extension), you could use an array again:

#define GEN_FRUIT(X) X,
#define GEN_FRUIT_V(X, Y) X = (Y),
enum class Fruit { FRUITS };
#undef GEN_FRUIT
#undef GEN_FRUIT_V

#define GEN_FRUIT(X) [static_cast<int>(Fruit::X)] = #X,
#define GEN_FRUIT_V(X, Y) GEN_FRUIT(X)
std::string const FruitNames[] = { FRUITS };
#undef GEN_FRUIT
#undef GEN_FRUIT_V

Yet future music... Result in above example would be an enum with gaps and the array filled with empty strings at the gaps. If gaps are large (or negative enum values exist), this approach would result in large array as well, so rather not appropriate any more, you might prefer falling back to the map solution again...

Aconcagua
  • 24,880
  • 4
  • 34
  • 59