interest in a flag library
Hi all, This is the first time I post on this mailing list :). I'm posting because i'd like to know if people would be interested in a library to treat enum types as flags. I find that's something which is lacking in the language since the introduction of strongly typed enums, and I've since written some code in order to do this. So far I've seen some discussion on this mailing list about that, but it doesn't seems that any library have been written yet. So do you think it is worth the effort of porting and documenting my code so that it can integrated into boost, or not? Regards, Masse Nicolas. PS: For those interested, the code consists of a flag class, and overloads operators like '|' or &. It can be used like this: enum struct option : uint { none = 0, first = 1, second = 2, third = 4, forth = 8 }; static constexpr flags<option> var1 = option::first; static constexpr flags<option> var2 = option::second | option::third; static constexpr auto var3 = var1 | var2; /* type will be resolved into flags<option> */ static constexpr auto var4 = option::first | option::second | option::third; /* works too */ int main() { std::cout << var1 << std::endl; std::cout << static_cast<uint>(var2) << std::endl; /* note that static_cast<int> won't compile since the underlying type is uint */ std::cout << (var3 & option::forth) << std::endl; std::cout << (var3 == var4) << std::endl; return 0; }
So do you think it is worth the effort of porting and documenting my code so that it can integrated into boost, or not?
It's for sure something I have encountered.
static constexpr auto var4 = option::first | option::second | option::third; /* works too */
How do you get this one to work? F
czw., 25 paź 2018 o 23:28 Masse Nicolas via Boost <boost@lists.boost.org> napisał(a):
Hi all,
This is the first time I post on this mailing list :). I'm posting because i'd like to know if people would be interested in a library to treat enum types as flags. I find that's something which is lacking in the language since the introduction of strongly typed enums, and I've since written some code in order to do this. So far I've seen some discussion on this mailing list about that, but it doesn't seems that any library have been written yet. So do you think it is worth the effort of porting and documenting my code so that it can integrated into boost, or not?
Hi. I would use a type-safe library for flags. I am missing one today. However, given that you provide your own type (which is good IMO), I would expect a different interface that reflects the meaning more directly. I consider bit-wise operations to be a hack rather than a proper solution. Expression `flags & Flags::EXEC` is quite idiomatic, but when I want to reset a flag, I have to write `flag &=~ Flag::EXEC` (which I call an 'oil lamp' operator), which is difficult to understand and likely to get wrong. (In fact, I am not sure I got it right in this example.) I would rather expect an interface using named functions or indexing operator: ``` if (!flags[Flag::READ]) flags.reset(Flag::EXEC); ``` Regards, &rzej;
On 26. Oct 2018, at 09:35, Andrzej Krzemienski via Boost <boost@lists.boost.org> wrote:
Hi. I would use a type-safe library for flags. I am missing one today. […] I would rather expect an interface using named functions or indexing operator:
``` if (!flags[Flag::READ]) flags.reset(Flag::EXEC); ```
There are std::bitset (fixed size, allocates on the stack) and boost::dynamic_bitset (dynamic size, allocates from stack and heap) https://www.boost.org/doc/libs/1_68_0/libs/dynamic_bitset/dynamic_bitset.htm... The latter also has more sensible implementations of operator& and operator|. Before you invent a new thing, what is wrong with these classes? Best regards, Hans
There are std::bitset (fixed size, allocates on the stack) and boost::dynamic_bitset (dynamic size, allocates from stack and heap)
https://www.boost.org/doc/libs/1_68_0/libs/dynamic_bitset/dynamic_bitset.htm...
The latter also has more sensible implementations of operator& and operator|. Before you invent a new thing, what is wrong with these classes?
Naming and type safety. Example: `File open(std::path, AccessMode)` Usage: `auto file = open("myPath/filename.foo", AccessMode::Read | AccessMode::Write)` - Named access to bits (Read/Write) instead of indices - Type safety: AccessMode instead of std::bitset, and you can't pass e.g. Includes::All to AccessMode just the same as strong enums protect against a similar issue
Le 26/10/2018 à 10:08, Hans Dembinski via Boost a écrit :
On 26. Oct 2018, at 09:35, Andrzej Krzemienski via Boost <boost@lists.boost.org> wrote:
Hi. I would use a type-safe library for flags. I am missing one today. […] I would rather expect an interface using named functions or indexing operator:
``` if (!flags[Flag::READ]) flags.reset(Flag::EXEC); ``` There are std::bitset (fixed size, allocates on the stack) and boost::dynamic_bitset (dynamic size, allocates from stack and heap)
https://www.boost.org/doc/libs/1_68_0/libs/dynamic_bitset/dynamic_bitset.htm...
The latter also has more sensible implementations of operator& and operator|. Before you invent a new thing, what is wrong with these classes?
Their size. The size of std::bitset<8> is not 8 bits but very often 64 bits. :( We need something like std::bitset<N>, where we can define the size of the block xxx::bitset<20, unsigned char>. The size of xxx::bitset<20, unsigned char> should be 3 bytes. Aside this point relative to the size, there is also the fact that std::bitset<N> uses only index between 0 and N-1. I've defined a ordinal library where the index is not a number between 0 and N-1, but any type that is isomorphic to O..N-1. https://github.com/viboes/std-make/tree/master/include/experimental/fundamen... This library defines several container and in particular an ordinal_set https://github.com/viboes/std-make/blob/master/include/experimental/fundamen... but also an ordinal array https://github.com/viboes/std-make/blob/master/include/experimental/fundamen... and an ordinal range You can find a draft proposal here https://github.com/viboes/std-make/blob/master/doc/proposal/ordinal/Ordinal.... In particular enums with power of 2 constants are isomorphic to 0..N-1. Unfortunately ordinal_set<O> when the cardinality of O is 24 is not 3 bytes as I use std::bitset as underlying implementation. I started an implementation of a bitmask that has a builtin unsigned int underlying representation in https://github.com/viboes/std-make/blob/master/include/experimental/fundamen... but I have not put both together. Please, be free to take whatever could be useful to you. Hopping this helps, Vicente
Right now my code provide the "+" and the "-" operators to set or reset flags. Anyway, I see no problem in adding some named function for those who prefer using them. Masse Nicolas. Le ven. 26 oct. 2018 à 09:35, Andrzej Krzemienski via Boost < boost@lists.boost.org> a écrit :
czw., 25 paź 2018 o 23:28 Masse Nicolas via Boost <boost@lists.boost.org> napisał(a):
Hi all,
This is the first time I post on this mailing list :). I'm posting because i'd like to know if people would be interested in a library to treat enum types as flags. I find that's something which is lacking in the language since the introduction of strongly typed enums, and I've since written some code in order to do this. So far I've seen some discussion on this mailing list about that, but it doesn't seems that any library have been written yet. So do you think it is worth the effort of porting and documenting my code so that it can integrated into boost, or not?
Hi. I would use a type-safe library for flags. I am missing one today. However, given that you provide your own type (which is good IMO), I would expect a different interface that reflects the meaning more directly. I consider bit-wise operations to be a hack rather than a proper solution. Expression `flags & Flags::EXEC` is quite idiomatic, but when I want to reset a flag, I have to write `flag &=~ Flag::EXEC` (which I call an 'oil lamp' operator), which is difficult to understand and likely to get wrong. (In fact, I am not sure I got it right in this example.)
I would rather expect an interface using named functions or indexing operator:
``` if (!flags[Flag::READ]) flags.reset(Flag::EXEC); ```
Regards, &rzej;
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
On Thu, Oct 25, 2018 at 11:28 PM Masse Nicolas via Boost < boost@lists.boost.org> wrote:
Hi all,
[...]
enum struct option : uint { none = 0, first = 1, second = 2, third = 4, forth = 8 };
static constexpr flags<option> var1 = option::first; static constexpr flags<option> var2 = option::second | option::third; static constexpr auto var3 = var1 | var2; /* type will be resolved into flags<option> */ static constexpr auto var4 = option::first | option::second | option::third; /* works too */
I think a better approach is to fully define the flag type as a class (without the enum). It gives you slightly less typing for the user. Such flags type would be defined similar to how the NamedType library is used. There's also a neat trick to not have to manually specify powers of two (and possibly get it wrong) using a separate type for bit numbers. using my_flags_type = flag_type<std::uint8, struct my_flags_tag>; constexpr my_flags_type red = 0_bit; constexpr my_flags_type green = 1_bit; constexpr my_flags_type blue = 2_bit; I describe such a flag type in this lightning talk: https://www.youtube.com/watch?v=NGrnKr9rSz4 -- Arvid Norberg
On Sun, Oct 28, 2018 at 7:01 PM Arvid Norberg via Boost < boost@lists.boost.org> wrote:
On Thu, Oct 25, 2018 at 11:28 PM Masse Nicolas via Boost < boost@lists.boost.org> wrote:
Hi all,
[...]
enum struct option : uint { none = 0, first = 1, second = 2, third = 4, forth = 8 };
static constexpr flags<option> var1 = option::first; static constexpr flags<option> var2 = option::second | option::third; static constexpr auto var3 = var1 | var2; /* type will be resolved into flags<option> */ static constexpr auto var4 = option::first | option::second | option::third; /* works too */
I think a better approach is to fully define the flag type as a class (without the enum).
I think it valuable to have the enum approach for a relatively simple handling of third-party libraries, etc. that know nothing of this library, as it would involve little to no effort to use them within this library. Perhaps one might explore both approaches? This enum approach eases adoption by allowing someone to use this class with existing enumerations, such as std::ios::flags. Although, I am concerned that it may lack the handling of enums not cast as a class or struct. How would it identify the appropriate type to which to resolve in such situations? - Trey
Le lun. 29 oct. 2018 à 00:01, Arvid Norberg <arvid.norberg@gmail.com> a écrit :
On Thu, Oct 25, 2018 at 11:28 PM Masse Nicolas via Boost < boost@lists.boost.org> wrote:
Hi all,
[...]
enum struct option : uint { none = 0, first = 1, second = 2, third = 4, forth = 8 };
static constexpr flags<option> var1 = option::first; static constexpr flags<option> var2 = option::second | option::third; static constexpr auto var3 = var1 | var2; /* type will be resolved into flags<option> */ static constexpr auto var4 = option::first | option::second | option::third; /* works too */
I think a better approach is to fully define the flag type as a class (without the enum). It gives you slightly less typing for the user. Such flags type would be defined similar to how the NamedType library is used. There's also a neat trick to not have to manually specify powers of two (and possibly get it wrong) using a separate type for bit numbers.
using my_flags_type = flag_type<std::uint8, struct my_flags_tag>; constexpr my_flags_type red = 0_bit; constexpr my_flags_type green = 1_bit; constexpr my_flags_type blue = 2_bit;
I describe such a flag type in this lightning talk: https://www.youtube.com/watch?v=NGrnKr9rSz4
-- Arvid Norberg
Hi, I had a quick look at your presentation. While I do think that using a class is an option, I still prefer to use enums for that, for several reasons: - The use of an enum for the values makes a clear differences betwen them and the flags (which are a combinaison of those values). - The use of enums allows to scope the values inside their name, while using your solution makes them visible directly inside your namespace. - The need of a tag struct is be a bit odd, espescially for now comers Anyway, I should say I like the idea of having an helper class to avoid the specification of to power of two. As a consequence, I will probably add something similar in my code. Nicolas.
participants (8)
-
Alexander Grund
-
Andrzej Krzemienski
-
Arvid Norberg
-
Frédéric
-
Hans Dembinski
-
Joseph Van Riper
-
Masse Nicolas
-
Vicente J. Botet Escriba