How best to implement a bitfield in C++ 14?
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/
2016-02-03 17:01 GMT+08:00 Niall Douglas
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.
This is what I have: http://melpon.org/wandbox/permlink/RDl3B6EmnG5hRuAJ
2016-02-03 17:30 GMT+08:00 TONGARI J
2016-02-03 17:01 GMT+08:00 Niall Douglas
: [...]
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.
This is what I have: http://melpon.org/wandbox/permlink/RDl3B6EmnG5hRuAJ
After re-reading your post, I think this doesn't meet your requirement :p
On 3 Feb 2016 at 17:37, TONGARI J wrote:
This is what I have: http://melpon.org/wandbox/permlink/RDl3B6EmnG5hRuAJ
After re-reading your post, I think this doesn't meet your requirement :p
The DEFINE_FLAGS macro is indeed unfortunate :) Niall -- ned Productions Limited Consulting http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/
Le 03/02/2016 10:01, Niall Douglas a écrit :
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. TBoost.Enums [1] has an alternative design, given an ordinal enum E we can create a enum_set<E> [2] that behaves like a bitset<N>, but we can use the enumerators instead of the position. I'm not saying it is better or worst, I'm just giving the pointer here in case this can help someone.
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); } };
What is the bit above? what is the decltype of bit(0)? Could flag store bit(5)?
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.
Yes, I believe we need something like that. Vicente [1] https://htmlpreview.github.io/?https://github.com/viboes/enums/blob/master/l... [2] https://htmlpreview.github.io/?https://github.com/viboes/enums/blob/master/l... P.S. This library is an old one that needs to be refactored to take advantage of C++14 and is not ready for review :(.
Le 04/02/2016 00:47, Vicente J. Botet Escriba a écrit :
Le 03/02/2016 10:01, Niall Douglas a écrit :
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); } };
What is the bit above? what is the decltype of bit(0)? Could flag store bit(5)? Please Niall, could you answer to these questions?
Vicente
On 6 Feb 2016 at 10:27, Vicente J. Botet Escriba wrote:
What is the bit above? what is the decltype of bit(0)? Could flag store bit(5)? Please Niall, could you answer to these questions?
I'd have thought the answer very obvious from https://gist.github.com/ned14/89ee39c6b8eb5254116a? Anyway I ended up choosing https://gist.github.com/ned14/614d8f3cca25924964ea instead which works a treat. BTW Vicente do you want to do anything about the nightly build failures of Boost.Thread on Windows and Windows Phone? Or should I disable those tests? I appreciate it isn't your fault, it's Boost.TypeTraits causing an internal compiler error. Interesting folk may view https://ci.nedprod.com/view/Boost%20Thread-Expected-Permit/job/Boost.T hread%20Build/ (look for the red dots!). Niall -- ned Productions Limited Consulting http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/
Le 06/02/2016 12:00, Niall Douglas a écrit :
On 6 Feb 2016 at 10:27, Vicente J. Botet Escriba wrote:
What is the bit above? what is the decltype of bit(0)? Could flag store bit(5)? Please Niall, could you answer to these questions? I'd have thought the answer very obvious from https://gist.github.com/ned14/89ee39c6b8eb5254116a? Really sorry. I missed the static bit function. I have the answers now.
Vicente
Le 06/02/2016 12:00, Niall Douglas a écrit :
BTW Vicente do you want to do anything about the nightly build failures of Boost.Thread on Windows and Windows Phone? Or should I disable those tests?
I appreciate it isn't your fault, it's Boost.TypeTraits causing an internal compiler error. Interesting folk may view https://ci.nedprod.com/view/Boost%20Thread-Expected-Permit/job/Boost.T hread%20Build/ (look for the red dots!).
I don't reach to see where the error is reported :( Vicente
On 6 Feb 2016 at 12:28, Vicente J. Botet Escriba wrote:
BTW Vicente do you want to do anything about the nightly build failures of Boost.Thread on Windows and Windows Phone? Or should I disable those tests?
I appreciate it isn't your fault, it's Boost.TypeTraits causing an internal compiler error. Interesting folk may view https://ci.nedprod.com/view/Boost%20Thread-Expected-Permit/job/Boost.T hread%20Build/ (look for the red dots!).
I don't reach to see where the error is reported :(
Can you see https://ci.nedprod.com? I know it's been down since 15th December until last weekend. Since I returned to full time employment free time has been extremely limited, I could only work on the problem a few hours per week. But it should be accessible to everyone now. Niall -- ned Productions Limited Consulting http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/
Le 06/02/2016 12:33, Niall Douglas a écrit :
On 6 Feb 2016 at 12:28, Vicente J. Botet Escriba wrote:
BTW Vicente do you want to do anything about the nightly build failures of Boost.Thread on Windows and Windows Phone? Or should I disable those tests?
I appreciate it isn't your fault, it's Boost.TypeTraits causing an internal compiler error. Interesting folk may view https://ci.nedprod.com/view/Boost%20Thread-Expected-Permit/job/Boost.T hread%20Build/ (look for the red dots!).
I don't reach to see where the error is reported :( Can you see https://ci.nedprod.com? yes. and I was able to see the previous link. The problem is that I don't know where the error is reported. Following the red dots didn't helped me.
E.g. this link https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=clang++-3.3,L... doesn't gives any information about where the error is. Please, could you gibe me the exact pointer where the errors are reported. BTW, the Boost develop regression tests don't show any significant failure since the 15th December.
I know it's been down since 15th December until last weekend.
Vicente
On 6 Feb 2016 at 13:02, Vicente J. Botet Escriba wrote:
I appreciate it isn't your fault, it's Boost.TypeTraits causing an internal compiler error. Interesting folk may view https://ci.nedprod.com/view/Boost%20Thread-Expected-Permit/job/Boost.T hread%20Build/ (look for the red dots!).
I don't reach to see where the error is reported :( Can you see https://ci.nedprod.com? yes. and I was able to see the previous link. The problem is that I don't know where the error is reported. Following the red dots didn't helped me.
E.g. this link https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=clang++-3.3,L...
doesn't gives any information about where the error is.
Boost.Thread on FreeBSD clang 3.3 is all good, that is why there is
no error.
Try this instead:
https://ci.nedprod.com/view/Boost%20Thread-Expected-Permit/job/Boost.Thread%...
Error is:
compile-c-c++
bin.v2\libs\thread\build\msvc-12.0\release\link-static\threading-multi
\win32\thread.obj
thread.cpp
.\boost/type_traits/detail/mp_defer.hpp(50) : fatal error C1001: An
internal error has occurred in the compiler.
(compiler file 'f:\dd\vctools\compiler\cxxfe\sl\p1\c\p1types.cpp',
line 192)
To work around this problem, try simplifying or changing the program
near the locations listed above.
Please choose the Technical Support command on the Visual C++
Help menu, or open the Technical Support help file for more
information
.\boost/type_traits/common_type.hpp(49) : see reference to
class template instantiation 'boost::common_type
Please, could you gibe me the exact pointer where the errors are reported. BTW, the Boost develop regression tests don't show any significant failure since the 15th December.
They may not be testing VS2013 in the configuration I am. Niall -- ned Productions Limited Consulting http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/
Le 06/02/2016 15:26, Niall Douglas a écrit :
On 6 Feb 2016 at 13:02, Vicente J. Botet Escriba wrote:
I appreciate it isn't your fault, it's Boost.TypeTraits causing an internal compiler error. Interesting folk may view https://ci.nedprod.com/view/Boost%20Thread-Expected-Permit/job/Boost.T hread%20Build/ (look for the red dots!).
I don't reach to see where the error is reported :( Can you see https://ci.nedprod.com? yes. and I was able to see the previous link. The problem is that I don't know where the error is reported. Following the red dots didn't helped me.
E.g. this link https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=clang++-3.3,L...
doesn't gives any information about where the error is. Boost.Thread on FreeBSD clang 3.3 is all good, that is why there is no error.
Try this instead:
https://ci.nedprod.com/view/Boost%20Thread-Expected-Permit/job/Boost.Thread%...
Error is:
compile-c-c++ bin.v2\libs\thread\build\msvc-12.0\release\link-static\threading-multi \win32\thread.obj thread.cpp .\boost/type_traits/detail/mp_defer.hpp(50) : fatal error C1001: An internal error has occurred in the compiler. (compiler file 'f:\dd\vctools\compiler\cxxfe\sl\p1\c\p1types.cpp', line 192) To work around this problem, try simplifying or changing the program near the locations listed above. Please choose the Technical Support command on the Visual C++ Help menu, or open the Technical Support help file for more information .\boost/type_traits/common_type.hpp(49) : see reference to class template instantiation 'boost::common_type
' being compiled The same cause is failing the Windows Phone builds - an internal compiler error. Have you reported the error to MS? Could you create a Trac ticket for type_traits? Do you have an idea on how to fix it?
Only VS2013 is affected. VS2015, VS2012 and VS2010 are okay.
Please, could you gibe me the exact pointer where the errors are reported. BTW, the Boost develop regression tests don't show any significant failure since the 15th December. They may not be testing VS2013 in the configuration I am.
Right. Vicente
The same cause is failing the Windows Phone builds - an internal compiler error. Have you reported the error to MS? Could you create a Trac ticket for type_traits? Do you have an idea on how to fix it?
Only VS2013 is affected. VS2015, VS2012 and VS2010 are okay.
Please, could you gibe me the exact pointer where the errors are reported. BTW, the Boost develop regression tests don't show any significant failure since the 15th December. They may not be testing VS2013 in the configuration I am.
Is this the same as https://svn.boost.org/trac/boost/ticket/11885 or https://svn.boost.org/trac/boost/ticket/11924 ? These are addressed by Update 5 to VC12. HTH, John.
On 6 Feb 2016 at 17:07, John Maddock wrote:
These are addressed by Update 5 to VC12.
Update 5 applied. We shall see how it fares. Thanks for the tip. Niall -- ned Productions Limited Consulting http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/
On 6 Feb 2016 at 20:27, Niall Douglas wrote:
These are addressed by Update 5 to VC12.
Update 5 applied. We shall see how it fares. Thanks for the tip.
Build on native codegen now all working, thanks. Windows Phone/Metro is still broken unfortunately. I have little experience in that target so I can't say as to the cause, but it could be the tooling is less permissive than before so Boost.Build etc needs upgrading to match. https://ci.nedprod.com/view/Boost%20Thread-Expected-Permit/job/Boost.T hread%20Build/CPPSTD=c++14,CXX=msvc-12.0,LINKTYPE=shared,label=winphon e8/495/console https://ci.nedprod.com/view/Boost%20Thread-Expected-Permit/job/Boost.T hread%20Build/CPPSTD=c++14,CXX=msvc-14.0,LINKTYPE=shared,label=winphon e8/495/console Niall -- ned Productions Limited Consulting http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/
On 07/02/2016 11:35, Niall Douglas wrote:
On 6 Feb 2016 at 20:27, Niall Douglas wrote:
These are addressed by Update 5 to VC12. Update 5 applied. We shall see how it fares. Thanks for the tip. Build on native codegen now all working, thanks.
Windows Phone/Metro is still broken unfortunately. I have little experience in that target so I can't say as to the cause, but it could be the tooling is less permissive than before so Boost.Build etc needs upgrading to match.
https://ci.nedprod.com/view/Boost%20Thread-Expected-Permit/job/Boost.T hread%20Build/CPPSTD=c++14,CXX=msvc-12.0,LINKTYPE=shared,label=winphon e8/495/console
What am I looking at, where's the error?
https://ci.nedprod.com/view/Boost%20Thread-Expected-Permit/job/Boost.T hread%20Build/CPPSTD=c++14,CXX=msvc-14.0,LINKTYPE=shared,label=winphon e8/495/console
Could this be related to https://social.msdn.microsoft.com/Forums/en-US/d8980415-9542-475e-8da9-1a4ef... ? Best, John.
On 7 Feb 2016 at 17:02, John Maddock wrote:
Windows Phone/Metro is still broken unfortunately. I have little experience in that target so I can't say as to the cause, but it could be the tooling is less permissive than before so Boost.Build etc needs upgrading to match.
https://ci.nedprod.com/view/Boost%20Thread-Expected-Permit/job/Boost.T hread%20Build/CPPSTD=c++14,CXX=msvc-12.0,LINKTYPE=shared,label=winphon e8/495/console
What am I looking at, where's the error?
Yes you're right, not an error but some warnings. I have configured Jenkins to send an email every night if there are any warnings too, and the warnings reports above look to be valid and the cause is Boost.Thread's unit tests missing some WinRT specific markup.
https://ci.nedprod.com/view/Boost%20Thread-Expected-Permit/job/Boost.T hread%20Build/CPPSTD=c++14,CXX=msvc-14.0,LINKTYPE=shared,label=winphon e8/495/console
Could this be related to https://social.msdn.microsoft.com/Forums/en-US/d8980415-9542-475e-8da9-1a4ef... ?
Looks very plausible. Apparently according to that Boost.Build needs to stop specifying /ZW on Windows Phone or something? Niall -- ned Productions Limited Consulting http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/
Firstly I'd like to thank Vicente and Tongari for their ideas as without their feedback I wouldn't have come up with https://gist.github.com/ned14/614d8f3cca25924964ea which combines ideas from all three of us. I'm relatively happy with this solution. Declaration is as follows: BOOST_AFIO_BITFIELD_BEGIN(flag) { none=0, delete_on_close=1, disable_safety_fsyncs=2 } BOOST_AFIO_BITFIELD_END(flag) Example usage: 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); if(f) ... This solution is convenient for programmers to use, works as you'd expect from a bitfield, if(bf) works and is 100% constexpr and generates zero runtime overhead, and it's ODR-used correct. The tradeoff is unfortunately now individual flag::XXX are not typesafe and have implicit convertibility to int, though 'flag' itself and any output of any operation on 'flag' is typesafe. Also, there is an ugly macro begin and end of the declaration, but I think this is the best typesafe bitfield I can think of which has a syntax resembling C bitfields. Any comments absolutely welcome. Niall -- ned Productions Limited Consulting http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/
Le 04/02/2016 16:19, Niall Douglas a écrit :
Firstly I'd like to thank Vicente and Tongari for their ideas as without their feedback I wouldn't have come up with https://gist.github.com/ned14/614d8f3cca25924964ea which combines ideas from all three of us.
I'm relatively happy with this solution. Declaration is as follows:
BOOST_AFIO_BITFIELD_BEGIN(flag) { none=0, delete_on_close=1, disable_safety_fsyncs=2 } BOOST_AFIO_BITFIELD_END(flag)
Example usage:
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); if(f) ... Yes, this is much better. This solution is convenient for programmers to use, works as you'd expect from a bitfield, if(bf) works and is 100% constexpr and generates zero runtime overhead, and it's ODR-used correct. The tradeoff is unfortunately now individual flag::XXX are not typesafe and have implicit convertibility to int, though 'flag' itself and any output of any operation on 'flag' is typesafe. Also, there is an ugly macro begin and end of the declaration, but I think this is the best typesafe bitfield I can think of which has a syntax resembling C bitfields.
Any comments absolutely welcome.
C/C++ bit fields means already something else
(http://en.cppreference.com/w/cpp/language/bit_field).
I will name the utility in some other way.
Do you see something wrong having two classes one for the bits enum and
one for the set of those bits enums?
Mixing both, the element and the set in a single class, has some
drawbacks. The flag type is not an enum, that is, is_enum<flag>::value
is false.
I don't see the added value of the macros and the inheritance
#define BOOST_AFIO_BITFIELD_BEGIN(type) \
struct type##_base \
{ \
enum enum_type
#define BOOST_AFIO_BITFIELD_END(type) \
;}; \
using type = bitfield
On 6 Feb 2016 at 12:00, Vicente J. Botet Escriba wrote:
Do you see something wrong having two classes one for the bits enum and one for the set of those bits enums? Mixing both, the element and the set in a single class, has some drawbacks. The flag type is not an enum, that is, is_enum<flag>::value is false.
Tongari's solution split them as you describe. I'm not against that solution, it's just a distinction I need to care about during using them and I don't want to care. I just want C bitfields which don't accidentally substitute for integers and therefore lead to bugs.
I don't see the added value of the macros and the inheritance
#define BOOST_AFIO_BITFIELD_BEGIN(type) \ struct type##_base \ { \ enum enum_type
#define BOOST_AFIO_BITFIELD_END(type) \ ;}; \ using type = bitfield
; template<class Enum> struct bitfield : public Enum
Why do you need to inherit from Enum? to introduce the enumerators inside the defined type?
Exactly. I'm effectively wrapping an enum into a typesafe struct with member functions.
What about
template<class Enum> struct bitfield_set { using enum_type = Enum; using underlying_type = std::underlying_type_t
; private: underlying_type _value; ...
enum class flag = unsigned { none=0, delete_on_close=1, disable_safety_fsyncs=2 };
using flags = bitfield_set<flag>;
Example usage:
flags f(flag::disable_safety_fsyncs); flags f2(f&flag::none); f2|=flag::delete_on_close; constexpr flags f3(flag::disable_safety_fsyncs); constexpr flags f4(f3&flag::none); if(f) ...
That's absolutely fine if you don't mind distinct types for the enumerators and the flags. For me personally, I find myself typing the wrong one in my code, and that's a source of compile errors and therefore wasted productivity. What is very annoying is that global operator overloads on a class enum gets you 98% of the way there. If only one could overload a global explicit bool operator ... we'd have our typesafe bit flags type. Niall -- ned Productions Limited Consulting http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/
Le 06/02/2016 12:54, Niall Douglas a écrit :
On 6 Feb 2016 at 12:00, Vicente J. Botet Escriba wrote:
Do you see something wrong having two classes one for the bits enum and one for the set of those bits enums? Mixing both, the element and the set in a single class, has some drawbacks. The flag type is not an enum, that is, is_enum<flag>::value is false. Tongari's solution split them as you describe.
I'm not against that solution, it's just a distinction I need to care about during using them and I don't want to care. I just want C bitfields which don't accidentally substitute for integers and therefore lead to bugs. I understand you, however I believe that the intent is better reflected with a type for elements and a a type for the set of elements.
I don't see the added value of the macros and the inheritance
#define BOOST_AFIO_BITFIELD_BEGIN(type) \ struct type##_base \ { \ enum enum_type
#define BOOST_AFIO_BITFIELD_END(type) \ ;}; \ using type = bitfield
; template<class Enum> struct bitfield : public Enum
Why do you need to inherit from Enum? to introduce the enumerators inside the defined type? Exactly. I'm effectively wrapping an enum into a typesafe struct with member functions. This makes your bitfield class implementation defined as too dependent.
What about
template<class Enum> struct bitfield_set { using enum_type = Enum; using underlying_type = std::underlying_type_t
; private: underlying_type _value; ...
enum class flag = unsigned { none=0, delete_on_close=1, disable_safety_fsyncs=2 };
using flags = bitfield_set<flag>;
Example usage:
flags f(flag::disable_safety_fsyncs); flags f2(f&flag::none); f2|=flag::delete_on_close; constexpr flags f3(flag::disable_safety_fsyncs); constexpr flags f4(f3&flag::none); if(f) ... That's absolutely fine if you don't mind distinct types for the enumerators and the flags. For me personally, I find myself typing the wrong one in my code, and that's a source of compile errors and therefore wasted productivity. Have you tried to name it flag_set instead of flags? Could you give some examples? What is very annoying is that global operator overloads on a class enum gets you 98% of the way there. If only one could overload a global explicit bool operator ... we'd have our typesafe bit flags type.
This is not the case for scoped enums, or at least not completely as scoped enums constructor is explicit, isn't it? Vicente
On Sat, Feb 6, 2016 at 4:54 PM, Vicente J. Botet Escriba
Le 06/02/2016 12:54, Niall Douglas a écrit :
On 6 Feb 2016 at 12:00, Vicente J. Botet Escriba wrote:
Do you see something wrong having two classes one for the bits enum and one for the set of those bits enums? Mixing both, the element and the set in a single class, has some drawbacks. The flag type is not an enum, that is, is_enum<flag>::value is false.
Tongari's solution split them as you describe.
I'm not against that solution, it's just a distinction I need to care about during using them and I don't want to care. I just want C bitfields which don't accidentally substitute for integers and therefore lead to bugs.
I understand you, however I believe that the intent is better reflected with a type for elements and a a type for the set of elements.
I agree. I've even had a few places where I need to specifically state "this type is _one_ of the bits" vs "this type is a few of the bits". It is the difference between container and element. Qt uses separate types. Not sure if that was for intent, or just the best way they could implement it at the time, but I've grown to like it that way. Tony
"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());
Well, you could still pass flag::none - the function address - to
flag::operator&().
But of course you could then pass any function - with the same signature -
so none() might need a signature like none(some_special_type unused = 0) to
prevent mistaken misuse.
Does that help?
Tony
------------------------------
*From:* "Niall Douglas"
On 5 Feb 2016 at 4:50, Gottlob Frege wrote:
Well, you could still pass flag::none - the function address - to flag::operator&(). But of course you could then pass any function - with the same signature - so none() might need a signature like none(some_special_type unused = 0) to prevent mistaken misuse.
Does that help?
I thought of this too (specifically some_special_type<bits set for this flag>), but I realised I was getting into metaprogramming land. I'm sure a "perfect" typesafe bitfield can finally be implemented as of C++ 14, but I think the likely implementation should be submitted as a N-paper to WG21 as an excellent example of what needs to be fixed in the C++ language. After all, typesafe bitfields ought to in a systems programming language! A conference talk on building one of these might be very interesting ... nudge nudge! :) Niall -- ned Productions Limited Consulting http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/
Well, you could still pass flag::none - the function address - to flag::operator&(). But of course you could then pass any function - with the same signature
Gottlob Frege sayeth: -
so none() might need a signature like none(some_special_type unused = 0) to prevent mistaken misuse.
Does that help?
Nial Douglass respondeth:
I thought of this too (specifically some_special_type<bits set for this flag>), but I realised I was getting into metaprogramming land.
I'm sure a "perfect" typesafe bitfield can finally be implemented as of C++ 14, but I think the likely implementation should be submitted as a N-paper to WG21 as an excellent example of what needs to be fixed in the C++ language. After all, typesafe bitfields ought to in a systems programming language!
A conference talk on building one of these might be very interesting ... nudge nudge! :)
+1 A "perfect typesafe bitfield" is very high value, and would make a great conference talk. --charley
On Fri, Feb 5, 2016 at 4:29 AM, Niall Douglas
On 5 Feb 2016 at 4:50, Gottlob Frege wrote:
Well, you could still pass flag::none - the function address - to flag::operator&(). But of course you could then pass any function - with the same signature - so none() might need a signature like none(some_special_type unused = 0) to prevent mistaken misuse.
Does that help?
I thought of this too (specifically some_special_type<bits set for this flag>), but I realised I was getting into metaprogramming land.
I'm sure a "perfect" typesafe bitfield can finally be implemented as of C++ 14, but I think the likely implementation should be submitted as a N-paper to WG21 as an excellent example of what needs to be fixed in the C++ language. After all, typesafe bitfields ought to in a systems programming language!
A conference talk on building one of these might be very interesting ... nudge nudge! :)
Your last nudge nudge was basically "someone should solve the thread-safe listener / observer-pattern and present it", and I've fallen for that one already, don't make me do another. You can have bitfields.
Niall
-- ned Productions Limited Consulting http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
participants (6)
-
charleyb123 .
-
Gottlob Frege
-
John Maddock
-
Niall Douglas
-
TONGARI J
-
Vicente J. Botet Escriba