2

I have an enum class like this (I am planning to add more options to it later):

enum class ViSequencePointType {
   JumpToValue = 0,
   RampToValue = 1
};

Then I have a configuration text file which each line supposed to represents one the enum values. Something like this:

1
0
255
A
WTF

I need to parse this file and create a vector of that enum class...so I do something like:

    bool conversionResult = false;
    int colThree = line.toInt(&conversionResult);
    if(!conversionResult) { 
         //failed to convert to integer
    } else {
    ViSequencePointType pt = static_cast<ViSequencePointType>(colThree);
    switch(pt) {
        case ViSequencePointType::JumpToValue:
            break;
        case ViSequencePointType::RampToValue:
            break;
        default:
            break;
    }

for that default case the compiler says

Default label in switch which covers all enumeration values

which I believe it means if there is any invalid entry in the text file exists, I can not find it out!

So how can I approach this problem without letting any invalid enumeration slip through during runtime?

DEKKER
  • 877
  • 6
  • 19
  • Throw an error on your switch statement's `default` case? For example, for line '255' in your file. – vahancho Feb 19 '19 at 12:35
  • 4
    No, the compiler warning means "you added a default but there are no valid enum values left that could lead to the default". In other words, you could only get to the default by doing something invalid. Whether casting 255 to a `ViSequencePointType` is UB is a [different question](https://stackoverflow.com/questions/18195312/what-happens-if-you-static-cast-invalid-value-to-enum-class). – Max Langhof Feb 19 '19 at 12:42
  • You could add a first and last value to the enum (`{SeqPointFirst=0,JumpToValue=0,RampToValue,SeqPointLast=RampToValue}`) and then simply check the range of the integer before casting to the enum. Of course this only works if your enum values are sequential. – Karsten Koop Feb 19 '19 at 13:10
  • This is an irritation from the compiler. It should be "only" a warning, and the code /should/ work correctly. But the c++11 spec clearly allows for values to be stored in enum types that are not the declared values. It only requires that such values are in the range of the declared values, or the declared underlying type. There is even a provision for cleanly assigning such values into an enum class variable, from c++17. – Gem Taylor Feb 19 '19 at 17:36

1 Answers1

2

In order to cover invalid/nonsensical enum values, a common practice is to

  • rely on the fact subsequent enum values are implicitly assigned the value of the previous enum value + 1
  • add an "Invalid" enum value at the lowest value in the enum (implicitly 0, or you can assign it a low value such as -1)
  • add a "Max" enum value at the highest value in the enum

Here's an example:

enum class ViSequencePointType 
{
    Invalid = -1,

    JumpToValue,            // is implicitly assigned enum value 0 (-1 + 1 == 0)
    RampToValue,            // is implicitly 1 (JumpToValue + 1)
    CrawlToValue,           // etc...
    HopToValue,    
    // add new values here       

    Max                     // Max must be the last value in the enum
};

Now when you parse your input value you can check the integral value is greater than Invalid and less than Max, and if so, you know it's a valid enum value

ViSequencePointType parse(const std::string& value)
{
    bool converted = false;
    int val = line.toInt(&converted);
    if(!converted) 
    { 
         // do additional conversion failure handling here if necessary
         return ViSequencePointType::Invalid;
    } 
    if (val <= static_cast<int>(ViSequencePointType::Invalid) ||
        val >= static_cast<int>(ViSequencePointType::Max)
    {
         // do additional out of bounds handling here if necessary
         return ViSequencePointType::Invalid;
    }
    return static_cast<ViSequencePointType>(val);
}

Now you know the output from parse is a valid enum value, with the output for unknown/invalid values denoted by enum value Invalid.

Steve Lorimer
  • 27,059
  • 17
  • 118
  • 213