0

Using C++, I was curious about how I should go about reading in a value from a file that has to go into an enumerated type.

For example, my file format looks like this:

firstname lastname strength weapon

If I were to rewrite it as datatypes, it's

string string int enum

Now I realize I'm using

ifstream din;
din >> strVal;
//that's for strings, for ints I use:
din >> intVal;

How can I read in those values from the file that have to go into my enumerated type? I know they have to be previously defined, I did that. But I can't cast a strVal to my enumerated type, so I'm not sure how to do it.

Thank you in advance to everybody that tries to help :)

  • See: [How to convert an enum type variable to a string?](http://stackoverflow.com/questions/5093460/how-to-convert-an-enum-type-variable-to-a-string) - you can also go the opposite way. – Vector Oct 05 '14 at 23:40
  • @Vector I did not know that was possible to go the other way, I will most definitely be reading that right now! Thank you! – SiggyxLeGiiT Oct 05 '14 at 23:43
  • When you say weapon in your file format, I assume you mean the integer equivalent, and not the enum's name as a string, right? – Kevin Oct 06 '14 at 02:43
  • @Kevin It would indeed have to be the string name and not the value because a few of the values have to be the same, so that route is not possible. – SiggyxLeGiiT Oct 06 '14 at 03:07

3 Answers3

0

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.

Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
  • I'm interested in reading what you have to say when you get back! My enums are indeed strings, not integers, just saying. :) – SiggyxLeGiiT Oct 05 '14 at 23:53
0

One way to solve this is by using the fact that values within an enum mapping are automatically assigned integer values, beginning with 0. You can initialize your values with any number you'd like and thus create numeric codes for your logic checks later on.

Consider the following enum mapping:

enum Weapon {KNIFE = 1, CHAINSAW = 2, HANDGUN = 3, ASSULT_RIFLE = 3, ROCKET_LAUNCHER = 4, ... };

After you get your input values from the file, you'll be able to use these assigned integer values in if-else and switch conditions. From here, using a simple switch to assign the correct value to your enum variable is easy.

You can try something like this:

int startingWeapon;
Weapon currentWeapon;
din >> strValue >> str2Value >> intValue >> startingWeapon;
switch (startingWeapon)
{
    case 1:
        currentWeapon = KNIFE;
        cout << "Weapon: Knife.\n";
        break;
    case 2:
        currentWeapon = CHAINSAW;
        cout << "Weapon: Chainsaw.\n";
        break;
.
.
.
    default:
        currentWeapon = KNIFE;
        cout << "Weapon: Knife.\n";
}

That said, it is IMPOSSIBLE to initialize a variable of your enum type by using the integer value. This will result in a compiler error:

Weapon currentWeapon = 1;

This is the correct way:

Weapon currentWeapon = KNIFE;
Gil Dekel
  • 355
  • 4
  • 15
  • Note that, in C++11, you should prefer enum classes for their type safety. However, IIRC, this means you'll have to write operator<< and operator>> for them yourselves, but this is fairly trivial. – Kevin Oct 06 '14 at 02:45
0

You will have to convert from a string or an int to your enum type. One way to encapsulate this is by overloading operator<<(). You may also want to overload operator>>() for output.

Code-Apprentice
  • 81,660
  • 23
  • 145
  • 268