0

In C I am able to take two or more enumerated flags and inclusive OR them: (flag1 | flag2)

In C++ I cannot do the same thing. I have some flags that I've scoped to my class but to OR them I have to cast. It looks like this:

namespace name
{
    class test
    {
    public:
        enum flag
        {
            firstflag = 1, secondflag = 2, thirdflag = 4
        };

        void foo(flag flags)
        {
            return;
        }
    };
}

int main(int argc, char *argv[])
{
    name::test obj;

    obj.foo((name::test::flag)(name::test::firstflag | name::test::secondflag));

    return 0;
}

That's kind of a mouthful, more in the real code than this example. I am wondering if there is a better way. I could change the argument passed to int void foo(int flags) but then in the Visual Studio 2010 debugger I wouldn't see the flags ORed, just a number.


Without the cast I get an error:

obj.foo(name::test::firstflag | name::test::secondflag);
error C2664: 'name::test::foo' : cannot convert parameter 1 from 'int' to 'name::test::flag'

I searched on stackoverflow and found a question with the answer to overload the | operator:
c++ - "enum - invalid conversion from int" in class - Stack Overflow

Yet when I use std::ios flags I don't have to do any casting, why is that? For example fstream has a prototype like for example fstream(char *filename, std::ios_base) and I can do this in my code:

fstream("filename",  ios::in | ios::out);


What do you guys suggest? I do not have a lot of C++11 capabilities so if you could keep that in mind when answering. Thanks

Community
  • 1
  • 1
loop
  • 3,460
  • 5
  • 34
  • 57
  • 1
    Looking at `std::ios_base`, the flags are declared `static constexpr type variable = implementation` – yizzlez Mar 07 '14 at 00:26
  • Aside from your question, the mouthful is easily slightly shortened: http://coliru.stacked-crooked.com/a/f9898f77f95c3c8b – Mooing Duck Mar 07 '14 at 00:48

3 Answers3

5

In the GCC implementation of ios::in, ios::out, etc. They use operator overloading to get the desired effect. E.g.

inline _GLIBCXX_CONSTEXPR _Ios_Openmode
operator|(_Ios_Openmode __a, _Ios_Openmode __b)
{ return _Ios_Openmode(static_cast<int>(__a) | static_cast<int>(__b)); }

In your case you could define the following method:

inline flag operator|(flag f1, flag f2)
{ 
  return flag(static_cast<int>(f1) | static_cast<int>(f2)); 
}

Cheers,

LynchPin
  • 66
  • 2
1

std::bitset is pretty good style for C++. Reference

#include <bitset>

namespace Flags {
enum Flags {
  first, second, third, NUM_FLAGS
};
}

class Test {
public:
  void foo(std::bitset<Flags::NUM_FLAGS> flags) {
    return;
  }
};

int main() {
  std::bitset<Flags::NUM_FLAGS> flags;
  flags[Flags::first] = true;
  flags[Flags::second] = false;
  flags[Flags::third] = true;

  Test obj;
  obj.foo(flags);
}
Sam Cristall
  • 4,328
  • 17
  • 29
0

You can write a template class to convert flags to std::bitset<> (which doesn't need ) indices and vice versa:

template<typename BitFieldType, BitFieldType BitValue, int8_t CurIndex>
struct BitIndexSelector;

template<typename BitFieldType, BitFieldType BitValue, int8_t CurIndex>
struct BitIndexSelector
{
    static const int8_t ResultIndex =
            ((CurIndex != -1) &&
             (((((BitFieldType)1) << CurIndex) & BitValue) > 0)
            ? CurIndex
            : BitIndexSelector
                <BitFieldType
                ,BitValue
                ,CurIndex - 1>::ResultIndex);

};

template<typename BitFieldType, BitFieldType BitValue>
struct BitIndexSelector<BitFieldType,BitValue,-1>
{
    static const int8_t ResultIndex = -1;
};

template<typename BitFieldType, BitFieldType BitValue = 0>
struct GetBitIndex
{
    static const int8_t Index =
        BitIndexSelector
            < BitFieldType
            , BitValue
            , sizeof(BitFieldType) * sizeof(char) * CHAR_BIT>::ResultIndex;
    typedef std::bitset<sizeof(BitFieldType) * CHAR_BIT> BitsetType;
};

Here's a sample for usage to convert the bitmask defined poll() event types to index values for accessing the various flags represented with a std::bitset<> in a convenient way:

enum PollEvents
{
    EV_POLLIN = GetBitIndex<short,POLLIN>::Index ,
    EV_POLLOUT = GetBitIndex<short,POLLOUT>::Index ,
    EV_POLLPRI = GetBitIndex<short,POLLPRI>::Index ,
    EV_POLLRDHUP = GetBitIndex<short,POLLRDHUP>::Index ,
    EV_POLLERR = GetBitIndex<short,POLLERR>::Index ,
    EV_POLLHUP = GetBitIndex<short,POLLHUP>::Index ,
};

A corresponding std::bitset<> can be manipulated using the indices defined in the enum above:

GetBitIndex<short>::BitsetType pollEventMask;

pollEvents[EV_POLLIN] = true; // set the POLLIN flag
pollEvents[EV_POLLERR] = false; // unset the POLLERR flag

The two operations above will do the same as:

short pollEventMask= POLLHUP | POLLERR; // Initialization just to show preset 
                                        // values
pollEventMask = (pollEventMask | POLLIN) & ~POLLERR; // This is the equivalent
                                                     // for the operations shown
                                                     // above

Some people claim for shortness, I personally claim for easy to read and use code on the semantics level (as long it doesn't hurt performance or footprint significantly).

A complete code sample can be found here (note it's only a gist I made, to bring it into productive state in another project).

πάντα ῥεῖ
  • 1
  • 13
  • 116
  • 190
  • That appears to be _complete_ overkill – Mooing Duck Mar 07 '14 at 00:45
  • @MooingDuck I have found that pretty useful for some contexts. I'm pretty sure that using a `std::bitset<>` to manage a certain set of flags is in no way more inefficient, than manipulating these directly with the `|`, `&` and `~` operators. – πάντα ῥεῖ Mar 07 '14 at 00:50
  • There is the fact that it's built in and everyone ought to know about it. To justify a custom version, you'd have to demonstrate that yours is significantly better in some way. I an't even figure out what yours _does_ much less why it's better. – Mooing Duck Mar 07 '14 at 05:59
  • @MooingDuck I edited to clarify a bit (ha pun intended ;) ). If one doesn't need such translation, just to define the bitset indices as enum is the more reasonable solution of course (as shown in Sam Cristall's answer). – πάντα ῥεῖ Mar 08 '14 at 00:10