Quantcast

Is there any interest in type-safe container of bool flags with noexcept guarantees?

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
14 messages Options
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Is there any interest in type-safe container of bool flags with noexcept guarantees?

Boost - Dev mailing list

Hello Boost community,
I'd like to determine interest in this kind of container.
Motivation
For managing sets of bool flags developers often use std::bitset or c-like
manual bit manipulations. Each approach brings problems.
Some disadvantages of using std::bitset:
* unpredictable and greedy allocation of bit storage;
* setters/getters can throw exceptions;
* it operates in low-level terms like "a bit number";
* we can easily assign one instance of std::bitset to another with the same
number of bits but semantically they may differ.
Proposal
Instead of managing bits via numbers let's manage them via types.
So start by declaring some types
class eats_meat;
class eats_grass;
class has_tail;
Then bind these types to flag identifiers
typedef typed_flags<eats_meat, eats_grass, has_tail> animal;

Unlike std::bitset the 'animal' instance allocates a minimal possible storage
(in this case sizeof(animal) == 1).
Create flags from scratch
animal wolf;
wolf.set<eats_grass>(false);
wolf.set<eats_meat, has_tail>();
wolf.set(flag<has_tail>{1}, flag<eats_meat>{1});
Create flags with a flexible human-readable constructor
wolf = animal{flag<has_tail>{1}, flag<eats_meat>{1}, flag<eats_grass>{0}};

I have implemented a minimal working prototype in my github repo
https://github.com/compmaniak/typed_flags
If you're interested you can try it online here
http://melpon.org/wandbox/permlink/55g1czUjwSFO8LS1
Expected benefits
* delegating calculation of a bit position for a certain type to compiler
gives us a strong type safety and prevents typos;
* noexcept guarantee of setting and getting bit values;
* we shouldn't take care of bits because we work in terms of typed properties;
* great readability, taking a short glance on a type definition tells you what
this type is about.

Some people on reddit (https://redd.it/601upg) found this library useful.
Any thoughts on the idea of the container would be much appreciated. --
Roman

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Is there any interest in type-safe container of bool flags with noexcept guarantees?

Boost - Dev mailing list
AMDG

On 03/21/2017 10:31 AM, Роман Орлов via Boost wrote:
>
> <snip>
> class eats_meat;
> class eats_grass;
> class has_tail;
> Then bind these types to flag identifiers
> typedef typed_flags<eats_meat, eats_grass, has_tail> animal;
>

what's wrong with
struct animal {
  bool eats_meat : 1;
  bool eats_grass : 1;
  bool has_tail : 1;
};

In Christ,
Steven Watanabe


_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Is there any interest in type-safe container of bool flags with noexcept guarantees?

Boost - Dev mailing list

> On 21 Mar 2017, at 19:23, Steven Watanabe via Boost <[hidden email]> wrote:
>
> AMDG
>
> On 03/21/2017 10:31 AM, Роман Орлов via Boost wrote:
>>
>> <snip>
>> class eats_meat;
>> class eats_grass;
>> class has_tail;
>> Then bind these types to flag identifiers
>> typedef typed_flags<eats_meat, eats_grass, has_tail> animal;
>>
>
> what's wrong with
> struct animal {
>  bool eats_meat : 1;
>  bool eats_grass : 1;
>  bool has_tail : 1;
> };
>

To me standard C bitfields fit the bill, and is what I will use as long as I do not need to do bitwise operations on the flags.  For just setting and reading them, it works fine and is simple to use.  Beyond that, allthough outside this topic I really wish the language had the ability to take better control of the binary representation and layout of C bitfields for binary data portability.  


Bjørn



_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Is there any interest in type-safe container of bool flags with noexcept guarantees?

Boost - Dev mailing list
In reply to this post by Boost - Dev mailing list
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};

To prevent this I propose to abstract from strict ordering and use
typed entities
   animal a1{flag<eats_grass>{1}, flag<has_tail>{1}};

And what about bitwise operations on plain structures? While working
with bool properties conjunction and disjunction are needed fairly
often. For each structure you'll have to implement them manually.
Of course it's better to have it out of the box
   auto a1 = animal{flag<eats_gass>{1}} | animal{flag<eats_meat>{1}};

Sometimes it's needed to test that all flags are set or at least one.
And again for each structure you'll have to implement it manually.
It's better to have these functions in container
   auto a1 = animal{flag<eats_gass>{1}};
   assert( (!a1.all()) );
   assert( (!a1.any<eats_meat, has_tail>()) ); // with some syntax sugar

Managing typed entities allows you to test common properties of
semantically different entities. For example:
   class p1;
   class p2;
   class p3;
   class p4;

   typedef typed_flags<p1, p2, p3> a; // has p3 property
   typedef typed_flags<p3, p4> b; // has p3 property too

   template<typename T, typename... Args>
   bool test_p(Args&&... args) {
     return (... && args.template test<T>());
   }
   // test p3 property from a and b
   test_p<p3>(a{flag<p3>{1}}, b{flag<p3>{1}});

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Is there any interest in type-safe container of bool flags with noexcept guarantees?

Boost - Dev mailing list
I agree that bitfields are not the way to go. They suffer from a number of
deficiencies - not least that each bit is *not a separate object* so they
are problematic i multithreaded environments. They also of course carry no
type information.

However, I am struggling to see how the proposed class is any more useful
than a std::tuple classes that support a bool conversion operator.

Can you demonstrate a use case where a tuple is inadequate?


On 22 March 2017 at 17:02, Roman Orlov via Boost <[hidden email]>
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};
>
> To prevent this I propose to abstract from strict ordering and use
> typed entities
>   animal a1{flag<eats_grass>{1}, flag<has_tail>{1}};
>
> And what about bitwise operations on plain structures? While working
> with bool properties conjunction and disjunction are needed fairly
> often. For each structure you'll have to implement them manually.
> Of course it's better to have it out of the box
>   auto a1 = animal{flag<eats_gass>{1}} | animal{flag<eats_meat>{1}};
>
> Sometimes it's needed to test that all flags are set or at least one.
> And again for each structure you'll have to implement it manually.
> It's better to have these functions in container
>   auto a1 = animal{flag<eats_gass>{1}};
>   assert( (!a1.all()) );
>   assert( (!a1.any<eats_meat, has_tail>()) ); // with some syntax sugar
>
> Managing typed entities allows you to test common properties of
> semantically different entities. For example:
>   class p1;
>   class p2;
>   class p3;
>   class p4;
>
>   typedef typed_flags<p1, p2, p3> a; // has p3 property
>   typedef typed_flags<p3, p4> b; // has p3 property too
>
>   template<typename T, typename... Args>
>   bool test_p(Args&&... args) {
>     return (... && args.template test<T>());
>   }
>   // test p3 property from a and b
>   test_p<p3>(a{flag<p3>{1}}, b{flag<p3>{1}});
>
>
> _______________________________________________
> Unsubscribe & other changes: http://lists.boost.org/mailman
> /listinfo.cgi/boost
>

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Is there any interest in type-safe container of bool flags with noexcept guarantees?

Boost - Dev mailing list
On 22.03.2017 20:14, Richard Hodges via Boost wrote:

> I agree that bitfields are not the way to go. They suffer from a number of
> deficiencies - not least that each bit is *not a separate object* so they
> are problematic i multithreaded environments. They also of course carry no
> type information.
>
> However, I am struggling to see how the proposed class is any more useful
> than a std::tuple classes that support a bool conversion operator.
>
> Can you demonstrate a use case where a tuple is inadequate?
>
std::tuple and proposed class typed_flags are completely different.
Via std::tuple you can emulate typed bit storage like this

   class p1; //
   class p2; // incomplete types
   class p3; //

   template<typename T>
   struct bit {
     unsigned char value;
   };

   std::tuple<bit<p1>, bit<p2>, bit<p3>> b1;
   assert( sizeof(b1) == 3 );

std::tuple can be parameterized only with *complete* types because it
stores instances of specified types. Thus in this example the
size of b1 is 3 bytes. We can't write std::tuple in this way

   std::tuple<p1, p2, p3> b2;

typed_flags can be parameterized with *incomplete* types because types
are used as tags for identifying positions of corresponding bits.

   typed_flags<p1, p2, p3> b3;
   assert( sizeof(b3) == 1 );

Actual bit values are stored in std::array<uint8_t, N> where N is
calculated depending on the number of template parameters.
So typed_flags is a templated frontend to the raw bit storage.
When we test a certain flag (or bit)

   b3.test<p2>();

the type p2 is resolved to the item in the backing array and offset
inside the item. typed_flags API is similar to std::bitset.

I can't post verbose examples here.
For better understanding, please, take a look at my repo
https://github.com/compmaniak/typed_flags

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Is there any interest in type-safe container of bool flags with noexcept guarantees?

Boost - Dev mailing list
In reply to this post by Boost - Dev mailing list
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.


_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Is there any interest in type-safe container of bool flags with noexcept guarantees?

Boost - Dev mailing list
In reply to this post by Boost - Dev mailing list
On 03/22/17 20:14, Richard Hodges via Boost wrote:
> I agree that bitfields are not the way to go. They suffer from a number of
> deficiencies - not least that each bit is *not a separate object* so they
> are problematic i multithreaded environments. They also of course carry no
> type information.

Thread safety is a red herring since none of the solutions presented in
this discussion are thread safe.

> However, I am struggling to see how the proposed class is any more useful
> than a std::tuple classes that support a bool conversion operator.
>
> Can you demonstrate a use case where a tuple is inadequate?

std::tuple does not compress bits, which is what std::bitset and
bitfields do.

Also, please, don't top-post. See the discussion policy here:

http://www.boost.org/community/policy.html


_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Is there any interest in type-safe container of bool flags with noexcept guarantees?

Boost - Dev mailing list
In reply to this post by Boost - Dev mailing list
Le 22/03/2017 à 17:02, Roman Orlov via Boost a écrit :

Hi,

I like the idea.

> 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;
>> };
>>
>
Steven is right, your library is kind of a library reflecting the
previous structure.
As we don't have yet reflection, we need tag types to identify the fields.
I would expect that once we have reflection, we could provide a bit mask
interface for the previous structure. But we don't have it yet.

> 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
>
I believe that this is good, as you could use it as member of POD
structures.
While I agree that it is good to initialize everything as soon as
possible, some times been able to don't initialize is the correct
answer. This is C++.
> 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;
>
Well I would replace 1 by true ;-)
I find this interface clear.
> Of course, it can be done in one line with initializer list
>   animal a1{0, 1, 1};
>
I agree that positional interfaces don't scale well as you show below.

> 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};
>
> To prevent this I propose to abstract from strict ordering and use
> typed entities
>   animal a1{flag<eats_grass>{1}, flag<has_tail>{1}};
>
> And what about bitwise operations on plain structures? While working
> with bool properties conjunction and disjunction are needed fairly
> often. For each structure you'll have to implement them manually.
> Of course it's better to have it out of the box
>   auto a1 = animal{flag<eats_gass>{1}} | animal{flag<eats_meat>{1}};
>
Have you considered to use tag classes that provide this kind of flag
arithmetic?
We write the fftag once, but we use them much more times.

   auto a1 = animal{eats_gass{1}} | animal{eats_meat{1}};

The definition of a tag is not complex

struct eats_gass : flag<eats_gass> {
     using flag<eats_gass>::flag<eats_gass>;
};

BTW, why do we need an argument to the flag class
Why
   auto a1 = animal{flag<eats_gass>{1}} | animal{flag<eats_meat>{1}};

and not simply

   auto a1 = animal{flag<eats_gass>{}} | animal{flag<eats_meat>{}};
> Sometimes it's needed to test that all flags are set or at least one.
> And again for each structure you'll have to implement it manually.
> It's better to have these functions in container
>   auto a1 = animal{flag<eats_gass>{1}};
>   assert( (!a1.all()) );
>   assert( (!a1.any<eats_meat, has_tail>()) ); // with some syntax sugar
If you follow bit_set interface it should be

   assert( (!a1.any()) );

The last could be

   assert( (!a1.any_of<eats_meat, has_tail>()) );

You could have also compile time flags that don't have storage. The
previous any could be as well

   assert( (! (a1 & literal_typed_flag<eats_meat, has_tail>{}) ));

BTW, the construction could be done using these literals

   auto a1 = literal_typed_flag<eats_gass, eats_meat>;

literal_typed_flag could be convertible to any typed_flag that contains
at least those tags.

While I'm for the member functions in your case, if we had compile time
reflection we should use non-member function (at least until we have the
possibility to create new types)


>
> Managing typed entities allows you to test common properties of
> semantically different entities. For example:
>   class p1;
>   class p2;
>   class p3;
>   class p4;
>
>   typedef typed_flags<p1, p2, p3> a; // has p3 property
>   typedef typed_flags<p3, p4> b; // has p3 property too
>
>   template<typename T, typename... Args>
>   bool test_p(Args&&... args) {
>     return (... && args.template test<T>());
>   }
>   // test p3 property from a and b
>   test_p<p3>(a{flag<p3>{1}}, b{flag<p3>{1}});
Here we see that having non-member functions could help avoiding the
not-friendly x.template test<T>

   template<typename T, typename... Args>
   bool test_p(Args&&... args) {
     return (... && test<T>(args));
   }


How typed_flags convert? Can typed_flags<a,b> be convertible to
typed_flags<a,b,c> or typed_flags<a,b,c>?

It could also be interesting to look at the tags proposed in the Range
TS to see if an adaptation could simplify the user interface.

HTH,
Vicente


_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Is there any interest in type-safe container of bool flags with noexcept guarantees?

Boost - Dev mailing list
In reply to this post by Boost - Dev mailing list
Le 22/03/2017 à 23:40, Andrey Semashev via Boost a écrit :

> 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);
>     }
>   };
>
I have an ordinal_set<Ordinal> that is type safe.
An enum as the previous one canbe see as an Ordinal and so

     enum animal_traits
     {
       eats_meat, eats_grass, has_tail, _count
     };
     using animal_traits_set = ordinal_set<animal_traits>

>   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.
compile time for ordinal sets would suffer a little bit as we need to
associate at compile time each enumerator to its position (this could be
provided by reflection or by user defined ordinal traits).

Vicente

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Is there any interest in type-safe container of bool flags with noexcept guarantees?

Boost - Dev mailing list
On 03/23/17 01:54, Vicente J. Botet Escriba wrote:
> I have an ordinal_set<Ordinal> that is type safe.
> An enum as the previous one canbe see as an Ordinal and so
>
>     enum animal_traits
>     {
>       eats_meat, eats_grass, has_tail, _count
>     };
>     using animal_traits_set = ordinal_set<animal_traits>

Is the ordinal_set part of Boost? Where can I see it?

Also, how do you deduce the number of bits from the enum type?

>>   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.
> compile time for ordinal sets would suffer a little bit as we need to
> associate at compile time each enumerator to its position (this could be
> provided by reflection or by user defined ordinal traits).

Ah, so one would also have to specialize traits for the enum?


_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Is there any interest in type-safe container of bool flags with noexcept guarantees?

Boost - Dev mailing list
In reply to this post by Boost - Dev mailing list
On 23/03/2017 11:44, Andrey Semashev via Boost wrote:
> On 03/22/17 20:14, Richard Hodges via Boost wrote:
>> I agree that bitfields are not the way to go. They suffer from a
>> number of deficiencies - not least that each bit is *not a separate
>> object* so they are problematic i multithreaded environments. They
>> also of course carry no type information.
>
> Thread safety is a red herring since none of the solutions presented in
> this discussion are thread safe.

FWIW, I have some code kicking around somewhere that lets you treat a
manually bit-mask-valued enum as atomic.  But yes, that's off-topic.



_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Is there any interest in type-safe container of bool flags with noexcept guarantees?

Boost - Dev mailing list
In reply to this post by Boost - Dev mailing list
Le 23/03/2017 à 00:01, Andrey Semashev via Boost a écrit :

> On 03/23/17 01:54, Vicente J. Botet Escriba wrote:
>> I have an ordinal_set<Ordinal> that is type safe.
>> An enum as the previous one canbe see as an Ordinal and so
>>
>>     enum animal_traits
>>     {
>>       eats_meat, eats_grass, has_tail, _count
>>     };
>>     using animal_traits_set = ordinal_set<animal_traits>
>
> Is the ordinal_set part of Boost? Where can I see it?
It was enum_set in [TBost.Enums]
     https://github.com/viboes/enums
https://htmlpreview.github.io/?https://github.com/viboes/enums/blob/master/libs/enums/doc/html/index.html

and then it became ordinal_set in [JASEL]
https://github.com/viboes/std-make/tree/master/include/experimental/fundamental/v3/ordinal

The last needs some work yet to adapt enums.
>
> Also, how do you deduce the number of bits from the enum type?
it is the size of the ordinal type. For enums, the number of unique
enumerators.

>
>>>   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.
>> compile time for ordinal sets would suffer a little bit as we need to
>> associate at compile time each enumerator to its position (this could be
>> provided by reflection or by user defined ordinal traits).
>
> Ah, so one would also have to specialize traits for the enum?
Yes. There could be a linear default trait 0..N trait.

Vicente


_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Is there any interest in type-safe container of bool flags with noexcept guarantees?

Boost - Dev mailing list
In reply to this post by Boost - Dev mailing list
On 23.03.2017 01:40, Andrey Semashev via Boost wrote:

> 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.
>

Using std::bitset in this way is not a good solution.
C++ standard doesn't claim that bitset's backing storage should be as
compact as possible. In stdlibc++ bits are stored in array of
unsigned long items. So on my Ubuntu x64

   sizeof(typed_bitset< animal_traits, animal_traits::_count >) == 8

That's not acceptable for only 3 bits.

Another problem is that member functions for bit manipulations are not
marked as *noexcept*. Using weak enum-based parameterization you can
be sure in proper use bits. But the compiler doesn't know it.

That's why I've come to making my own container. I need compact storage
and noexcept guarantee at compile time.

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Loading...