Boost logo

Boost :

Subject: Re: [boost] Interest in bit-wise flags?
From: Jeffrey Bush (jeff_at_[hidden])
Date: 2014-06-28 19:01:26


>
> >
> > Hi Boost!
> >
> > I have developed a type-safe bit-wise flags class for my own personal use
> > and
> > am wondering if it would be of interest for me to submit it as a Boost
> > library.
> > At the moment it is not ready for submission (code is messy) but I am
> > testing
> > the waters so I present an overview below.
> > * Fully ISO standard C++11 (except for an MSVC version which is
> > API-compatible
> > except it doesn't support as many an infinite number of flag names)
> > * They are type-safe, requiring explicit casts to and from
> integers/booleans
> > * Supports standard bit-wise operations, and always keep values within
> the
> > possible set of flags
> > * Supports getting the name of a flag and looking up flags by name at
> > run-time
> > (when a flag is actually multiple-flags it gives something like "A|B")
> > * Supports getting an array of all flags at run-time
> >
> > Now the bad things, which may be a deal-breaker:
> > * Because of limitations in C++ the syntax can feel a bit awkward. You
> have
> > to
> > use a macro to define flag-names (which actually creates a class) then
> you
> > can pass those flag-names as template arguments to the flags. An
> example:
> > FLAG_NAME(A);
> > FLAG_NAME(B);
> > FLAG_NAME(C);
> > typedef Flags<std::uint8_t, Flag_A, Flag_B, Flag_C>::WithValues<0x01,
> > 0x02, 0x04> MyFlags;
> > * I also couldn't get around using the ::WithValues nested class
> > * I use a set of macros to make this cleaner (e.g. the above could be
> > FLAGS(A, B, C)) and once defined there is no more ugliness
> >
> > Thanks for consideration!
> > Jeff
> >
> > _______________________________________________
> > Unsubscribe & other changes:
> http://lists.boost.org/mailman/listinfo.cgi/boost
> >
> >
>
> Jeffrey Bush <jeff <at> coderforlife.com> writes:
>
> I have posted the current (ugly) code online:
> https://gist.github.com/coderforlife/6d8bac451d49dd1a0c81#file-flags-hpp
>
> Above that is the even uglier MSCV-specific implementation.
>
> I focused on function not readability when making it, I understand that it
> would take significant work to make it Boost ready.
>
> Differences from BOOST_BITMASK or lordodinscpplib are that it supports ~
> that is value-constrained (only flips bits that are covered by flags, it
> supports names at run-time (converting to and from strings), and it can
> default-value then flags with powers of two.
>
> If those features are undesired, then this class is unnecessary seeing that
> those other solutions are significantly simpler or more portable.
>
> However, looking at those has given me some ideas on how to simplify my
> code
> and make it possibly work in Clang.
>
> Jeff
>

I have updated it, simplifying the design while keeping the same API and
provided a simple test program, both available at
https://gist.github.com/coderforlife/6d8bac451d49dd1a0c81. It now works
fine in all compilers I have tested (MSVC 2010, MSVC 2013, mingw-w64 4.8.2,
gcc 4.8.2, clang 3.4). This simplification greatly reduced the amount of
code and allowed me to combine the fallback version that doesn't need
variadic templates into the main version. I have also made it so the
declaration can be inside a class or shared header and the definition
elsewhere. This separation was necessary because const non-integral values
cannot be defined in classes. Additionally, I have cleaned up the code
quite a bit to fit better with Boost standards, although not complete yet
(still uses tabs and some lines are longer than 80 characters although most
are not).

The only difference in API is that you can no longer pre-declare flag name
objects or do `typedef Flags<uint8_t, Name1, Name2,
Name3>::WithValues<1,2,4> MyFlags` or similar like before, you must use the
macro FLAGS()/FLAGS_WITH_VALUES() [which was available before].

I have compiled a very detailed list of differences between BOOST_BITMASK
and (BOOST_)FLAGS. The first is an advantage of BITMASK, the second is a
draw, and the remainder are advantages of FLAGS.

   - BITMASK requires an enum or scoped enum to be declared and then the
   macro to be used, but can entirely within a shared header. FLAGS would be a
   single-line, or if in a shared header have separate declarations and
   definitions. Both support being inside classes by having some macro outside
   the class.
   - BITMASK supports unlimited flags with less than 2^32 while FLAGS
   supports at most 32 flags but any underlying integral type. Both could have
   their limits increased.
   - BITMASK when used with scoped enums requires full bool casting and
   does not support the ! operator for checking if any/no flag is set and when
   used with original enums has some type safety issues (can compare values
   from different enums directly). FLAGS has the best of both, have strong
   type safety while also supporting the ! operator and an explicit bool cast
   (safe-bool).
   - BITMASK allows (and actually the ~ operator causes) bits that aren't
   associated with flags to be set while FLAGS prevents these bits from ever
   being set. This is a numerical safety (I have examples when this would be
   important).
   - FLAGS will automatically set the flag-values to powers of two if you
   want it to, reducing possibility of typos for simple flags while still
   allowing you to have explicit flag values when necessary (for example, when
   having pre-defined names for combinations of flags or retaining
   number-compatibility after dropping a particular flag name). BITMASK always
   requires you to set the values explicitly.
   - FLAGS support runtime value-name conversion, allowing something like
   XML reading/writing using the flag names instead of the numbers, debugging
   output, user-input parsing, etc.

Additionally, BOOST_BITMASK is in boost/detail, does this mean it shouldn't
be used outside of Boost?

(I have a method of making it so there is no need for a separate
declaration vs definition, however it is "ugly" but does work [and the
end-user wouldn't have to really know] - it makes each flag-name a static
function instead of static-const variable and adds an implicit constructor
that takes such a static function and calls it to get the real value which
is a static-const local variable).

Jeff


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk