Question

With the following template to try to make C++ 11/14's new class-enum work as-desired, I find that the following code doesn't even try to invoke the implicit ctor to use a nonmember template that would fit by VS2013 update 1:

BitTest is defined as:

template <typename enumT>
bool BitTest(const EnumeratedFlags<enumT>& lhs, const EnumeratedFlags<enumT>& rhs)
{
    return (lhs & rhs);
}

testing code with anomalies:

enum class Flags { None = 0x00, CanChangeDataSources = 0x01, RequiresExclusiveAccess = 0x02 };
EnumeratedFlags<Flags> lhs(Flags::CanChangeDataSources);

// ... the following fails to even attempt to convert the Foo::Flags
if (BitTest(lhs, Flags::CanChangeDataSources)) { DoSomething(); }

// this compiles, but I don't know why it's necessary (and this is annoying and ugly)...
if (BitTest(lhs, EnumeratedFlags<Flags>(Flags::CanChangeDataSources))) { DoSomething(); }

Here is the template definition I am currently attempting to use:

template <typename enumT>
class EnumeratedFlags
{
public:

    typedef enumT enum_type;
    typedef typename std::underlying_type<enumT>::type store_type;

// constructors

    EnumeratedFlags()
        : m_bits(0)
    {
    }

    EnumeratedFlags(enum_type flag)
        : m_bits(static_cast<store_type>(flag))
    {
    }

    explicit EnumeratedFlags(store_type value)
        : m_bits(value)
    {
    }

    EnumeratedFlags(const std::initializer_list<enum_type> & initializers)
        : m_bits(0)
    {
        for (auto flag : initializers)
            m_bits |= static_cast<store_type>(flag);
    }

// operators

    operator std::string () const
    {
        return to_string();
    }

    bool operator [] (enum_type flag) const
    {
        return test(flag);
    }

    store_type operator * () const
    {
        return m_bits;
    }

    operator bool () const
    {
        return m_bits != store_type(0);
    }

// explicit accessors

    store_type bits() const
    {
        return m_bits;
    }

    std::string to_string() const
    {
        std::string str(size(), '0');

        for (size_t x = 0; x < size(); ++x)
            str[size() - x - 1] = (m_bits & (1 << x) ? '1' : '0');

        return str;
    }

    EnumeratedFlags & set(enum_type flag)
    {
        BitSet(m_bits, static_cast<store_type>(flag));
        return *this;
    }

    EnumeratedFlags & set_if(enum_type flag, bool set_or_clear)
    {
        BitSetIf(m_bits, static_cast<store_type>(flag), set_or_clear);
        return *this;
    }

    EnumeratedFlags & clear()
    {
        m_bits = store_type(0);
        return *this;
    }

    EnumeratedFlags & flip()
    {
        m_bits = ~m_bits;
        return *this;
    }

    EnumeratedFlags & flip(enum_type flag)
    {
        m_bits ^= static_cast<store_type>(flag);
        return *this;
    }

    size_t count() const
    {
        // http://www-graphics.stanford.edu/~seander/bithacks.html#CountBitsSetKernighan

        store_type bits = m_bits;
        size_t total = 0;
        for (; bits != 0; ++total)
        {
            bits &= bits - 1; // clear the least significant bit set
        }
        return total;
    }

    size_t size() const
    {
        // one character per possible bit
        return sizeof(enum_type) * 8;
    }

    bool test(enum_type flag) const
    {
        return BitTest(m_bits, static_cast<store_type>(flag));
    }

    bool any() const
    {
        return m_bits != 0;
    }

    bool none() const
    {
        return m_bits == 0;
    }

private:

    store_type m_bits;
};

template <class charT, class traits, typename enumT>
std::basic_ostream<charT, traits> & operator << (std::basic_ostream<charT, traits> & os, const EnumeratedFlags<enumT> & flags)
{
    return os << flags.to_string();
}

template <typename enumT>
EnumeratedFlags<enumT> operator & (const EnumeratedFlags<enumT>& lhs, const EnumeratedFlags<enumT>& rhs)
{
    return EnumeratedFlags<enumT>(lhs.bits() & rhs.bits());
}

template <typename enumT>
EnumeratedFlags<enumT> operator | (const EnumeratedFlags<enumT>& lhs, const EnumeratedFlags<enumT>& rhs)
{
    return EnumeratedFlags<enumT>(lhs.bits() | rhs.bits());
}

template <typename enumT>
EnumeratedFlags<enumT> operator ^ (const EnumeratedFlags<enumT>& lhs, const EnumeratedFlags<enumT>& rhs)
{
    return EnumeratedFlags<enumT>(lhs.bits() ^ rhs.bits());
}

template <typename enumT>
bool BitTest(const EnumeratedFlags<enumT>& lhs, const EnumeratedFlags<enumT>& rhs)
{
    return (lhs & rhs);
}

Basically, I would have thought that any free function in the form of X (const T & lhs, const T & rhs) would use up to one user-defined conversion to find a valid invocation of BitTest<>(), above. Instead, I have to explicitly state the conversion in my above code to get the compiler to use this function, which vastly reduces the expressive power of template class EnumeratedFlags<>.

In general, C++ drives me nuts that there isn't a good way to use bits that combines all of the features and good programming habits of using a scoped enum (enum class Foo) and bit-fields (or a similar named set of bits) and make using them very easy for the client-programmer while retaining basic sanity checking from the compiler (won't auto-convert to a numeric type or vice-verse). Seems like a more comprehensive improvement in the language spec is required to make a bit-field enum that really shines... or am I missing something?

Was it helpful?

Solution

From §14.8.1/6 [temp.arg.explicit]

Implicit conversions (Clause 4) will be performed on a function argument to convert it to the type of the corresponding function parameter if the parameter type contains no template-parameters that participate in template argument deduction.

In your example the second argument to BitTest() does participate in template argument deduction, hence no implicit conversion is attempted and the compiler is unable to convert the type Flags to const EnumeratedFlags<Flag>&.

You could solve it by converting the second parameter type to a non-deduced context, thus preventing it from participating in template argument deduction.

template <typename enumT>
bool BitTest(const EnumeratedFlags<enumT>& lhs, 
             const EnumeratedFlags<typename EnumeratedFlags<enumT>::enum_type>& rhs)
{
    return (lhs & rhs);
}

Live demo

Of course, the other solution is to provide these overloads instead

template <typename enumT>
bool BitTest(const EnumeratedFlags<enumT>& lhs, 
             enumT rhs)
{
    return (lhs & EnumeratedFlags<enumT>(rhs));
}

template <typename enumT>
bool BitTest(enumT lhs, 
             const EnumeratedFlags<enumT>& rhs)
{
    return BitTest(rhs, lhs);
}

OTHER TIPS

My question is already answered above. However, this has been bothering me forever, so I was able to leverage everyone's responses and came up with, what I think is for the first time, a mechanism that I really like!

This gives me a way to use C++ enumerations to store bit flag values, and then to logically bit-manipulate them at will, while never losing type information or the compiler's assistance with making sure that I fall into the pit of success. :)

I hope this helps you too! (Note: this code compiles and runs properly under VS2013 update 1)

#pragma once

#include <type_traits>

// This is my ongoing attempt to make a really solid enumeration facility in C++11/14
//
// What I hate about C++98 (and older) enum
//  - lack of namespace scoping of the non-class enum (name collisions everywhere)
//  - auto conversion to numeric types (int i = MyEnumConstant), but no conversion back again (so supports losing info, but restricts regaining it)
//
// What I hate about C++11/14 built-in `enum class X`
//  - having to constantly cast in order to treat them (now neither direction works)
//  - no built-in mechanism to treat enumerated values as bits or bit-flags

template <typename enum_type>
class bitflag_enum
{
public:

    // expose our underlying types
    typedef enum_type enum_type;
    typedef typename std::underlying_type<enum_type>::type store_type;

    // constructors

    bitflag_enum()
        : m_bits(0)
    {
    }

    bitflag_enum(enum_type flag)
        : m_bits(static_cast<store_type>(flag))
    {
    }

    explicit bitflag_enum(store_type value)
        : m_bits(value)
    {
    }

    // operators

    operator bool() const
    {
        return m_bits != store_type(0);
    }

    // explicit accessors

    store_type bits() const
    {
        return m_bits;
    }

private:

    store_type m_bits;
};

// because implicit conversion isn't considered if a type participates in template type deduction,
// we've defined both homogeneous and heterogeneous operators here for bitflag_enum<enum_type> and enum_type
// hence we define logical operators &, |, ^ and comparisons for TxT, TxU, UxT (where T is bitflag_enum<enum_type>, and U is enum_type)

template <typename enum_type>
bool operator != (bitflag_enum<enum_type> lhs, bitflag_enum<enum_type> rhs)
{
    return bitflag_enum<enum_type>(lhs.bits() != rhs.bits());
}

template <typename enum_type>
bool operator != (bitflag_enum<enum_type> lhs, enum_type rhs)
{
    using store_type = std::underlying_type<enum_type>::type;
    return bitflag_enum<enum_type>(lhs.bits() != static_cast<store_type>(rhs));
}

template <typename enum_type>
bool operator != (enum_type lhs, bitflag_enum<enum_type> rhs)
{
    using store_type = std::underlying_type<enum_type>::type;
    return bitflag_enum<enum_type>(static_cast<store_type>(lhs) != rhs.bits());
}

template <typename enum_type>
bool operator == (bitflag_enum<enum_type> lhs, bitflag_enum<enum_type> rhs)
{
    return bitflag_enum<enum_type>(lhs.bits() == rhs.bits());
}

template <typename enum_type>
bool operator == (bitflag_enum<enum_type> lhs, enum_type rhs)
{
    using store_type = std::underlying_type<enum_type>::type;
    return bitflag_enum<enum_type>(lhs.bits() == static_cast<store_type>(rhs));
}

template <typename enum_type>
bool operator == (enum_type lhs, bitflag_enum<enum_type> rhs)
{
    using store_type = std::underlying_type<enum_type>::type;
    return bitflag_enum<enum_type>(static_cast<store_type>(lhs) == rhs.bits());
}

template <typename enum_type>
bitflag_enum<enum_type> operator & (bitflag_enum<enum_type> lhs, bitflag_enum<enum_type> rhs)
{
    return bitflag_enum<enum_type>(lhs.bits() & rhs.bits());
}

template <typename enum_type>
bitflag_enum<enum_type> operator & (bitflag_enum<enum_type> lhs, enum_type rhs)
{
    using store_type = std::underlying_type<enum_type>::type;
    return bitflag_enum<enum_type>(lhs.bits() & static_cast<store_type>(rhs));
}

template <typename enum_type>
bitflag_enum<enum_type> operator & (enum_type lhs, bitflag_enum<enum_type> rhs)
{
    using store_type = std::underlying_type<enum_type>::type;
    return bitflag_enum<enum_type>(static_cast<store_type>(lhs)& rhs.bits());
}

template <typename enum_type>
bitflag_enum<enum_type> operator | (bitflag_enum<enum_type> lhs, bitflag_enum<enum_type> rhs)
{
    return bitflag_enum<enum_type>(lhs.bits() | rhs.bits());
}

template <typename enum_type>
bitflag_enum<enum_type> operator | (bitflag_enum<enum_type> lhs, enum_type rhs)
{
    using store_type = std::underlying_type<enum_type>::type;
    return bitflag_enum<enum_type>(lhs.bits() | static_cast<store_type>(rhs));
}

template <typename enum_type>
bitflag_enum<enum_type> operator | (enum_type lhs, bitflag_enum<enum_type> rhs)
{
    using store_type = std::underlying_type<enum_type>::type;
    return bitflag_enum<enum_type>(static_cast<store_type>(lhs) | rhs.bits());
}

template <typename enum_type>
bitflag_enum<enum_type> operator ^ (bitflag_enum<enum_type> lhs, bitflag_enum<enum_type> rhs)
{
    return bitflag_enum<enum_type>(lhs.bits() ^ rhs.bits());
}

template <typename enum_type>
bitflag_enum<enum_type> operator ^ (bitflag_enum<enum_type> lhs, enum_type rhs)
{
    using store_type = std::underlying_type<enum_type>::type;
    return bitflag_enum<enum_type>(lhs.bits() ^ static_cast<store_type>(rhs));
}

template <typename enum_type>
bitflag_enum<enum_type> operator ^ (enum_type lhs, bitflag_enum<enum_type> rhs)
{
    using store_type = std::underlying_type<enum_type>::type;
    return bitflag_enum<enum_type>(static_cast<store_type>(lhs) ^ rhs.bits());
}

// The only missing pieces above are for the UxU cases
// we allow you to have those by defining a specialization of is_bitflag_enum<>, as follows:
//
// template <> struct is_bitflag_enum<YourEnumType> : std::true_type { };
//
// However, by default, no other types will convert to an bitflag_enum<> unless you explicitly say you want it
//
// If you have asked for them, then you can use MyEnum::ValueX | MyEnum::ValueY and that will produce a bitflag_enum<MyEnum>
// so your code can simply use your enumeration values with scope and as-if they were bit flags as you would think you could

// don't mess up existing enumerations or types by defining these global operators on every existing type!
template <typename enum_type> struct is_bitflag_enum : std::false_type { };

template <typename enum_type>
typename std::enable_if<is_bitflag_enum<enum_type>::value, bitflag_enum<enum_type>>::type
operator & (enum_type lhs, enum_type rhs)
{
    using store_type = std::underlying_type<enum_type>::type;
    return bitflag_enum<enum_type>(static_cast<store_type>(lhs) & static_cast<store_type>(rhs));
}

template <typename enum_type>
typename std::enable_if<is_bitflag_enum<enum_type>::value, bitflag_enum<enum_type>>::type
operator | (enum_type lhs, enum_type rhs)
{
    using store_type = std::underlying_type<enum_type>::type;
    return bitflag_enum<enum_type>(static_cast<store_type>(lhs) | static_cast<store_type>(rhs));
}

template <typename enum_type>
typename std::enable_if<is_bitflag_enum<enum_type>::value, bitflag_enum<enum_type>>::type
operator ^ (enum_type lhs, enum_type rhs)
{
    using store_type = std::underlying_type<enum_type>::type;
    return bitflag_enum<enum_type>(static_cast<store_type>(lhs) ^ static_cast<store_type>(rhs));
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top