little_mice
little_mice

Reputation: 177

Enum with c++ (Input checking)

I am new in C++ and came up with the idea of writing "a short quiz" to use "enum" and have a clearer understanding of how it works. My idea consists in displaying three options to the user, and the he/she would have to introduce the number that corresponds to the correct option. This depicts the situation:

# include <iostream>

int main(){

    std::cout << "Which one is the capital of Ireland?";
    std::cout << "\n1-Barcelona\n2-Frankfurt\n3-Dublin\n";
    std::cout << "Please introduce the number of the correct option";

    enum question {barcelona=1,frankfurt=2,dublin=3};

    int iAnswer;
    std::cin >> iAnswer;

    question eAnswer= static_cast<question>(iAnswer);

The problem with this code is that after casting the integer introduced by the user (iAnswer), there's no error message/warning when such number does not exist in the "question" data type options. In other words; if the user introduced 100, after casting iAnswer we could see that there's no option related to 100 since the only accepted values are 1,2 and 3. The question would be then... how could I check that the number the user introduces does exist in my enum options? why casting doesn't show any error when in theoretically the casting couldn't be successfully done?

Thanks!!

Upvotes: 0

Views: 3011

Answers (3)

aep
aep

Reputation: 1675

As others have already pointed out, static_cast simply converts an int to Enum, it doesn't verify.

Please find my own solution for simple range checked enum(enums that have contiguous values). It uses scoped enums which are much safer and will work for your case.

#include <iostream>
#include <exception>
#include <type_traits>

template <typename E>
auto safe_cast_enum_in_range(typename std::underlying_type<E>::type val, E min, E max) {
  if (val < static_cast<decltype(val)>(min) || val > static_cast<decltype(val)>(max)) {
    throw std::out_of_range("oor");
  }
  return static_cast<E>(val);
}

int main() {
  enum class E1{k1, k2, k3};
  try {
    auto x = safe_cast_enum_in_range(2, E1::k1, E1::k3); // no exception
    auto y = safe_cast_enum_in_range(4, E1::k1, E1::k3); // exception
  } catch (...) {
    std::cout << "Exception\n";
  }
}

Upvotes: 1

Robert Andrzejuk
Robert Andrzejuk

Reputation: 5222

The question would be then... how could I check that the number the user introduces does exist in my enum options?

You have to make a function to validate and map the integer to an enum:

enum class Question
{
    invalid   = 0,
    barcelona = 1,
    frankfurt = 2,
    dublin    = 3
};

Question map_to_question_enum(int answer)
{
    switch (answer)
    {
        case 1:
            return Question::barcelona;

        case 2:
            return Question::frankfurt;

        case 3:
            return Question::dublin;

        default:
            return Question::invalid;
    };
}

Used like:

int answer;
std::cin >> answer;

Question question_answer = map_to_question_enum(answer);

if (question_answer == Question::invalid)
     ...
else 
     ...

why casting doesn't show any error when in theoretically the casting couldn't be successfully done?

Because static_cast only converts an integer into the enum. By it's definition it does not verify.

Most probably the question during verification would be: what to do in the case of an invalid value?

  • throw an exception? (sometimes exceptions are not allowed)
  • convert to an error value? (which value symbolizes an error?)
  • is the error from the enum list or from the different area?
  • ...

Upvotes: 0

Andr&#233; Caceres
Andr&#233; Caceres

Reputation: 729

Does this help to understand? https://en.cppreference.com/w/cpp/language/enum

Values of integer, floating-point, and enumeration types can be converted by static_cast or explicit cast, to any enumeration type. If the underlying type is not fixed and the source value is out of range, the result is unspecified (until C++17)undefined (since C++17). (The source value, as converted to the enumeration's underlying type if floating-point, is in range if it would fit in the smallest bit field large enough to hold all enumerators of the target enumeration.) Otherwise, the result is the same as the result of implicit conversion to the underlying type.

Note that the value after such conversion may not necessarily equal any of the named enumerators defined for the enumeration.

To solve the problem of checking the user input, you could it with something like this:

    question getEnumAnswerFromInteger(int iAnswer)
    {
       if (iAnswer >= 1 && iAnswer <= 3)
       {
           return static_cast<question>(iAnswer);
       }
       else { /* throw some exception... */ }
    }

Also, consider using enum class instead of just enum. And then using the proper scope do address the values.

Upvotes: 2

Related Questions