Boost logo

Boost :

Subject: Re: [boost] Is there any interest in type-safe container of bool flags with noexcept guarantees?
From: Andrey Semashev (andrey.semashev_at_[hidden])
Date: 2017-03-22 22:40:00


On 03/22/17 19:02, Roman Orlov via Boost wrote:
> On 21.03.2017 21:23, Steven Watanabe via Boost wrote:
>>
>> what's wrong with
>> struct animal {
>> bool eats_meat : 1;
>> bool eats_grass : 1;
>> bool has_tail : 1;
>> };
>>
>
> There are several reasons I don't like that approach
>
> While declaring a variable of type 'animal' you should always keep in
> mind to initialize it with empty initializer
> animal a1; // bit fields are not initialized
> animal a2{}; // now it's ok, bits are set to zeros
>
> When you want to initialize some fields (eats_grass, has_tail) you have
> to write several lines of code
> animal a1{};
> a1.eats_grass = 1;
> a1.has_tail = 1;
>
> Of course, it can be done in one line with initializer list
> animal a1{0, 1, 1};
>
> But we don't write a code ones, we are about to support it for a long
> time. One day we decided to add a new bit field to structure
> struct animal {
> bool can_fly : 1; // yeah, somebody puts it on the first position
> bool eats_meat : 1;
> bool eats_grass : 1;
> bool has_tail : 1;
> };
>
> What will happen with all of list initializers? There will be bugs we
> can't detect at compile time.
> animal a1{0, 1, 1}; // now it doesn't work properly
>
> We have to find all of such initializers and rewrite them
> animal a1{0, 0, 1, 1};

Frankly, all the above problems are solved with constructors.

The only real problem I see with the struct approach is that it's
lacking expressiveness. From this declaration:

   animal a1{0, 1, 1};

it is difficult to immediately say what the value of has_tail will be.
It would be better if we had named field initialization, like in C, but
alas.

Usually, I solve this problem with std::bitset with an enum providing
names for the flags.

   enum animal_traits
   {
     eats_meat, eats_grass, has_tail, _count
   };
   typedef std::bitset< animal_traits::_count > animal;

   animal a1; // all traits are false
   a1[eats_grass] = 1;
   a1[has_tail] = 1;

I think, this provides the necessary expressiveness and performance. I
guess, your proposal provides better type safety than this solution, but
I'm not sure the improvement is significant enough to outweigh the more
complicated syntax. For example, one could write a slightly more
complicated version of the above:

   enum animal_traits
   {
     eats_meat, eats_grass, has_tail, _count
   };

   template< typename Enum, unsigned int Count >
   class typed_bitset :
     public std::bitset< Count >
   {
   public:
     bool operator[] (Enum idx) const
     {
       return std::bitset< Count >::operator[](idx);
     }
   };

   typedef typed_bitset< animal_traits, animal_traits::_count > animal;

   animal a1; // all traits are false
   a1[eats_grass] = 1;
   a1[has_tail] = 1;
   a1[10] = 1; // error

I suspect it would also be faster to compile.


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