enums and data input

Greetings. I am trying to get a handle on using enums. In particular I would like to set something up whereby a user could input data that would be used in a constructor call to create an instance of class Book. The code in main is similar to the way I would do it for built-in types, but as is it won't compile.

Anyways I could use a little guidance here. My sample code is stripped down to the bare minimum, I hope there is enough left to indicate what I have in mind.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <iostream>

using namespace std;

enum class Genre
{
    fiction, nonfiction, periodical, biography, children
};

class Book
{
    public:
        string title;
        Genre genre;
};

int main()
{
    cout << "Enter the genre: ";
    Genre g;
    cin >> g;

    return 0;
}
The standard input output library knows about standard types like int and library-defined types like std::string

genre is a user-defined scoped enum; there is no implicit conversion between genre and any integral type. We need to provide user-defined input output facilities for genre

Something like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include <iostream>
#include <string>
#include <algorithm>
#include <cassert>

enum class genre { fiction, nonfiction, periodical, biography, children };

namespace
{
    constexpr const char* genre_names[] { "fiction", "nonfiction", "periodical", "biography", "children" } ;
    constexpr std::size_t genre_count = sizeof(genre_names) / sizeof( genre_names[0] ) ;
    const std::string scope = "genre" ;
    const std::string prefix = scope + "::" ;
}

std::istream& operator>> ( std::istream& stm, genre& g ) // input
{
    std::string str ;
    if( stm >> str )
    {
        for( char& c : str ) c = std::tolower(c) ; // ignore case
        const auto prefix_begin = str.find(prefix) ;
        if( prefix_begin == 0 ) str = str.substr( prefix.size() ) ; // scope prefix is optional for input

        for( std::size_t pos = 0 ; pos < genre_count ; ++pos ) if( str == genre_names[pos] )
        {
            g = genre(pos) ;
            return stm ;
        }
    }

    // attempted input failed
    stm.setstate( stm.rdstate() | std::ios_base::failbit ) ; // set the stream to a failed state
    return stm ;
}

std::ostream& operator<< ( std::ostream& stm, genre g ) // output
{
    const unsigned int ival = unsigned(g) ;
    assert( ival < genre_count ) ;

    if( ival < genre_count ) return stm << prefix << genre_names[ival] ; // valid genre
    else return stm << "genre::invalid_value(" << ival << ')' ; // g may be uninitialised or it has been given a value 
                                                                // via a wrong cast  (we may want to throw here)
}

int main()
{
    genre g{} ;

    while( std::cin >> g ) std::cout << g << "   " ;
    
    g = genre(56) ;
    std::cout << g << '\n' ;
}

http://coliru.stacked-crooked.com/a/c4a71b4233c0d2f4
Thanks. User defined input operator it is then. It'll take a little time to sort this out.
> It'll take a little time to sort this out.

Start with something simple - accept an integer from the user (0 for genre::fiction, 1 for genre::nonfiction etc.)
(Sometime later, if required, refine it to accept strings.)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream> 

enum class genre { fiction, nonfiction, periodical, biography, children };

std::istream& operator>> ( std::istream& stm, genre& g ) // input
{
    int value ;
    if( std::cin >> value )
    {
        switch(value)
        {
            case int(genre::fiction): g = genre::fiction ; break ;
            case int(genre::nonfiction): g = genre::nonfiction ; break ;
            case int(genre::periodical): g = genre::periodical ; break ;
            case int(genre::biography): g = genre::biography ; break ;
            case int(genre::children): g = genre::children ; break ;
            default: stm.setstate( stm.rdstate() | std::ios_base::failbit ) ; // failed
        }
    }

    return stm ;
}
谢谢,that means thank you.
Last edited on
>Start with something simple - accept an integer from the user (0 for >genre::fiction, 1 for genre::nonfiction etc.)
>(Sometime later, if required, refine it to accept strings.)

Thanks this is a little more my speed.
Topic archived. No new replies allowed.