Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

An enum X : int (C#) or enum class X : int (C++11) is a type that has a hidden inner field of int that can hold any value. In addition, a number of predefined constants of X are defined on the enum. It is possible to cast the enum to its integer value and vice versa. This is all true in both C# and C++11.

In C# enums are not only used to hold individual values, but also to hold bitwise combinations of flags, as per Microsoft's recommendation. Such enums are (usually, but not necessarily) decorated with the [Flags] attribute. To make the lives of developers easier, the bitwise operators (OR, AND, etc...) are overloaded so that you can easily do something like this (C#):

void M(NumericType flags);

M(NumericType.Sign | NumericType.ZeroPadding);

I am an experienced C# developer, but have been programming C++ only for a couple of days now, and I am not known with the C++ conventions. I intend to use a C++11 enum in the exact same way as I was used to do in C#. In C++11 the bitwise operators on scoped enums are not overloaded, so I wanted to overload them.

This solicited a debate, and opinions seem to vary between three options:

  1. A variable of the enum type is used to hold the bit field, similar to C#:

    void M(NumericType flags);
    
    // With operator overloading:
    M(NumericType::Sign | NumericType::ZeroPadding);
    
    // Without operator overloading:
    M(static_cast<NumericType>(static_cast<int>(NumericType::Sign) | static_cast<int>(NumericType::ZeroPadding)));
    

    But this would counter the strongly typed enum philosophy of C++11's scoped enums.

  2. Use a plain integer if you want to store a bitwise combination of enums:

    void M(int flags);
    
    M(static_cast<int>(NumericType::Sign) | static_cast<int>(NumericType::ZeroPadding));
    

    But this would reduce everything to an int, leaving you with no clue as to which type you're supposed to put in the method.

  3. Write a separate class that will overload operators and hold the bitwise flags in a hidden integer field:

    class NumericTypeFlags {
        unsigned flags_;
    public:
        NumericTypeFlags () : flags_(0) {}
        NumericTypeFlags (NumericType t) : flags_(static_cast<unsigned>(t)) {}
        //...define BITWISE test/set operations
    };
    
    void M(NumericTypeFlags flags);
    
    M(NumericType::Sign | NumericType::ZeroPadding);
    

    (full code by user315052)

    But then you have no IntelliSense or whatever support to hint you at the possible values.

I know this is a subjective question, but: What approach should I use? What approach, if any, is the most widely recognized in C++? What approach do you use when dealing with bit fields and why?

Of course since all three approaches work, I'm looking for factual and technical reasons, generally accepted conventions, and not simply personal preference.

For example, because of my C# background I tend to go with approach 1 in C++. This has the added benefit that my development environment can hint me on the possible values, and with overloaded enum operators this is easy to write and understand, and quite clean. And the method signature shows clearly what kind of value it expects. But most people here disagree with me, probably for good reason.

share|improve this question
3  
+1 for good question. –  Md. Mahbubur R. Aaman Apr 9 '13 at 13:55

4 Answers 4

Historically, I would always have used the old (weakly-typed) enumeration to name the bit constants, and just used the storage class explicitly to store the resulting flag. Here, the onus would be on me to make sure my enumerations fit in the storage type, and to keep track of the association between the field and it's related constants.

I like the idea of strongly-typed enums, but I'm not really comfortable with the idea that variables of enumerated type may contain values that aren't among that enumeration's constants.

Eg, assuming the bitwise or has been overloaded:

enum class E1 { A=1, B=2, C=4 };
void test(E1 e) {
    switch(e) {
    case E1::A: do_a(); break;
    case E1::B: do_b(); break;
    case E1::C: do_c(); break;
    default:
        illegal_value();
    }
}
// ...
test(E1::A); // ok
test(E1::A | E1::B); // nope

For your 3rd option, you need some boilerplate to extract the enumeration's storage type. Assuming we want to force an unsigned underlying type (we can handle signed too, with a little more code):

template <size_t Size> struct IntegralTypeLookup;
template <> struct IntegralTypeLookup<sizeof(int64_t)> { typedef uint64_t Type; };
template <> struct IntegralTypeLookup<sizeof(int32_t)> { typedef uint32_t Type; };
template <> struct IntegralTypeLookup<sizeof(int16_t)> { typedef uint16_t Type; };
template <> struct IntegralTypeLookup<sizeof(int8_t)>  { typedef uint8_t Type; };

template <typename IntegralType> struct Integral {
    typedef typename IntegralTypeLookup<sizeof(IntegralType)>::Type Type;
};

template <typename ENUM> class EnumeratedFlags {
    typedef typename Integral<ENUM>::Type RawType;
    RawType raw;
public:
    EnumeratedFlags() : raw() {}
    EnumeratedFlags(EnumeratedFlags const&) = default;

    void set(ENUM e)   { raw |=  static_cast<RawType>(e); }
    void reset(ENUM e) { raw &= ~static_cast<RawType>(e); };
    bool test(ENUM e) const { return raw & static_cast<RawType>(e); }

    RawType raw_value() const { return raw; }
};
enum class E2: uint8_t { A=1, B=2, C=4 };
typedef EnumeratedFlags<E2> E2Flag;

This still doesn't give you IntelliSense or autocompletion, but the storage type detection is less ugly than I originally expected.


Now, I did find an alternative: you can specify the storage type for a weakly-typed enumeration. It even has the same syntax as in C#

enum E4 : int { ... };

Because it's weakly-typed, and implicitly converts to/from int (or whatever storage type you choose), it feels less weird to have values which don't match the enumerated constants.

The downside is that this is described as "transitional" ...

NB. this variant adds its enumerated constants to both the nested and the enclosing scope, but you can work around this with a namespace:

namespace E5 {
    enum Enum : int { A, B, C };
}
E5::Enum x = E5::A; // or E5::Enum::A
share|improve this answer
    
Another downside of weakly typed enums is that their constants pollute my namespace, since they don't need to be prefixed with the enum name. And that may also cause all kinds of weird behavior if you have two different enums both with a member with the same name. –  Virtlink Apr 9 '13 at 13:44
    
That's true. The weakly-typed variant with the specified storage type adds its constants to both the enclosing scope and its own scope, iiuc. –  Useless Apr 9 '13 at 14:00
    
The unscoped enumerator is only declared in the surrounding scope. Being able to qualify it by the enum-name is part of lookup rules, not the declaration. C++11 7.2/10: Each enum-name and each unscoped enumerator is declared in the scope that immediately contains the enum-specifier. Each scoped enumerator is declared in the scope of the enumeration. These names obey the scope rules defined for all names in (3.3) and (3.4). –  Lars Viklund Mar 10 at 20:56
    
with C++11 we have std::underlying_type that provides the underlying type of an enum. So we have 'template <typename IntegralType> struct Integral { typedef typename std::underlying_type<IntegralType>::type Type; }; ` In C++14 these is even more simplified to'template <typename IntegralType> struct Integral { typedef std::underlying_type_t<IntegralType> Type; }; –  emsr May 2 at 21:22

The simplest way is to provide the operator overloads yourself. I am thinking of creating a macro to expand the basic overloads per type.

enum class SBJFrameDrag : int
{
    None = 0x00,
    Top = 0x01,
    Left = 0x02,
    Bottom = 0x04,
    Right = 0x08,
};

inline SBJFrameDrag operator | (SBJFrameDrag lhs, SBJFrameDrag rhs)

{
    return (SBJFrameDrag)((int)lhs | (int)rhs);
}

inline SBJFrameDrag& operator |= (SBJFrameDrag& lhs, SBJFrameDrag rhs)
{
    lhs = (SBJFrameDrag)((int)lhs | (int)rhs);
    return lhs;
}
share|improve this answer

A short example of enum-flags below, looks pretty much like C#.

About the approach, in my opinion: less code, less bugs, better code.

#indlude "enum_flags.h"

ENUM_FLAGS(foo_t)
enum class foo_t
    {
     none           = 0x00
    ,a              = 0x01
    ,b              = 0x02
    };

ENUM_FLAGS(foo2_t)
enum class foo2_t
    {
     none           = 0x00
    ,d              = 0x01
    ,e              = 0x02
    };  

int _tmain(int argc, _TCHAR* argv[])
    {
    if(flags(foo_t::a & foo_t::b)) {};
    // if(flags(foo2_t::d & foo_t::b)) {};  // Type safety test - won't compile if uncomment
    };

ENUM_FLAGS(T) is a macro, defined in enum_flags.h (less then 100 lines, free to use with no restrictions).

share|improve this answer
1  
is file enum_flags.h the same as in 1st revision of your question? if yes, you can use revision URL to refer to it: http://programmers.stackexchange.com/revisions/205567/1 –  gnat Jul 21 '13 at 15:35
    
+1 looks good, clean. I'll try this out in our SDK project. –  Garet Claborn Jul 8 at 19:11

Typically you'd define a set of integer values that correspond to single-bit set binary numbers, then add them together. This is the way C programmers usually do it.

So you'd have (using the bitshift operator to set the values, eg 1 << 2 is the same as binary 100)

#define ENUM_1 1
#define ENUM_2 1 << 1
#define ENUM_3 1 << 2

etc

In C++ you have more options, define a new type rather that is an int (use typedef) and similarly set values as above; or define a bitfield or a vector of bools. The last 2 are very space efficient and make a lot more sense for dealing with flags. A bitfield has the advantage of giving you type checking (and therefore intellisense).

I'd say (obviously subjective) that a C++ programmer should use a bitfield for your problem, but I tend to see the #define approach used by C programs a lot in C++ programs.

I suppose the bitfield is the closest to C#'s enum, why C# tried to overload an enum to be a bitfield type is weird - an enum should really be a "single-select" type.

share|improve this answer
2  
using macros in c++ in such way is bad –  BЈовић Apr 9 '13 at 12:37

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.