5
\$\begingroup\$

All solutions to flag sets that I've come across are sets of integers representing individual bits (often preprocessor constants or enums). What has always bothered me about these is that they must be combined using bitwise OR. That is, we're adding values to a set using or. A | B reads as "A or B", not "A and B". To test if a flag is set, we use bitwise AND, equally awkward if you think about it. These operators are so well known that people don't think about them, but I'd rather use operators that better represent the action taken.

So I set out to create a simple class that I could use to pass flags into my functions. To add things to the set of flags I wanted to use +. - can be used to remove flags from the set.

The difficulty I have is with testing for flags. What is a good overloadable C++ operator that represents the "contains" operation? I wanted it to read "set X contains A". This is an asymmetric test by definition, that you can't reverse. The < operator is the closest in shape to "contains", but very far in meaning. It might also create expectations: X < A would suggest !(X >= A), but if X contains A, then it's not necessarily true that X is not contained in A (think e.g. in X only contains A).

So I settled for the == operator. X == A means X contains A, and X != A means it doesn't contain A. The danger now is that X == A does not necessarily imply A == X.

Below is the class I wrote, with a usage example. I'd like to hear suggestions for better operators to use instead of ==. I'm also open for any comments on style, clarity, etc.

There is a class that encapsulates options. It is not used directly, but through two macros. These are necessary to create unique types such that different flag sets cannot be mixed. All constants defined through the second macro are constexpr; adding and testing values can potentially be done at compile time.

namespace dip {

namespace detail {

template< typename E >
class dip__Options {
      using value_type = unsigned long;
      value_type values;
      constexpr dip__Options( value_type v, int ) : values{ v } {} // used by operator+
   public:
      constexpr dip__Options() : values( 0 ) {}
      explicit constexpr dip__Options( value_type n ) : values{ 1UL << n } {}
      constexpr bool operator==( dip__Options const other ) const {
         return ( values & other.values ) == other.values;
      }
      constexpr bool operator!=( dip__Options const other ) const {
         return !operator==( other );
      }
      constexpr dip__Options operator+( dip__Options const other ) const {
         return { values | other.values, 0 };
      }
      dip__Options& operator+=( dip__Options const other ) {
         values |= other.values;
         return *this;
      }
      dip__Options& operator-=( dip__Options const other ) {
         values &= ~other.values;
         return *this;
      }
};

} // namespace detail

/// \brief Declare a type used to pass options to a function or class.
/// <snip>
/// For class member values, add `static` in front of `DIP_DEFINE_OPTION`.
#define DIP_DECLARE_OPTIONS( name ) class name##__Tag; using name = dip::detail::dip__Options< name##__Tag >

/// \brief Use in conjunction with `DIP_DECLARE_OPTIONS`. `index` should be no higher than 31.
#define DIP_DEFINE_OPTION( name, option, index ) constexpr name option { index }

} // namespace dip

Some functions using option flags might look like this:

namespace dip {

DIP_DECLARE_OPTIONS( MyOptions );
DIP_DEFINE_OPTION( MyOptions, Option_clean, 0 );
DIP_DEFINE_OPTION( MyOptions, Option_fresh, 1 );
DIP_DEFINE_OPTION( MyOptions, Option_shine, 2 );

void MyFunction( MyOptions opts ) {
   if( opts == Option_clean + Option_fresh ) {
      std::cout << "Fresh & clean\n";
   } else {
      if( opts == Option_clean )
         std::cout << "It's clean!\n";
      if( opts == Option_fresh )
         std::cout << "It's fresh!\n";
      if( opts == Option_shine )
         std::cout << "Oh, shiny\n";
   }
}

DIP_DECLARE_OPTIONS( YourOptions );
DIP_DEFINE_OPTION( YourOptions, Option_red, 0 );
DIP_DEFINE_OPTION( YourOptions, Option_green, 1 );

void YourFunction( YourOptions opts ) {
   if( opts == Option_red )
      std::cout << "We've got red\n";
   if( opts == Option_green )
      std::cout << "We've got green\n";
}

} // namespace dip

And then using these functions would look like this:

int main() {
   dip::MyFunction( dip::Option_shine );
   dip::MyFunction( dip::Option_clean + dip::Option_fresh );
   dip::YourFunction( dip::Option_red + dip::Option_green );
   //dip::YourFunction( dip::Option_shine ); // compile error
   //auto opts = dip::Option_clean + dip::Option_red; // compile error
}
\$\endgroup\$

1 Answer 1

7
\$\begingroup\$

You're not the first to be frustrated at the bitmasks we've inherited from K&R - it would be really nice if the language provided better support in this area, and many libraries have given us different takes on the answer. Qt uses its moc preprocessor to turn Q_FLAGS enums into QFlags<E> value types with suitable operations, for example; that's just one source of inspiration you might look at.


I think the unconventional meaning of == is dangerous and will catch you out (perhaps somewhere you didn't expect it, such as inside an algorithm template or a unit-test assertion). Equals means equals: substitutable and commutative, and no programmer will thank you for subverting that.

Sorry to be so harsh, but it has to be said!

What can you do instead? To be honest, I think I would abandon trying to shoehorn the contains relationship into an operator, and simply write contains() (and possibly also its inverse, in(), if that helps the calling code read better).


I have to admit that I'm not a fan of macros (when I use them, it's an admission of some sort of failure). In particular, DIP_DEFINE_OPTION is very verbose when compared to declaring a built-in enum value.

One thing you might consider is making the template argument E be an enum type rather than a meaningless tag type, and use that to derive the flag values - have you tried that?

The use of unsigned long may be inefficient for small sets, and insufficient for large sets. This probably ought to be a template argument, too, and there really needs to be some checking that we never attempt to over-shift beyond its size.

We might be able to do better than constexpr if we are willing to never convert between integers and options at run-time - we can make a factory template (parametrised on the option number) and hide the constructor completely.


Tested version using a factory template

#include <type_traits>

namespace dip {

namespace detail {

template<typename Enum, typename = typename std::enable_if<std::is_enum<Enum>::value>::type>
class dip__Options {
    using value_type = unsigned long;
    using enum_u_type = typename std::underlying_type<Enum>::type;
    value_type values;

    // private constructor - argument is already shifted
    explicit constexpr dip__Options(value_type n = 0) : values {n} {}

public:
    dip__Options(Enum n)
        : values{ value_type(1u) << static_cast<enum_u_type>(n) }
    {}

    // factory method - alternative to constructor
    template <Enum N>
    static constexpr dip__Options value() {
        return dip__Options{ value_type(1u) << static_cast<enum_u_type>(N) };
    }

    constexpr bool operator==(dip__Options const other) const {
        return values == other.values;
    }
    constexpr bool operator!=(dip__Options const other) const {
        return !operator==(other);
    }

    constexpr bool in(dip__Options const other) const {
        return (values & other.values) == values;
    }
    constexpr bool contains(dip__Options const other) const {
        return other.in(*this);
    }

    constexpr dip__Options operator+(dip__Options const other) const {
        return dip__Options{ values | other.values };
    }
    constexpr dip__Options operator-(dip__Options const other) const {
        return dip__Options{ values & ~other.values };
    }
    // operators += and -= omitted for clarity
};

} // namespace detail

} // namespace dip

template<typename T>
dip::detail::dip__Options<T> operator+(T a, dip::detail::dip__Options<T> b)
{
    return b+a;
}
#include <iostream>
enum class MyOption_number {
    clean, fresh, shine
};

using MyOptions = dip::detail::dip__Options<MyOption_number>;
constexpr auto Option_clean = MyOptions::value<MyOption_number::clean>();
constexpr auto Option_fresh = MyOptions::value<MyOption_number::fresh>();
constexpr auto Option_shine = MyOptions::value<MyOption_number::shine>();

void MyFunction(MyOptions opts) {
    std::cout << "---\n";
    if (opts == Option_clean + Option_fresh) {
        std::cout << "Fresh & clean\n";
    } else {
        if (Option_clean.in(opts))
            std::cout << "It's clean!\n";
        if (Option_fresh.in(opts))
            std::cout << "It's fresh!\n";
        if (Option_shine.in(opts))
            std::cout << "Oh, shiny\n";
    }
}

enum class YourOption {
    red, green
};

// we might use a macro for this using declaration and the following '+' operator
using YourOptions = dip::detail::dip__Options<YourOption>;

YourOptions operator+(YourOption a, YourOption b)
{
    return YourOptions{a} + b;
}

void YourFunction(YourOptions opts) {
    std::cout << "---\n";
    if (opts.contains(YourOption::red))
        std::cout << "We've got red\n";
    if (opts.contains(YourOption::green))
        std::cout << "We've got green\n";
}
int main() {
    MyFunction(Option_shine);
    MyFunction(Option_clean + Option_fresh);
    MyFunction(Option_clean + Option_fresh + Option_shine);

    YourFunction(YourOption::red + YourOption::green);
    // YourFunction(Option_shine); // compile error

    // auto opts = Option_clean + YourOption::red; // compile error
}
\$\endgroup\$
6
  • \$\begingroup\$ Thanks for your comments, Toby. Maybe contains() is the right way to go. I have tried all sorts of things with enums, but you can't overload its operators nor define methods. This means that the constants that represent each flag must be of the new class type, not enum. And I don't know how to automatically generate constants based off of a list of enum constants. I do agree that DIP_DEFINE_OPTION is a little verbose, the code that it generates is not long or complicated, maybe it doesn't need to exist. I guess I like the symmetry of it. \$\endgroup\$ Commented Dec 20, 2017 at 13:36
  • \$\begingroup\$ I'm really curious about your factory template idea, but I don't see how it would work. Could you sketch the concept in a few sentences? \$\endgroup\$ Commented Dec 20, 2017 at 13:37
  • \$\begingroup\$ I've provided an example to show what I mean (see the value() template method); I think it can be further improved (if we allow the constructor to be a conversion, perhaps - and can we overload (non-class) operator+ and friends for enum class types? That might help, too - I'm still experimenting. \$\endgroup\$ Commented Dec 20, 2017 at 15:04
  • \$\begingroup\$ I've updated the example to should how we can add some implicit conversion as well. \$\endgroup\$ Commented Dec 20, 2017 at 15:31
  • \$\begingroup\$ That is really nice! The implicit conversion is pretty clever. Your first version had some redundancy, but this updated version is really clean. Using the enum makes it a lot more intuitive for a C++ programmer too. Thank you so very much, Toby! \$\endgroup\$ Commented Dec 20, 2017 at 15:52

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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