3

Possible Duplicate:
Input from stream to enum type

I have several classes with different enums as class members and I want to read the classes from a stream.

The following code shows an exemplary class:

  enum enSide{
    eLeft,
    eRight
  };

  enum enType{
    eConUndefined,
    eConRoom    
  };

  class MyClass{
    public:
      friend std::istream& operator>>(std::istream& in, MyClass& val) {
        in >> val.mSide >> val.mType >> val.mTargetId;        
        return in;      
      }

      MyClass(){}

    private:
      enSide mSide;
      enType mType;
      int mTargetId; 
  };

Unfortunately this does not work since direct reading into an enum Value is not possible (no template for >>).

Thus I created a helper class:

template<class ENUM>
class ScanTo{
  public:
    friend std::istream& operator>>(std::istream& in, ScanTo<ENUM>& eval) {
      unsigned int val;
      in >> val;
      eval.mrEnum = static_cast<ENUM>(val);
      return in;      
    }

    ScanTo(ENUM& eRef):mrEnum(eRef){}

  private:
    ENUM& mrEnum;    
};

Now I can write the code for reading MyClass as follows:

friend std::istream& operator>>(std::istream& in, MyClass& val) {
  ScanTo<enSide> scanside(val.mSide);
  ScanTo<enType> scantype(val.mType);
  in >> scanside >> scantype >> val.mTargetId;        
  return in;      
}

This is already not far from what I wanted but still needs two indirections over the helper class, which cannot be written as temporarys:

friend std::istream& operator>>(std::istream& in, MyClass& val) {
 in >>  ScanTo<enSide>(val.mSide)>> ScanTo<enType>(val.mType) >> val.mTargetId;        
 return in;      
}

does not compile (gcc 4.43), because a non const reference to a temporary is forbidden as pointed out in the comments.

So here comes the question:

Can this be done easier without resorting to some temporaries and templates as done above?

Community
  • 1
  • 1
Martin
  • 4,346
  • 2
  • 25
  • 54
  • Have you seen this? http://stackoverflow.com/questions/5633784/input-from-stream-to-enum-type – kol Jun 24 '12 at 13:01
  • 1
    The condensed version compiles (and works) perfectly with my VS2010. What compiler are you using? – gha.st Jun 24 '12 at 13:17
  • 1
    @dionadar I think VS allows binding temporaries (`ScanTo(val.mSide)`) to a reference to non-const (`myClass& val`) by means of an extension. It's non-standard, though. – jrok Jun 24 '12 at 13:21
  • @kol: Yes but this means a separate function for every enum. I have loads ;-) – Martin Jun 24 '12 at 13:24
  • @dionadar: I use gcc version 4.4.3 and as jrok points out it seems more severe about binding temporaries to non const references – Martin Jun 24 '12 at 13:26
  • you could trick the compiler by binding to const reference and use a `const_cast<>()`, though this sounds like a bad solution. – Walter Jun 24 '12 at 14:22
  • For those who closed the question: IMHO this is not a duplicate. The referenced question adresses the reading of one singular enum and no generic read function for all enum types – Martin Jun 25 '12 at 20:45
  • Posted an answer on the non-closed question: http://stackoverflow.com/a/21360718/1424877 @Martin had it just about right; I simply copied his code, fixed the bug that prevented it from compiling, and added a helper function for convenience. – Quuxplusone Jan 26 '14 at 07:07
  • @Quuxplusone: nice solution! – Martin Aug 15 '16 at 23:35

2 Answers2

1

I think you may write a helper function template:

template <class T>
std::istream& operator >>(std::istream& is, T& t)
{
    int i;
    is >> i;
    t = (T)i;
    return is;
}

which makes

in >> val.mSide >> val.mType >> val.mTargetId;

possible.

alantsui
  • 11
  • 1
  • 1
    This kills you since it can be triggered for any arbitrary type. If you forget to define operator >> for your custom type but use it anyway.Your code will compile but typecast an int to any custom type – Martin Jun 24 '12 at 17:05
0

The best option for you is to define your data members as int, and use type safe accessors to set and retrieve them.

class MyClass{
  public:
    friend std::istream& operator>>(std::istream& in, MyClass& val) {
      in >> val.mSide >> val.mType >> val.mTargetId;        
      return in;      
    }

    MyClass(){}

    enSide side () const { return static_cast<enSide>(mSide); }
    void side (enSide v) { mSide = v; }

    enType type () const { return static_cast<enType>(mType); }
    void type (enType v) { mType = v; }

    int targetId () const { return mTargetId; }
    void targetId (int v) { mTargetId = v; }

  private:
    int mSide;
    int mType;
    int mTargetId; 
};

This avoids the temporaries as you desired.

jxh
  • 64,506
  • 7
  • 96
  • 165