Boost logo

Boost :

Subject: [boost] How best to implement a bitfield in C++ 14?
From: Niall Douglas (s_sourceforge_at_[hidden])
Date: 2016-02-03 04:01:09


Dear Boost,

As part of the AFIO v2 rewrite I've been trying to improve on how
AFIO v1 did bitfields by making better use of constexpr and the type
system to make usage more natural. I'd appreciate feedback on my
efforts.

AFIO v1 has an example of usage at https://goo.gl/oVLIjU or
https://gist.github.com/ned14/9ef2a9d00da0ffc877b8 and it took the
following form:

enum class flag : size_t
{
  none=0,
  delete_on_close=1,
  disable_safety_fsyncs=2
};
BOOST_AFIO_DECLARE_CLASS_ENUM_AS_BITFIELD(flag)

The BOOST_AFIO_DECLARE_CLASS_ENUM_AS_BITFIELD macro declares all the
sensible global overloads you'd expect, is type safe and you can use
it without surprise except in one single instance: testing for
emptiness. The problem is that you cannot declare as a global
operator an explicit operator bool nor member functions on enums, so
one cannot do if(f) though if(!f) is fine. In AFIO v1, I used if(!!f)
and if(!!(f & flag::foo)) everywhere, which is fine if a little
abtuse.

AFIO v2's current bitfield has an example of usage at
https://goo.gl/LsjSGD or
https://gist.github.com/ned14/89ee39c6b8eb5254116a and it takes the
following form:

struct flag : bitwise_flags<flag>
{
  flag() = default;
  constexpr flag(bitwise_flags<flag> v) noexcept :
bitwise_flags<flag>(v) { }
  static constexpr auto none(){ return bit(0);}
  static constexpr auto delete_on_close(){ return bit(1); }
  static constexpr auto disable_safety_fsyncs(){ return bit(2); }
};

Here the bitwise_flags<> base class does all the implementation
magic, so once again you can use it without surprise except for the
obvious clanger - bitfield items are now functions, so instead of the
more natural:

  flag f(flag::disable_safety_fsyncs);
  flag f2(f&flag::none);
  f2|=flag::delete_on_close;
  constexpr flag f3(flag::disable_safety_fsyncs);
  constexpr flag f4(f3&flag::none);

You are stuck with:

  flag f(flag::disable_safety_fsyncs());
  flag f2(f&flag::none());
  f2|=flag::delete_on_close();
  constexpr flag f3(flag::disable_safety_fsyncs());
  constexpr flag f4(f3&flag::none());

I'll admit I started this with the static constexpr flag values being
member variables which always works on MSVC and on GCC and clang when
the optimiser is running. Unfortunately both GCC and clang appear to
be extremely conservative about when a constexpr variable is
ODR-used, indeed despite my original design of an unavoidable
lvalue-to-rvalue conversion intended to force ODR-used to be off both
compilers go ahead and enforce ODR-used anyway which generates
undefined reference errors for the storage of the static member
variables :( If you'd like to read more about that, and the WG21 DR
relating to [basic.def.odr], see
https://stackoverflow.com/questions/8016780/undefined-reference-to-sta
tic-constexpr-char/28846608#28846608.

So, do Boosters think we can actually make a C++ 14 bitfield which:

1. Is typesafe, so not a C bitfield.

2. Is convenient for programmers to declare (i.e. little boilerplate
in a specific bitfield declaration).

3. Works as you'd expect a bitfield to work, including
bitfield::flag.

4. if(bf) works.

5. Is 100% constexpr and generates zero runtime overhead.

My thanks in advance for any advice.

Niall

-- 
ned Productions Limited Consulting
http://www.nedproductions.biz/ 
http://ie.linkedin.com/in/nialldouglas/



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