2

For the assignment I'm currently working on, I have defined an enum class for differing class semesters.

enum class Semester {
        FALL,
        SPRING,
        SUMMER
};

In my overloaded << operator, I am to print out the lowercase string equivalent of Semester type. I've done so by creating a map with Semester keys and std::string pairs as such:

std::map<Semester, std::string> to_string
{
        {Semester::FALL, "fall"},
        {Semester::SPRING, "spring"},
        {Semester::SUMMER, "summer"}
};

std::stream& operator <<(std::stream& ost, enum Semester semester)
{
        return ost << ::to_string[semester];
}

Already unsure if the previous solution was optimal, I moved onto the next task which was defining Semester load_semester(std::istream& ist). This function receives input from the user as std::string (figuring I'd use std::getline()) and converts it to Semester type. I imagine I could use a map similar to before albeit reversed, but I was curious what a 'proper' way to approach this problem would be.

EDIT: I appreciate the feedback! I will be implementing functions as @casey suggested.

I am implementing enum classes for both semesters and sections (i.e. MATH, READING, SCIENCE, etc.) I will possibly be required to implement more in the future. Could I implement these functions in such a way that I could reuse them?

typoplasm
  • 23
  • 4
  • I don't think you should be worried about "optimal" code when learning. More often than not, you'll get so hung up on irrelevant performance concerns that you'll make no progress on actual functionality or end up spiraling into weird problems that you struggle to debug. A map is fine, but a more common approach is to just use an array. You don't gain much here by the way you're currently using this map, except that if your enum is a value outside the range, your map will implicitly _add_ new empty strings, whereas an unchecked array access would result in undefined behavior. – paddy Apr 13 '21 at 04:06
  • 4
    *I was curious what a 'proper' way to approach this problem would be*. Like many things in programming, it is a matter of opinion. – R Sahu Apr 13 '21 at 04:23

1 Answers1

1

Don't bother with a std::map, just use a function:

//As an aside, avoid uppercase enum names
enum class Semester {
    Fall,
    Spring,
    Summer
};

std::string to_string(const Semester& s) {
    switch(s) {
        case Semester::Fall: return "fall";
        case Semester::Spring: return "spring";
        case Semester::Summer: return "summer";
        default: throw std::exception("to_string: Semester enum values have changed.");
    }
}

Semester from_string(std::string str) {
    std::transform(std::begin(str), std::end(str), std::begin(str), [](unsigned char c)->unsigned char { return std::tolower(c); });
    if(str == "fall") {
        return Semester::Fall;
    } else if(str == "spring") {
        return Semester::Spring;
    } else if(str == "summer") {
        return Semester::Summer;
    } else {
        throw std::exception("from_string: Semester enum values have changed.");
    }
}

std::ostream& operator<<(std::ostream& ost, const Semester& semester) {
    ost << to_string(semester);
    return ost;
}
Casey
  • 10,297
  • 11
  • 59
  • 88
  • Why is a function better than a map? – John Kugelman Apr 13 '21 at 04:23
  • One might argue that a map uses unnecessary memory. However, here there is a maintenance problem with keeping two sets of strings. I personally use a single table for enum-to-string lookups, and I search that same table for string-to-enum lookup. Again, a matter of design requirements, project motivation, _number of enums_, and so on... It really is an opinion-based question which is why I refrained from answering it. – paddy Apr 13 '21 at 04:29
  • @JohnKugelman See paddy's comment. :) All good points. I've done the above for enums that contain *hundreds* of values. – Casey Apr 13 '21 at 04:35
  • 1
    :-) I have one issue with this and it is that throws instead of setting the stream(s) in a failed state when things fail. – Ted Lyngmo Apr 13 '21 at 04:47
  • @TedLyngmo Failing loudly when the enum values change makes it very clear that "Hey, don't forget to change these functions, too!" – Casey Apr 13 '21 at 04:59
  • @Casey I guess. It could also have other reasons, like trying to extract from a corrupt file or someone casting `4` to a `Semester` and then trying to output that. It's just that it's more common to set the stream in a failed state. If the stream is set to throw on failures, it will, so you could just set the failbit i.m.o. – Ted Lyngmo Apr 13 '21 at 05:16