1

Consider the following code:

//Note that the enum is nonsequential. I don't have control of
//this enum so I cannot change it to be sequential
enum class Animal {
    DOG = 0,
    CAT = 1,
    LLAMA = 20,
    ALPACA = 21,
};

//This data is received as an int,
//but I want to know what Animal it refers to
int data_0 = 0; 
int data_2 = 2;

My application will receive some data, and I want to know which Animal it refers to.

Animal foo = static_cast<Animal>(data_0); //Great, it's an Animal::DOG.
Animal bar = static_cast<Animal>(data_2); //Whoops!  This will crash.

if (foo == Animal::DOG) { //This is an easy comparison
    //do something
}

Clearly, I need to be doing some validation on the incoming data or I risk a crash.

To me, it seems like the solution is to do explicit validation when casting an enum, but I don't know enough about C++ to know a clean way to do this:

//This is not sufficient to validate the value
if (data_2 > Animal::DOG && data_2 < Animal::ALPACA) {
    Animal bar = static_cast<Animal>(data_2); //Whoops!  This will crash.
}

//This is sufficient to validate the value, but is very ugly
Animal bar;
if (data_2 == static_cast<int>(Animal::DOG)   ||
    data_2 == static_cast<int>(Animal::CAT)   ||
    data_2 == static_cast<int>(Animal::LLAMA) ||
    data_2 == static_cast<int>(Animal::ALPACA)) {
    bar = static_cast<Animal>(data_2); //This does not run because of the checking
}

//bar was not assigned, so it would be NULL or 0
if (bar == Animal::DOG) {
    //oh no, we shouldn't be here!
}

There has to be a better way to do this, so I feel like I'm missing something. How could I designthis be done such that an int can be cast to an Animal, but Animal cannot end up being an invalid value when the casting fails?

JD Reese
  • 113
  • 1
  • 5
  • Thanks for marking the dupe Jack. I had searched but couldn't find another question which satisfied the non-sequential part of mine. Between the answers here and on the marked dupe, I think I have enough info to work through it. – JD Reese Mar 09 '18 at 21:50

2 Answers2

0

If the code to do the validation is not in the path of a performance critical section of your code base, you can use a function that does the necessary validation.

std::pair<bool, Animal> getAnimal(int data)
{
   switch (data)
   {
      case Animal::DOG
        return {true, Animal::DOG};

      ...

      default:
   }

   return {false, ANIMAL::DOG};
}

and use it as:

auto res = getAnimal(data);
if ( res.first )
{
   // Valid data.
   // Use res.second.
}
else
{
   // Deal with invalid data.
}
R Sahu
  • 204,454
  • 14
  • 159
  • 270
0

Use a table that contains both the enum value and the name:

+--------+----------+  
| DOG    | "DOG"    |  
+--------+----------+  
| CAT    | "CAT"    |  
+--------+----------+  
| LLAMA  | "llama"  |  
+--------+----------+  
| ALPACA | "alpaca" |  
+--------+----------+  

Search the table for the ID. If the ID exists you can fetch the associated name. You can also search by name to find the ID.

You could use std::map if you want to associate ID with name or Name with ID.

Thomas Matthews
  • 56,849
  • 17
  • 98
  • 154