Yuya
Yuya

Reputation: 13

How to output enum elements using cout?

I am trying to output a element of enum I declared but for example, when I input push_ups, it outputs a number like 8621623 instead of showing push_ups. I have no idea why. I am Japanese so I am sorry if my English is broken. Thank you so much.

#include <iostream>
#include <string>
using namespace std;

enum exercise { push_ups, sit_ups, squats, walking, radio_calisthenics };

istream& operator>>(istream& is, exercise& i)
{
    int tmp;
    if (is >> tmp)
        i = static_cast<exercise>(tmp);
    return is;
}

int main()
{
    exercise a;
    cin >> a;
    cout << a << endl;
}

Upvotes: 1

Views: 454

Answers (4)

Ruks
Ruks

Reputation: 3956

I am trying to output a element of enum I declared but for example, when I input push_ups, it outputs a number like 8621623 instead of showing push_ups.

In the operator>> overload, std::cin accepts integers so push_ups isn't an integer, so std::cin will fail and and the line i = static_cast<exercise>(tmp); will be skipped making a uninitialized which when printed can cause Undefined Behavior to occur.

If you want to map strings to respective enum values, you could do that by mapping each string to the corresponding enum values manually using a hashmap (In C++, that container is called std::unordered_map):

#include <unordered_map>

// ...

const unordered_map<string, exercise> exercise_map {
    { "push_ups", push_ups },
    { "sit_ups", sit_ups },
    { "squats", squats },
    { "walking", walking },
    { "radio_calisthenics", radio_calisthenics }
};

istream& operator>>(istream& is, exercise& i) {
    std::string tmp;
    if (is >> tmp) {
        auto const it = exercise_map.find(tmp);
        if (it != exercise_map.end())
            i = it->second;
    }
    return is;
}

Now, to print out the corresponding string value from the enum, we have to do the reverse, i.e., find the key in the hashmap using the value:

ostream& operator<<(ostream& os, exercise& i) {
    auto const it = std::find_if(exercise_map.begin(), exercise_map.end(),
        [i](std::pair<std::string, exercise> const& e) {
            return e.second == i;
        }
    );
    if (it != exercise_map.end())
        os << it->first;
    return os;
}

This is how the full code should look like:

#include <unordered_map>
#include <algorithm>
#include <utility>
#include <iostream>
#include <string>

using namespace std;

enum exercise { push_ups, sit_ups, squats, walking, radio_calisthenics };

const std::unordered_map<std::string, exercise> exercise_map {
    { "push_ups", push_ups },
    { "sit_ups", sit_ups },
    { "squats", squats },
    { "walking", walking },
    { "radio_calisthenics", radio_calisthenics }
};

istream& operator>>(istream& is, exercise& i) {
    std::string tmp;
    if (is >> tmp) {
        auto const it = exercise_map.find(tmp);
        if (it != exercise_map.end())
            i = it->second;
    }
    return is;
}

ostream& operator<<(ostream& os, exercise& i) {
    auto const it = std::find_if(exercise_map.begin(), exercise_map.end(),
        [i](std::pair<std::string, exercise> const& e) {
            return e.second == i;
        }
    );
    if (it != exercise_map.end())
        os << it->first;
    return os;
}

int main() {
    exercise a;
    cin >> a;
    cout << a << endl;
}

Upvotes: 1

Aziuth
Aziuth

Reputation: 3902

You seem to have problems with understanding enums.
I think you entered the string value "push_ups" and assumed the program would understand it to refer to a value of your enum.
An enum is just a placeholder for an integer type, mostly used to increase the readability of your program.

See an enum to be a better way to express something like

// chess_piece = 1 : pawn
// chess_piece = 2 : rook
// chess_piece = 3 : bishop
...
int chess_piece;

as

enum ChessPiece { Pawn, Rook, Bishop, ...};
ChessPiece chess_piece;

In the upper variant, it is clear that the names pawn, rook etc are comment only. The enum isn't different in that regard. It is clearly more readable to write if(chess_piece == Pawn), but the word "Pawn" is only a part of the code in the programming language, not in the compiled program. It does not exist as string value.

What you can do is to add something like

exercise exercise_from_string(const std::string& input)
{
     if(input == "push_ups") return push_ups;
     ...

Or the same with a switch. I'd also advise that you have a value "unknown" in case a function like that finds no known term.

Edit: One of the other guys updated his answer while I wrote this, his code using a std::map is better than mine.
Hope my answer helps you understanding the core issue, though.

Upvotes: 0

this line here:

i = static_cast<exercise>(tmp);

will work perfectly when tmp is holding an int value between 0 and 4,

but in your case tmp = push_ups is breaking the cast operation and your ref i is wrongly initialized

Upvotes: 0

user7860670
user7860670

Reputation: 37549

push_ups is not a valid integer value that you are trying to read at is >> tmp so a remains uninitialized. If you want to input names then you'll need to read string and then manually convert it to corresponding enum value. Same for output. Without a properly overloaded operator << a will be treated as an integer.

Upvotes: 1

Related Questions