"Reusable" operator overloading for enum?

Discussion in 'C++' started by nick, Dec 27, 2009.

  1. nick

    nick Guest

    I'm sort of rusty at C++, so this may be silly, but here's my
    situation:

    I have an enum like this:


    enum ParserMode
    {
    PM_NONE = 0,
    PM_READY = 1, // ready for next col or row, or EOF
    PM_EMPTY = 2, // empty cell
    PM_CELL = 4, // cell
    PM_QCELL = 8, // quoted cell
    PM_HEAD = 16, // header cell
    };


    And elsewhere I want to write something like:


    void DoStuff(ParserMode m)
    {
    if (m & PM_HEAD)
    ...
    else if (m & PM_QCELL)
    ...
    }
    ...
    DoStuff(PM_CELL | PM_EMPTY | PM_HEAD);


    But this will not work, because the compiler complains about not being
    able to cast between int and ParserMode. So some operator overloading
    clears that up:


    inline ParserMode operator|(ParserMode p1, ParserMode p2)
    {
    return (ParserMode)((int)p1 | (int)p2);
    };
    inline ParserMode operator&(ParserMode p1, ParserMode p2)
    {
    return (ParserMode)((int)p1 & (int)p2);
    };
    inline ParserMode operator~(ParserMode p1)
    {
    return (ParserMode)(~(int)p1);
    };
    inline ParserMode& operator&=(ParserMode& p1, ParserMode p2) {
    p1 = p1 & p2;
    return p1;
    }
    ...


    ....And everything works fine. But, now I need to add another enum, and
    I don't want to copy all of these overloaded operators for this other
    enum. Is there some way I can share one set of operator overloads
    between many enums using templates, macros, or some other trick, or am
    I just going about this wrong altogether?

    Thanks in advance for any help.

    -- Nick
    nick, Dec 27, 2009
    #1
    1. Advertising

  2. Hi,

    nick wrote:
    > inline ParserMode operator|(ParserMode p1, ParserMode p2)
    > {
    > return (ParserMode)((int)p1 | (int)p2);
    > };
    > ...


    you won't come around to use macros in this case.

    E.g.

    #define FLAGSATTRIBUTE(T) \
    inline static T operator|(T l, T r) \
    { return (T)((unsigned)l|r); } \
    inline static T operator&(T l, T r) \
    { return (T)((unsigned)l&r); } \
    inline static T& operator|=(T& l, T r) \
    { return l = (T)((unsigned)l|r); } \
    inline static T& operator&=(T& l, T r) \
    { return l = (T)((unsigned)l&r); } \
    inline static T operator*(bool l, T r) \
    { return (T)(l*(unsigned)r); } \
    inline static T operator*(T l, bool r) \
    { return (T)((unsigned)l*r); } \
    inline static T operator~(T a) \
    { return (T)~(unsigned)a; }

    and possibly

    #define CLASSFLAGSATTRIBUTE(T) \
    inline friend T operator|(T l, T r) \
    { return (T)((unsigned)l|r); } \
    inline friend T operator&(T l, T r) \
    { return (T)((unsigned)l&r); } \
    inline friend T& operator|=(T& l, T r) \
    { return l = (T)((unsigned)l|r); } \
    inline friend T& operator&=(T& l, T r) \
    { return l = (T)((unsigned)l&r); } \
    inline friend T operator*(bool l, T r) \
    { return (T)(l*(unsigned)r); } \
    inline friend T operator*(T l, bool r) \
    { return (T)((unsigned)l*r); } \
    inline friend T operator~(T a) \
    { return (T)~(unsigned)a; }

    for private or protected nested enums.

    enum ParserMode
    {
    PM_NONE = 0,
    PM_READY = 1, // ready for next col or row, or EOF
    PM_EMPTY = 2, // empty cell
    PM_CELL = 4, // cell
    PM_QCELL = 8, // quoted cell
    PM_HEAD = 16, // header cell
    };
    FLAGSATTRIBUTE(ParserMode)

    The result is similar than the FlagsAttribute class in DotNET.


    Marcel
    Marcel Müller, Dec 27, 2009
    #2
    1. Advertising

  3. On Dec 27, 9:36 am, nick <> wrote:
    > I'm sort of rusty at C++, so this may be silly, but here's my
    > situation:
    >
    > I have an enum like this:
    >
    >   enum ParserMode
    >   {
    >     PM_NONE  = 0,
    >     PM_READY = 1,     // ready for next col or row, or EOF
    >     PM_EMPTY = 2,     // empty cell
    >     PM_CELL =  4,     // cell
    >     PM_QCELL = 8,     // quoted cell
    >     PM_HEAD = 16,     // header cell
    >   };
    >
    > And elsewhere I want to write something like:
    >
    >   void DoStuff(ParserMode m)
    >   {
    >     if (m & PM_HEAD)
    >       ...
    >     else if (m & PM_QCELL)
    >       ...
    >   }
    >   ...
    >   DoStuff(PM_CELL | PM_EMPTY | PM_HEAD);
    >
    > But this will not work, because the compiler complains about not being
    > able to cast between int and ParserMode. So some operator overloading
    > clears that up:
    >
    >   inline ParserMode operator|(ParserMode p1, ParserMode p2)
    >   {
    >     return (ParserMode)((int)p1 | (int)p2);
    >   };
    >   inline ParserMode operator&(ParserMode p1, ParserMode p2)
    >   {
    >     return (ParserMode)((int)p1 & (int)p2);
    >   };
    >   inline ParserMode operator~(ParserMode p1)
    >   {
    >     return (ParserMode)(~(int)p1);
    >   };
    >   inline ParserMode& operator&=(ParserMode& p1, ParserMode p2) {
    >     p1 = p1 & p2;
    >     return p1;
    >   }
    >   ...
    >
    > ...And everything works fine. But, now I need to add another enum, and
    > I don't want to copy all of these overloaded operators for this other
    > enum. Is there some way I can share one set of operator overloads
    > between many enums using templates, macros, or some other trick, or am
    > I just going about this wrong altogether?
    >
    > Thanks in advance for any help.
    >
    > -- Nick


    Hi Nick

    I think the following code helps you

    enum ParserMode {
    PM_NONE = 0,
    PM_READY = 1, // ready for next col or row, or EOF
    PM_EMPTY = 2, // empty cell
    PM_CELL = 4, // cell
    PM_QCELL = 8, // quoted cell
    PM_HEAD = 16, // header cell
    };

    enum ScannerMode { // another enum
    SM_NONE = 0,
    SM_READY = 1,
    SM_EMPTY = 2,
    SM_CELL = 4,
    SM_QCELL = 8,
    SM_HEAD = 16,
    };

    template<class ENUM_TYPE>
    ENUM_TYPE operator|(ENUM_TYPE e1, ENUM_TYPE e2)
    {
    int i1 = e1; // implicit type conversion
    int i2 = e2;

    return ENUM_TYPE(i1 | i2);
    }

    template<class ENUM_TYPE>
    ENUM_TYPE operator&(ENUM_TYPE e1, ENUM_TYPE e2)
    {
    int i1 = e1; // implicit type conversion
    int i2 = e2;

    return ENUM_TYPE(i1 & i2);
    }

    template<class ENUM_TYPE>
    ENUM_TYPE operator~(ENUM_TYPE e)
    {
    int i = e; // implicit type conversion
    return ENUM_TYPE(~i);
    }

    int main()
    {
    ScannerMode s1 = SM_NONE, s2 = SM_HEAD;
    s1 = s1 | s2;

    ParserMode p1 = PM_NONE, p2 = p1;
    p1 = ~p1;
    p2 = ~(p2 & p1);

    return 0;
    }

    Regards,
    -- Saeed Amrollahi
    Saeed Amrollahi, Dec 27, 2009
    #3
  4. nick

    Balog Pal Guest

    "nick" <>
    > enum ParserMode
    > DoStuff(PM_CELL | PM_EMPTY | PM_HEAD);
    >
    > But this will not work, because the compiler complains about not being
    > able to cast between int and ParserMode. So some operator overloading
    > clears that up:
    > inline ParserMode operator|(ParserMode p1, ParserMode p2)
    > inline ParserMode operator&(ParserMode p1, ParserMode p2)
    > inline ParserMode operator~(ParserMode p1)
    > inline ParserMode& operator&=(ParserMode& p1, ParserMode p2) {
    > ...
    > ...And everything works fine. But, now I need to add another enum, and
    > I don't want to copy all of these overloaded operators for this other
    > enum.


    Yeah, it is a common "problem" too bad the language did not look for that
    when removing the int->enum conversion; the listed operations currently
    promote the enum to int (or bigger) create the result and it stays that
    type. Instead of vorking natively on the enum and produce that type. :(
    At least for | and &.

    I never used ~ that way, as it may not work. The rule is that the
    implementation type for enum must represent all the enumerators and their |.
    so the value of your ~ is not predictable (unless you can force the compiler
    to use some fixed type.

    To create the missing operators I use a macro that takes enum name as param
    and expands to text similar to yours. And just add it right after the enum
    definition if it is used that way, saves redundancy and provides kinda
    documantation of purpose too.
    Balog Pal, Dec 27, 2009
    #4
  5. nick

    Balog Pal Guest

    "Saeed Amrollahi" <>

    >>

    template<class ENUM_TYPE>
    ENUM_TYPE operator|(ENUM_TYPE e1, ENUM_TYPE e2)
    {
    int i1 = e1; // implicit type conversion
    int i2 = e2;

    return ENUM_TYPE(i1 | i2);
    }
    <<

    There is nothing to restrict the arguments here, so this template may pick
    up unwanted stuff. (And use of the old-style cast in return instead of
    static_cast makes it even more dangerous).
    Balog Pal, Dec 27, 2009
    #5
  6. Balog Pal wrote:

    > "Saeed Amrollahi" <>
    >
    >>>

    > template<class ENUM_TYPE>
    > ENUM_TYPE operator|(ENUM_TYPE e1, ENUM_TYPE e2)
    > {
    > int i1 = e1; // implicit type conversion
    > int i2 = e2;
    >
    > return ENUM_TYPE(i1 | i2);
    > }
    > <<
    >
    > There is nothing to restrict the arguments here, so this template may pick
    > up unwanted stuff. (And use of the old-style cast in return instead of
    > static_cast makes it even more dangerous).


    One may use is_enum<ENUM_TYPE>::value to restrict it.
    Johannes Schaub (litb), Dec 27, 2009
    #6
  7. On Dec 27, 5:37 pm, "Balog Pal" <> wrote:
    > "Saeed Amrollahi" <>
    >
    >
    >
    > template<class ENUM_TYPE>
    > ENUM_TYPE operator|(ENUM_TYPE e1, ENUM_TYPE e2)
    > {
    >   int i1 = e1; // implicit type conversion
    >   int i2 = e2;
    >
    >   return ENUM_TYPE(i1 | i2);}
    >
    > <<
    >
    > There is nothing to restrict the arguments here, so this template may pick
    > up unwanted stuff. (And use of the old-style cast in return instead of
    > static_cast makes it even more dangerous).


    I see what you mean. The code by Nick had the problem too. From point
    of C++ Design and Implmentation,
    he wanted to share common code between two different enums. For well-
    known reasons, I prefer template
    over macros. I am not sure, but if I'm not mistaken, in C++0x, the
    Scoped enums don't allow the implicit
    conversion from enum to int.

    regards,
    -- Saeed Amrollahi
    Saeed Amrollahi, Dec 27, 2009
    #7
  8. nick

    nick Guest

    Thanks for all the great replies!

    Marcel, your solution works perfectly. I like how it 'documents' the
    enum as being a flag set. I'm using this for now with a few small
    additions, but now I'm curious why people seem generally opposed to
    macros...

    Anyway, I have a few other small questions if you guys are still
    interested. Balog, you mentioned the ~ operator would not be
    predictable, and it sounds like it's because the way enums are stored
    in memory is not predictable?

    This makes me wonder if I should cast to unsigned as in Marcel's
    example, or signed as in Saeed's example and as I was doing before. Of
    course I'll only want to use positive numbers for my flag sets, but
    enums seem to handle negative numbers just fine, so I'm not sure what
    the right thing to do is here.

    So, I guess my question is: is there a way to explicitly store an enum
    as an int, or an unsigned int, or whatever, either using a language
    construct or some kind of compiler instrucion? I'm using GCC 4.3.

    The template stuff may be a bit over my head, but here's how I
    understand it: Saeed's solution will work, but it will overload those
    operators for all (classes? enums?), not just the flag sets I need
    those operators for. Johannes says I can use is_enum<ENUM_TYPE>::value
    to fix it so it only affects (enums?) ... is that about right? If so
    what are the advantages of that over Marcel's method, other than macro
    definitions looking ugly in the source?

    Thanks for the help!

    -- Nick
    nick, Dec 27, 2009
    #8
  9. "nick" <> wrote in message
    news:...
    > I'm sort of rusty at C++, so this may be silly, but here's my
    > situation:
    >
    > I have an enum like this:
    >
    >
    > enum ParserMode
    > {
    > PM_NONE = 0,
    > PM_READY = 1, // ready for next col or row, or EOF
    > PM_EMPTY = 2, // empty cell
    > PM_CELL = 4, // cell
    > PM_QCELL = 8, // quoted cell
    > PM_HEAD = 16, // header cell
    > };
    >
    >
    > And elsewhere I want to write something like:
    >
    >
    > void DoStuff(ParserMode m)
    > {
    > if (m & PM_HEAD)
    > ...
    > else if (m & PM_QCELL)
    > ...
    > }
    > ...
    > DoStuff(PM_CELL | PM_EMPTY | PM_HEAD);
    >
    >
    > But this will not work, because the compiler complains about not being
    > able to cast between int and ParserMode. So some operator overloading
    > clears that up:
    >
    >
    > inline ParserMode operator|(ParserMode p1, ParserMode p2)
    > {
    > return (ParserMode)((int)p1 | (int)p2);
    > };
    > inline ParserMode operator&(ParserMode p1, ParserMode p2)
    > {
    > return (ParserMode)((int)p1 & (int)p2);
    > };
    > inline ParserMode operator~(ParserMode p1)
    > {
    > return (ParserMode)(~(int)p1);
    > };
    > inline ParserMode& operator&=(ParserMode& p1, ParserMode p2) {
    > p1 = p1 & p2;
    > return p1;
    > }
    > ...
    >
    >
    > ...And everything works fine. But, now I need to add another enum, and
    > I don't want to copy all of these overloaded operators for this other
    > enum. Is there some way I can share one set of operator overloads
    > between many enums using templates, macros, or some other trick, or am
    > I just going about this wrong altogether?
    >
    > Thanks in advance for any help.
    >


    errm, if they are flags why not just use '#define'?...


    > -- Nick
    BGB / cr88192, Dec 28, 2009
    #9
  10. nick

    Balog Pal Guest

    > template<class ENUM_TYPE>
    > ENUM_TYPE operator|(ENUM_TYPE e1, ENUM_TYPE e2)
    >
    >> There is nothing to restrict the arguments here, so this template may
    >> pick
    >> up unwanted stuff. (And use of the old-style cast in return instead of
    >> static_cast makes it even more dangerous).


    >I see what you mean. The code by Nick had the problem too. From point
    >of C++ Design and Implmentation,
    >he wanted to share common code between two different enums. For well-
    >known reasons, I prefer template
    >over macros.


    Me too. But for this case I stand with macros. Global templated operator --
    that is way more evil than macros :)

    And for this case I want certain operators only for a couple of enums. (bit
    operators for those that has masks, increment, addition that I use in state
    machine, etc...)

    If that is_enum<> actually works it still picks up all the enums, masking
    problems where I used an unintended one.

    >I am not sure, but if I'm not mistaken, in C++0x, the
    >Scoped enums don't allow the implicit
    >conversion from enum to int.


    Yeah, there are good improvements, class enums do not convert to integrals
    implicitly, also you can set a fixed implementation type -- and if you did,
    you can forward-declare the enum, and define its enumerators at multiple
    places. (IIRC)
    Balog Pal, Dec 28, 2009
    #10
  11. nick

    Balog Pal Guest

    "nick" <>
    > Marcel, your solution works perfectly. I like how it 'documents' the
    > enum as being a flag set. I'm using this for now with a few small
    > additions, but now I'm curious why people seem generally opposed to
    > macros...


    You will find it in all accepted standard/guideline books. The most serious
    roblem is that macros do not respect scope. And for many applications there
    are way better tools -- constants/enums, inline functions, templates...
    Though not for everything.

    > Anyway, I have a few other small questions if you guys are still
    > interested. Balog, you mentioned the ~ operator would not be
    > predictable, and it sounds like it's because the way enums are stored
    > in memory is not predictable?


    Yes, if you have enumerators 0, 1 -- the used type may be char, unsigned
    char, short, int, possibly others. Certain use approach can go without
    problems, but I'd avoid it unless absolutely needed. If you want it to
    remove bits combined with &, rather define a named function doing right
    that.

    > This makes me wonder if I should cast to unsigned as in Marcel's
    > example, or signed as in Saeed's example and as I was doing before. Of
    > course I'll only want to use positive numbers for my flag sets, but
    > enums seem to handle negative numbers just fine, so I'm not sure what
    > the right thing to do is here.


    For bit-masks I stick to unsigned operations.

    > So, I guess my question is: is there a way to explicitly store an enum
    > as an int, or an unsigned int, or whatever, either using a language
    > construct or some kind of compiler instrucion? I'm using GCC 4.3.


    Probably gcc has a compiler switch to 'treat enums as ints' or like, though
    for all of them. Look dox for attribute, rthough don't hold your breath. For
    language level control wait C++0x.

    > The template stuff may be a bit over my head, but here's how I
    > understand it: Saeed's solution will work, but it will overload those
    > operators for all (classes? enums?), not just the flag sets I need
    > those operators for. Johannes says I can use is_enum<ENUM_TYPE>::value
    > to fix it so it only affects (enums?) ... is that about right?


    AFAIK is_enum is some boost magic.
    Balog Pal, Dec 28, 2009
    #11
  12. nick

    nick Guest

    Thanks, Balog. I did away with the ~ operator, but instead of
    replacing it with a named function I figured I'd use the - and -=
    operators for unsetting the flag. Here's what I ended up with, maybe
    this will be useful for someone else.

    #define FLAGS_(T) \
    inline static T operator*(bool l, T r) \
    { return (T)(l*(unsigned)r); } \
    inline static T operator*(T l, bool r) \
    { return (T)((unsigned)l*r); } \
    inline static T operator|(T l, T r) \
    { return (T)((unsigned)l|r); } \
    inline static T operator&(T l, T r) \
    { return (T)((unsigned)l&r); } \
    inline static T operator+(T l, T r) \
    { return l|r; } \
    inline static T operator-(T l, T r) \
    { return l&r?(T)((unsigned)l-(unsigned)r):l; } \
    inline static T& operator|=(T& l, T r) \
    { return l = l|r; } \
    inline static T& operator&=(T& l, T r) \
    { return l = l&r; } \
    inline static T& operator+=(T& l, T r) \
    { return l = l+r; } \
    inline static T& operator-=(T& l, T r) \
    { return l = l-r; }
    nick, Dec 28, 2009
    #12
  13. Balog Pal wrote:

    > "nick" <>
    >> Marcel, your solution works perfectly. I like how it 'documents' the
    >> enum as being a flag set. I'm using this for now with a few small
    >> additions, but now I'm curious why people seem generally opposed to
    >> macros...

    >
    > You will find it in all accepted standard/guideline books. The most
    > serious
    > roblem is that macros do not respect scope. And for many applications
    > there are way better tools -- constants/enums, inline functions,
    > templates... Though not for everything.
    >
    >> The template stuff may be a bit over my head, but here's how I
    >> understand it: Saeed's solution will work, but it will overload those
    >> operators for all (classes? enums?), not just the flag sets I need
    >> those operators for. Johannes says I can use is_enum<ENUM_TYPE>::value
    >> to fix it so it only affects (enums?) ... is that about right?

    >


    You first have to make sure that the type is not an integral type (just
    compare it with each and every integral type), and then make sure it's not a
    class type, and then test if it converts to one integral type. If that'S the
    case, it must be an enum. The book "C++ templates, the complete guide" shows
    a possible implementation. I'm trying to remember it here:

    // don't let class types convert to the integral types
    template<typename T>
    struct SlurpUDC {
    operator T();
    };

    typedef char yes[1];
    typedef char no[2];

    yes &int_(int);
    yes &int_(unsigned int);
    yes &int_(long);
    yes &int_(unsigned long);
    no & int_(...);
    // if your compiler has it, then long long, unsigned long long

    template<typename> struct intt { static bool const value = false; };
    #define INTT(X) template<> struct intt<X> { static bool const value = true;
    }

    INTT(char);
    INTT(signed char);
    INTT(unsigned char);
    INTT(int);
    INTT(unsigned int);
    INTT(long);
    INTT(unsigned long);
    INTT(wchar_t);
    INTT(bool);
    // if your compiler has it, then long long, unsigned long long, and hoping
    // that i've not missed one.

    C++0x introduces implementation defined integral types that may not match
    any of these, but then it also has is_enum, so you don't need to write this
    at all, anyway.

    template<typename T>
    struct is_enum {
    static bool const value = sizeof int_(SlurpUDC<T>()) == sizeof(yes)
    && !intt<T>::value;
    };

    Notice that an enumeration type will promote to one integral type out of the
    4 above. That's all i think. Now you can write

    template<typename A>
    typename enable_if<is_enum<A>::value, A>::type operator|(A a, A b) { ... }

    Hope it helps...
    Johannes Schaub (litb), Dec 28, 2009
    #13
  14. Johannes Schaub (litb) wrote:

    > Balog Pal wrote:
    >
    >> "nick" <>
    >>> Marcel, your solution works perfectly. I like how it 'documents' the
    >>> enum as being a flag set. I'm using this for now with a few small
    >>> additions, but now I'm curious why people seem generally opposed to
    >>> macros...

    >>
    >> You will find it in all accepted standard/guideline books. The most
    >> serious
    >> roblem is that macros do not respect scope. And for many applications
    >> there are way better tools -- constants/enums, inline functions,
    >> templates... Though not for everything.
    >>
    >>> The template stuff may be a bit over my head, but here's how I
    >>> understand it: Saeed's solution will work, but it will overload those
    >>> operators for all (classes? enums?), not just the flag sets I need
    >>> those operators for. Johannes says I can use is_enum<ENUM_TYPE>::value
    >>> to fix it so it only affects (enums?) ... is that about right?

    >>

    >
    > You first have to make sure that the type is not an integral type (just
    > compare it with each and every integral type), and then make sure it's not
    > a class type, and then test if it converts to one integral type. If that'S
    > the case, it must be an enum. The book "C++ templates, the complete guide"
    > shows a possible implementation. I'm trying to remember it here:
    >
    > // don't let class types convert to the integral types
    > template<typename T>
    > struct SlurpUDC {
    > operator T();
    > };
    >
    > typedef char yes[1];
    > typedef char no[2];
    >
    > yes &int_(int);
    > yes &int_(unsigned int);
    > yes &int_(long);
    > yes &int_(unsigned long);
    > no & int_(...);
    > // if your compiler has it, then long long, unsigned long long
    >
    > template<typename> struct intt { static bool const value = false; };
    > #define INTT(X) template<> struct intt<X> { static bool const value =
    > #true;
    > }
    >
    > INTT(char);
    > INTT(signed char);
    > INTT(unsigned char);
    > INTT(int);
    > INTT(unsigned int);
    > INTT(long);
    > INTT(unsigned long);
    > INTT(wchar_t);
    > INTT(bool);
    > // if your compiler has it, then long long, unsigned long long, and hoping
    > // that i've not missed one.
    >
    > C++0x introduces implementation defined integral types that may not match
    > any of these, but then it also has is_enum, so you don't need to write
    > this at all, anyway.
    >
    > template<typename T>
    > struct is_enum {
    > static bool const value = sizeof int_(SlurpUDC<T>()) == sizeof(yes)
    > && !intt<T>::value;
    > };
    >
    > Notice that an enumeration type will promote to one integral type out of
    > the 4 above. That's all i think. Now you can write
    >
    > template<typename A>
    > typename enable_if<is_enum<A>::value, A>::type operator|(A a, A b) { ... }
    >
    > Hope it helps...


    You may need to introduce special cases in "SlurpUDC" for void, function and
    array types. For each of these, there are traits though that can do the work
    for you, though. Alternatively, you can instead return "operator T&()",
    which i believe will only need a specialization for void.

    template<typename T> struct SlurpUDC { operator T&(); };
    // definitely won't convert to one of the integral types
    template<> struct SlurpUDC<void> { };
    template<> struct SlurpUDC<void const> { };
    template<> struct SlurpUDC<void volatile> { };
    template<> struct SlurpUDC<void const volatile> { };

    Put a INTT(short); INTT(unsigned short); and in my tests with GCC, the above
    codes work fine for regognizing an enum type.
    Johannes Schaub (litb), Dec 28, 2009
    #14
  15. nick

    Balog Pal Guest

    "Johannes Schaub (litb)" <>
    >>> The template stuff may be a bit over my head, but here's how I
    >>> understand it: Saeed's solution will work, but it will overload those
    >>> operators for all (classes? enums?), not just the flag sets I need
    >>> those operators for. Johannes says I can use is_enum<ENUM_TYPE>::value
    >>> to fix it so it only affects (enums?) ... is that about right?

    >>

    >
    > You first have to make sure that the type is not an integral type (just
    > compare it with each and every integral type), and then make sure it's not
    > a
    > class type, and then test if it converts to one integral type.


    Then "every integral type" is the weak point -- extensions like long long or
    __int64 are widespread, but their knowledge is tied to compiler/version
    info...

    So until official tr1:: support I don;t consider it a stable point, and
    avoid unless very much need some functionality and accept limitation to some
    compilers.

    > C++0x introduces implementation defined integral types that may not match
    > any of these, but then it also has is_enum, so you don't need to write
    > this
    > at all, anyway.


    Well, native type info is a thing we needed much. At least that remains with
    the concepts scrapped. :(
    Balog Pal, Dec 28, 2009
    #15
  16. Balog Pal wrote:

    > "Johannes Schaub (litb)" <>
    >>>> The template stuff may be a bit over my head, but here's how I
    >>>> understand it: Saeed's solution will work, but it will overload those
    >>>> operators for all (classes? enums?), not just the flag sets I need
    >>>> those operators for. Johannes says I can use is_enum<ENUM_TYPE>::value
    >>>> to fix it so it only affects (enums?) ... is that about right?
    >>>

    >>
    >> You first have to make sure that the type is not an integral type (just
    >> compare it with each and every integral type), and then make sure it's
    >> not a
    >> class type, and then test if it converts to one integral type.

    >
    > Then "every integral type" is the weak point -- extensions like long long
    > or __int64 are widespread, but their knowledge is tied to compiler/version
    > info...
    >
    > So until official tr1:: support I don;t consider it a stable point, and
    > avoid unless very much need some functionality and accept limitation to
    > some compilers.
    >

    I agree with you in principle. But I think we could test after is_enum
    signals true (and remove our integral test) whether "T()" converts to
    "void*": If it does, it must be an integral type, since T() then is a null
    pointer constant. We will have to make sure T is not a floating point type
    before.

    Then i think the limitation is moved to knowing the three guaranteed
    floating point types. (i think i forgot about them in is_enum).
    Johannes Schaub (litb), Dec 28, 2009
    #16
  17. nick

    James Kanze Guest

    On Dec 27, 7:30 pm, nick <> wrote:

    [...]
    > Marcel, your solution works perfectly. I like how it
    > 'documents' the enum as being a flag set. I'm using this for
    > now with a few small additions, but now I'm curious why people
    > seem generally opposed to macros...


    Because they don't obey scope. In this case, all of the other
    solutions have worse problems, so macros are the way to go,

    > Anyway, I have a few other small questions if you guys are
    > still interested. Balog, you mentioned the ~ operator would
    > not be predictable, and it sounds like it's because the way
    > enums are stored in memory is not predictable?


    No. It's because formally, the results may be a value which is
    not in the legal range of the enum (which in your case is
    effectively the or of all of the values). In practice, there
    won't be any problem, so I wouldn't worry about it (assuming, of
    course, that you do convert the value back to the target type).

    > This makes me wonder if I should cast to unsigned as in
    > Marcel's example, or signed as in Saeed's example and as I was
    > doing before. Of course I'll only want to use positive numbers
    > for my flag sets, but enums seem to handle negative numbers
    > just fine, so I'm not sure what the right thing to do is here.


    The safest solution is probably just to use unary + for the
    conversion, e.g.:

    MyEnum
    operator|( MyEnum lhs, MyEnum rhs )
    {
    return static_cast< MyEnum >( +lhs | +rhs );
    }

    (The unary + operator is an arithmetic operator, so forces the
    conversion of the enum into the underlying type, just as -
    would.) If this looks too weird or exotic, there's always
    "+ 0" instead.

    > So, I guess my question is: is there a way to explicitly store
    > an enum as an int, or an unsigned int, or whatever, either
    > using a language construct or some kind of compiler
    > instrucion? I'm using GCC 4.3.


    I have some template metacode somewhere which evaluates the
    underlying type, but IIRC, it's a bit hairy. Most of the time,
    you don't need variables of the underlying type, however, and
    the unary + trick is sufficient.

    --
    James Kanze
    James Kanze, Dec 28, 2009
    #17
  18. nick wrote:

    > Thanks for all the great replies!
    >
    > Marcel, your solution works perfectly. I like how it 'documents' the
    > enum as being a flag set. I'm using this for now with a few small
    > additions, but now I'm curious why people seem generally opposed to
    > macros...
    >
    > Anyway, I have a few other small questions if you guys are still
    > interested. Balog, you mentioned the ~ operator would not be
    > predictable, and it sounds like it's because the way enums are stored
    > in memory is not predictable?
    >
    > This makes me wonder if I should cast to unsigned as in Marcel's
    > example, or signed as in Saeed's example and as I was doing before. Of
    > course I'll only want to use positive numbers for my flag sets, but
    > enums seem to handle negative numbers just fine, so I'm not sure what
    > the right thing to do is here.
    >
    > So, I guess my question is: is there a way to explicitly store an enum
    > as an int, or an unsigned int, or whatever, either using a language
    > construct or some kind of compiler instrucion? I'm using GCC 4.3.
    >


    This one shows how you can find the right type:
    http://stackoverflow.com/questions/1528374/how-can-i-extend-a-lexical-cast-
    to-support-enumerated-types/1528436#1528436 . Doing "get_etype<sizeof
    find_etype(EnumType())>::type" should give the type that can store all the
    enum's values.

    That's not the underlying type though - so its sizeof and the one of the
    enum may differ, but I think that doesn't matter as long as you can store
    all values.
    Johannes Schaub (litb), Dec 28, 2009
    #18
  19. Hi!

    nick wrote:
    > Thanks, Balog. I did away with the ~ operator, but instead of
    > replacing it with a named function I figured I'd use the - and -=
    > operators for unsetting the flag. Here's what I ended up with, maybe
    > this will be useful for someone else.
    >
    > inline static T operator-(T l, T r) \
    > { return l&r?(T)((unsigned)l-(unsigned)r):l; } \


    I cannot recommend this, since it produces undefined results in case you
    try to remove more than one flag at once. Furthermore it creates a
    reasonable runtime overhead because of the conditional expression.

    I would prefer the use of l&~r, although ~r is not well defined by the
    standard so far. In practice there is most likely not even one existing
    implementation that breaks this code. And it is very unlikely that one
    will ever exist, too, since in C++0x already has defined behavior
    because you can specify the underlying type of the enum.

    It is much more likely that future versions of the standard address this
    kind of use case of enumeration types in some way. This will make the
    above code superfluous.


    By the way. There is another clean solution without using macros and
    undefined behavior. Write your own enumeration class. You could use a
    template base class to define the required operators.

    template <typename I>
    class EnumBase
    {protected:
    I Value;
    EnumBase(I value) : Value(value) {}
    };

    template <typename I, typename T>
    class EnumFlagsBase : EnumBase<I>
    {protected:
    EnumFlagsBase(I value) : EnumBase(value) {}

    friend T operator|(T l, T r)
    { l.Value |= r.Value;
    return l;
    }
    //...
    };

    class MyEnum : public EnumFlagsBase<unsigned, MyEnum>
    {
    MyEnum(unsigned value) : EnumFlagsBase(value) {}

    public:
    static const MyEnum Flag1;
    static const MyEnum Flag2;
    };

    const MyEnum MyEnum::Flag1 = MyEnum(1);
    const MyEnum MyEnum::Flag2 = MyEnum(2);


    int main()
    { MyEnum val = MyEnum::Flag1|MyEnum::Flag2;
    return 0;
    }


    The resulting object has the memory footprint of the underlying integral
    type full type safety and no runtime overhead.

    Strictly speaking the not fully specialized base class EnumBase could be
    merged into EnumFlagsBase. But if you have some common base functions or
    you need enumeration types that are no flags and you do not want the
    implicit conversion to int, the additional type might be useful to avoid
    unwanted code redundancies in the executable.


    Marcel
    Marcel Müller, Dec 30, 2009
    #19
  20. nick

    nick Guest


    > > inline static T operator-(T l, T r) \
    > > { return l&r?(T)((unsigned)l-(unsigned)r):l; } \

    >
    > I cannot recommend this, since it produces undefined results in case you
    > try to remove more than one flag at once. Furthermore it creates a
    > reasonable runtime overhead because of the conditional expression.
    >
    > I would prefer the use of l&~r, although ~r is not well defined by the
    > standard so far. In practice there is most likely not even one existing
    > implementation that breaks this code. And it is very unlikely that one
    > will ever exist, too, since in C++0x already has defined behavior
    > because you can specify the underlying type of the enum.


    Ouch, you're right, I don't know what I was thinking here, it should
    have been a simple l-=(l&r) ... must have been late. ;)

    > By the way. There is another clean solution without using macros and
    > undefined behavior. Write your own enumeration class. You could use a
    > template base class to define the required operators.
    >
    > template <typename I>
    > class EnumBase
    > {protected:
    >    I Value;
    >    EnumBase(I value) : Value(value) {}
    >
    > };
    >
    > template <typename I, typename T>
    > class EnumFlagsBase : EnumBase<I>
    > {protected:
    >    EnumFlagsBase(I value) : EnumBase(value) {}
    >
    >    friend T operator|(T l, T r)
    >    { l.Value |= r.Value;
    >      return l;
    >    }
    >    //...
    >
    > };
    >
    > class MyEnum : public EnumFlagsBase<unsigned, MyEnum>
    > {
    >    MyEnum(unsigned value) : EnumFlagsBase(value) {}
    >
    >   public:
    >    static const MyEnum Flag1;
    >    static const MyEnum Flag2;
    >
    > };
    >
    > const MyEnum MyEnum::Flag1 = MyEnum(1);
    > const MyEnum MyEnum::Flag2 = MyEnum(2);
    >
    > int main()
    > { MyEnum val = MyEnum::Flag1|MyEnum::Flag2;
    >    return 0;
    >
    > }
    >
    > The resulting object has the memory footprint of the underlying integral
    > type full type safety and no runtime overhead.
    >
    > Strictly speaking the not fully specialized base class EnumBase could be
    > merged into EnumFlagsBase. But if you have some common base functions or
    > you need enumeration types that are no flags and you do not want the
    > implicit conversion to int, the additional type might be useful to avoid
    > unwanted code redundancies in the executable.
    >
    > Marcel


    Now that looks cool, but the problem I have with it is the way the
    enums are written... instead of being able to do something like this:

    EnumFlagsBase Foobars
    {
    foo = 1,
    bar = 2
    }

    I have to write:

    class Foobars : public EnumFlagsBase<unsigned, Foobars>
    {
    Foobars(unsigned value) : EnumFlagsBase(value) {}

    public:
    static const Foobars Foo;
    static const Foobars Bar;

    };

    const Foobars Foobars::Foo = Foobars(1);
    const Foobars Foobars::Bar = Foobars(2);

    ....which seems a bit unwieldy. Thanks for explaining how it would
    look, though. I wonder if there's a way to wrangle that into a more
    concise format without using macros?
    nick, Dec 30, 2009
    #20
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. John Smith
    Replies:
    2
    Views:
    414
    Ivan Vecerina
    Oct 6, 2004
  2. Replies:
    1
    Views:
    389
  3. Replies:
    11
    Views:
    720
    James Kanze
    May 16, 2007
  4. hurcan solter
    Replies:
    3
    Views:
    715
    Cholo Lennon
    Aug 29, 2007
  5. Replies:
    11
    Views:
    545
Loading...

Share This Page