First off you need a mapping between enumeration values (in the program) and strings (in the file). It could just be the integer values expressed in decimal. Or e.g. the enumeration member names.
Names have the advantage that they're readable, especially when they're English, but the disadvantage that they require some additional machinery and, hence, development and maintenance work.
One way to define name string is to simply use an array of names, and for a modicum of sanity-check check that the size of that array equals the number of enumeration values:
#include <iostream>
#include <stdexcept> // std::exception, std::runtime_error
#include <stdlib.h> // EXIT_FAILURE, EXIT_SUCCESS
#include <string>
namespace my {
using std::ios;
using std::istream;
using std::string;
struct Weekday
{
enum Enum{ monday, tuesday, wednesday, thursday, friday, saturday, sunday };
static Enum constexpr first_value = monday;
static Enum constexpr last_value = sunday;
static int constexpr n_values = last_value + 1;
static
auto identifiers() -> char const* const (&)[n_values]
{
static char const* const the_identifiers[] =
{ "monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday" };
return the_identifiers;
}
};
Alternatively the preprocessor or general code generation can be used to generate corresponding enumeration value names and name strings, but IMHO that's more work than it's worth.
With the names available all you have to do to support >>
, is to define a suitable operator>>
, e.g. as follows:
istream& operator>>( istream& stream, Weekday::Enum& value )
{
string name;
if( stream >> name )
{
for( int i = 0; i < Weekday::n_values; ++i )
{
if( name == Weekday::identifiers()[i] )
{
value = Weekday::Enum( i );
return stream;
}
}
stream.setstate( ios::failbit );
}
return stream;
}
} // namespace my
Here I chose to use the same failure reporting as with standard input operations, namely via the stream state. Alternatively one could use exceptions. However, as I see it exceptions belong at a higher level, e.g. in the calling code:
using namespace std;
void cpp_main()
{
my::Weekday::Enum day;
cout << "Day? ";
cin >> day;
if( !cin ) { throw runtime_error( "Not a valid weekday name." ); }
cout << "You chose a " << my::Weekday::identifiers()[day] << "." << endl;
}
auto main() -> int
{
try
{
cpp_main(); return EXIT_SUCCESS;
}
catch( exception const& x )
{
cerr << "!" << x.what() << endl;
}
return EXIT_FAILURE;
}
That's about it, except for optimization. The string lookup can possibly be optimized by placing the strings in a std::map
or std::unordered_map
. But with stream i/o the main time-consumer is the i/o, so any optimization of the lookup will probably not yield any practically significant overall performance improvement.
Note: to compile this with Visual C++ 12.0, which doesn't support constexpr
, use /D constexpr=const /D _ALLOW_KEYWORD_MACROS
.