Having fun with C++11 – how to pass flags to a function
I’ve never been very active posting here, and since I left academia it has been even slower. All of 2016 passed without a single post! Since I now work for a company, it’s become more difficult for me to post about the fun little things that I work with. Nevertheless, I wanted to share a C++ construct that I came up with to pass flags (a collection of yes/no options) to a function.
I’ve been having fun with C++11. I have used mostly C (besides scripting languages such MATLAB or Python) to write image analysis algorithms. Back in the early 1990’s I played around with C++, but hadn’t used it since. In my new job, all code is in C++, and so I’ve had the chance to learn about C++11 and its standard library. C++11 feels like a different language from the C++ of the early 1990’s. It is very expressive, and makes even complicated things easy to implement and maintain.
One of the issues I’ve run into is how to pass flags to a function. Image analysis algorithms often have different modes
of operation, and the user of the function must be able to choose the mode. A very simple example could be a clipping
function: for example, the clipping function can clip only the low grey-values, only the high grey-values, or both; it
can use hard clipping or soft clipping; and it can use the given limits or compute them from the data. These options
correspond to four independent boolean values: ClipLow
, ClipHigh
, ClipSoft
, and ClipAuto
. If the function has
four boolean parameters, then the function call is quite unreadable:
Clip(image, true, true, false, true);
One does not know what each of the true and false values refer to.
The classical solution
In the old days, writing in C, we would use an enumerator for these four options, giving each one a different power of two as a value:
typedef enum {
ClipLow = 1,
ClipHigh = 2,
ClipSoft = 4,
ClipAuto = 8,
} ClipFlags;
Clip(image, ClipLow | ClipHigh | ClipAuto);
The values can be combined with the bit-wise OR operator, yielding a single value that encodes which of the options were chosen. This worked well, but (being C) there was no strong type checking. You could pass the value 5327 as the flag, and the compiler would be just as happy. Later versions of the C standard enforced stronger type checking, and the compiler would issue warnings if the value passed was not one of those in the enumerator. However, this meant that any combination of two values would yield an integer that is not one of the values in the enumerator, so warnings would be issued when passing more than one flag to the function. To suppress these warnings, function calls looked like this instead:
Clip(image, (ClipFlags)(ClipLow | ClipHigh | ClipAuto));
…hardly any better.
The function itself would test whether a flag is set or not by using the bit-wise AND operator:
if (flags & ClipAuto) { ... }
The C++ way
In C++ there is stronger type checking. Enumerators work more or less the same way, but C++11 introduces an enum class
,
which does strong type checking: it is impossible to cast a random integer to the enumerator type. The strongly typed
enumerator is great, as it makes it unnecessary to test for illegal values. A variable of that type can only ever obtain
values within the given collection. This does mean, however, that it is useless for flags.
Alternatives are the std::bitset
type, which implements exactly what flags are supposed to do, but identify bits only
by their position. One then would define a strong enumerator that gives a name to each bit in the set:
enum class ClipFlags {
ClipLow, // has a value of 0 by default
ClipHigh, // has a value of 1
ClipSoft, // has a value of 2
ClipAuto, // has a value of 3
};
std::bitset<4> clipFlagsParam;
clipFlagsParam[int(ClipFlags::ClipLow)] = true;
This makes for even more awkward code… But because bit sets can be ANDed and ORed together, one can define constants that have only one bit set, and use these just as we used the enumerated constants in the old C code (but with strong type checking, meaning that one cannot assign just any integer value as the flag set):
using ClipFlags = std::bitset<4>;
constexpr ClipFlags ClipLow(1);
constexpr ClipFlags ClipHigh(2);
constexpr ClipFlags ClipSoft(4);
constexpr ClipFlags ClipAuto(8);
Clip(image, ClipLow | ClipHigh | ClipAuto);
There is, however, still one small issue here that we haven’t discussed yet: if another function, with a different set
of flags, also happens to have four flags, its flag type (e.g. ConvolutionFlags
) would be an alias, because it also
maps to a std::bitset<4>
. Good-bye strong type checking:
Clip(image, ConvolutionHack); // the compiler is OK with this,
// even if it makes no sense...
How do we really want to use flags?
Let’s take a moment here to reflect on the syntax of flags. We OR the flags together to set multiple flags. We are
saying “I want ClipLow
OR ClipHigh
”, rather than “I want ClipLow
AND ClipHigh
”. Because we do want both flags
set, not the one or the other. To test, we use the AND operator, which is equally awkward. We accept this syntax because
we’ve always done it this way, and we know what it means, but the syntax really does not express what we mean at all!
Wouldn’t it be nicer if we could instead say
Clip(image, ClipLow + ClipHigh + ClipAuto);
What operator to use to test whether a flag is set is a little bit more involved. I have settled finally for the
equality operator (==
), even though it’s somewhat an abuse of its meaning. But we expect comparison operators to yield
a true/false response, so it is a natural fit in some way. Another option could be the greater-or-equal operator (>=
),
which could be read as “the flag set is a superset of the flags on the right”. So flags >= ClipLow + ClipHigh
says
that flags
has at least those two flags set. It also makes the asymmetry clear.
However, !(flags >= ClipLow + ClipHigh)
is not the same as flags < ClipLow + ClipHigh
, and that can be confusing
too. So instead I picked the equality operator here, with the inequality operator returning the opposite value.
My way
This is the class that I came up with:
template<typename E>
class Options {
unsigned long values;
constexpr Options(unsigned long v, int) : values{v} {}
public:
constexpr Options() : values(0) {}
constexpr Options(unsigned n) : values{1UL << n} {}
constexpr bool operator==(Options const& other) const {
return (values & other.values) == other.values;
}
constexpr bool operator!=(Options const& other) const {
return !operator==(other);
}
constexpr Options operator+(Options const& other) const {
return {values | other.values, 0};
}
Options& operator+=(Options const& other) {
values |= other.values;
return *this;
}
Options& operator-=(Options const& other) {
values &= ~other.values;
return *this;
}
};
It is pretty straight-forward. A single private member is an integer that holds the flag values. A constructor creates
an object with the n’th value set. The operators discussed above are implemented, as well as the compound assignment
operators +=
and -=
, which add and remove a flag from a set. The private constructor is used by the plus operator.
The class is defined as a class template, where the template argument exists solely to make a distinction between
different sets. That is, the template argument makes it so that we can define two different flag sets (for the clip
function and the convolution function) and keep those sets as strictly different types. This is how you would use it:
class ClipFlags__Tag; // never defined!
using ClipFlags = Options<ClipFlags__Tag>;
constexpr ClipFlags ClipLow(0);
constexpr ClipFlags ClipHigh(1);
constexpr ClipFlags ClipSoft(2);
constexpr ClipFlags ClipAuto(3);
Clip(image, ClipLow + ClipHigh + ClipAuto);
Clip(image, ConvolutionHack); // compiler error, ConvolutionHack is not of type ClipFlags.
if (flags == ClipAuto) { ... }
I have also defined the following two preprocessor macros, to make it even easier to use this class template:
#define
DECLARE_OPTIONS(name) class name##__Tag; using name = Options<name##__Tag>
#define
DEFINE_OPTION(name, option, index) constexpr name option(index)
To create the constants above we now do:
DECLARE_OPTIONS(ClipFlags);
DEFINE_OPTIONS(ClipFlags, ClipLow, 0);
DEFINE_OPTIONS(ClipFlags, ClipHigh, 1);
DEFINE_OPTIONS(ClipFlags, ClipSoft, 2);
DEFINE_OPTIONS(ClipFlags, ClipAuto, 3);
This is hardly more work than the original C code to define flags, yet it yields strong type checking (the user will never be able to pass illegal or unknown flags, confuse the flags from different algorithms, etc.), and a very pleasant syntax that makes sense for people that never had to deal with bit sets.
Do note also that
DEFINE_OPTIONS(ClipFlags, ClipBoth, ClipLow + ClipHigh);
invokes the copy constructor, so is perfectly fine.
In closing
C++11 really allows to create custom types that behave in any way one wants. And it doesn’t take a lot of effort to do so. I’m certainly enjoying its expressiveness, especially compared with plain old C.
Let me know what you think!
Edit
Based on a question on Code Review, I’ve significantly modified this idea here. You can see the current implementation in DIPlib.