Topic: flag types
Author: Sean Middleditch <sean.middleditch@gmail.com>
Date: Sun, 21 Jul 2013 18:15:30 -0700 (PDT)
Raw View
------=_Part_29_24117747.1374455730955
Content-Type: text/plain; charset=ISO-8859-1
A very common need in many programs is to store a collection of flags,
often stored as a collection of bits. In the past, enums were typically
used, often of a form similar to:
enum flags {
none = 0,
first = 0x1,
second = 0x2,
third = 0x4,
};
These would then often be stored in an integral type. Since the enum value
cast to integral type implicitly, you could use expressions like
first|third or such with no problem. Common problems, however, were that
the enum constants were injected into the enclosing namespace, any integral
valid would implicitly cast to the variable holding the flags, and so on.
C++11 style enum class solve most of these problems, but are an annoyance
for flag types as they are designed to be strict enumerations. Namely,
they cannot be used with operators like |, &, ~, or ^, and there is no
definitive type in which to store values made of these compositions.
Operating overloading and some cleverness can be employed to get around
these problems, but not without pain.
Purely as an initial idea proposal, and this syntax is entirely made-up
just to serve for exposition, imagine we had a facilities geared for flags:
flag_type my_flags {
first, // value is 1
second, // value is 2
third, // value is 4
};
flag_type other_flags : unsigned int { // signed types are not legal
none = 0, // value is 0, of course
first // value is 1
};
sizeof(my_flags) == sizeof(smallest unsigned integral type that can
contain all defined values)
sizeof(other_flags) == sizeof(unsigned int)
my_flags foo = my_flags::first|my_flags::third; // valid, | is defined
for any flag_type to be the expected result, typed as the corresponding
flag_type
my_flags bar = 5; // not valid, no conversion from int to my_flags
my_flags gar = other_flags::first; // not valid, no conversion from
other_flags to my_flags
my_flags baz = foo&my_flags::first; // & is defined as works as
expected, typed as corresponding flag_type
bool test = baz; // not valid; no implicit conversion from a flag_type
to boolean
if (baz) // valid, implicit boolean context is true if value is not
zero, false if it is zero
The idea is that we get all the strictness of enum class while still being
usable for flags as pre-C++11 enums were.
The rule for values would be that if a value has an explicit assigned (none
= 0), the value is as given. If no assignment is given, the next power of
two from the previous item is chosen, or 1 is chosen if the previous value
was 0. The first value listed is 1, not 0.
flag_type foo {
a, // 1
b = 3, // 3
c, // 4, next power of 2 after 3
d = a|c, // 5
e, // 8, next power of 2 after 5
f = d|e, // 13
g, // 16, next power of 2 after 13
h = 3, // 3, alias for b
};
I could see a nullflag_type or something which is implicitly equal to 0 but
also implicitly converts to any flag_type, equivalent to nullptr and
nullopt.
I'm of the opinion a library solution would be best, but I've been
incapable of creating one that works smoothly without a lot of repetition
for each flags variant. Minor language extensions may be needed to make a
library approach feasible, such as the ability to inject enum values into
another namespace, and the ability for friend operators of a template to
override enum class operations (which doesn't work as of GCC 4.8; unsure if
that's just a bug or not).
Larger language changes may be necessary to directly support a "flag_type"
built-in, if a library approach can't be made that is easy to use and isn't
too error-prone. The best I can do now (which isn't too different from the
C++98 days) is to make a template that wraps a particular enum. This still
make flag definition fragile (coders must be conscious of bit patterns;
would be less troublesome with binary literals, but only slightly IMO),
awkward scoping for values, and enum classes cannot be used if | and & are
meant to work with lots of excess casting.
I note as prior art of attention to this problem is the C# flags attribute,
which I consider to fall far short of a full solution. C#'s built-in enum
rules are a mix of C++98 and C++11 are better suited to use as flags than
either C++ approach currently available.
On a highly related note, something like a
std::max_enumeration_value<type>::value would be really great. It is
_extremely_ common to see things like:
enum class foo {
first,
second,
last // highest "legal" value of the enumeration, +1
};
Used in loops, asserts, and so on. Having such a construct both for enums
(C++98 and C++11 style) as well as some flag_type would be very handy.
Sensible or off the deep end?
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
------=_Part_29_24117747.1374455730955
Content-Type: text/html; charset=ISO-8859-1
Content-Transfer-Encoding: quoted-printable
A very common need in many programs is to store a collection of flags, ofte=
n stored as a collection of bits. In the past, enums were typically u=
sed, often of a form similar to:<div><br></div><div> enum flags {</di=
v><div> none =3D 0,</div><div> first =3D 0x1,</di=
v><div> second =3D 0x2,</div><div> third =3D 0x4,=
</div><div> };</div><div><br></div><div>These would then often be sto=
red in an integral type. Since the enum value cast to integral type i=
mplicitly, you could use expressions like first|third or such with no probl=
em. Common problems, however, were that the enum constants were injec=
ted into the enclosing namespace, any integral valid would implicitly cast =
to the variable holding the flags, and so on.</div><div><br></div><div>C++1=
1 style enum class solve most of these problems, but are an annoyance for f=
lag types as they are designed to be strict enumerations. Namely, the=
y cannot be used with operators like |, &, ~, or ^, and there is no def=
initive type in which to store values made of these compositions.</div><div=
><br></div><div>Operating overloading and some cleverness can be employed t=
o get around these problems, but not without pain.</div><div><br></div><div=
>Purely as an initial idea proposal, and this syntax is entirely made-up ju=
st to serve for exposition, imagine we had a facilities geared for flags:</=
div><div><br></div><div> flag_type my_flags {</div><div> =
first, // value is 1</div><div> second, // =
value is 2</div><div> third, // value is 4</div><div>&nb=
sp; };</div><div> flag_type other_flags : unsigned int { =
// signed types are not legal</div><div> none =3D 0, // value =
is 0, of course</div><div> first // value is 1</div><div> =
; };</div><div><br></div><div> sizeof(my_flags) =3D=3D si=
zeof(smallest unsigned integral type that can contain all defined values)</=
div><div> sizeof(other_flags) =3D=3D sizeof(unsigned int)</div>=
<div><br></div><div> my_flags foo =3D my_flags::first|my_flags:=
:third; // valid, | is defined for any flag_type to be the expected result,=
typed as the corresponding flag_type</div><div> my_flags =
bar =3D 5; // not valid, no conversion from int to my_flags</div><div> =
; my_flags gar =3D other_flags::first; // not valid, no conversion fr=
om other_flags to my_flags</div><div> my_flags baz =3D foo&=
my_flags::first; // & is defined as works as expected, typed as corresp=
onding flag_type</div><div> bool test =3D baz; // not valid; no=
implicit conversion from a flag_type to boolean</div><div><br></div><div>&=
nbsp; if (baz) // valid, implicit boolean context is true if value is=
not zero, false if it is zero<br></div><div><br></div><div>The idea is tha=
t we get all the strictness of enum class while still being usable for flag=
s as pre-C++11 enums were.</div><div><br></div><div>The rule for values wou=
ld be that if a value has an explicit assigned (none =3D 0), the value is a=
s given. If no assignment is given, the next power of two from the pr=
evious item is chosen, or 1 is chosen if the previous value was 0. Th=
e first value listed is 1, not 0.</div><div><br></div><div> fla=
g_type foo {</div><div> a, /=
/ 1</div><div> b =3D 3, // 3</div><div> &nb=
sp; c, // 4, next power of 2 after 3</div=
><div> d =3D a|c, // 5</div><div> e, =
// 8, next power of 2 after 5</div><div> =
f =3D d|e, // 13</div><div> g, =
// 16, next power of 2 after 13</div><div> &nbs=
p; h =3D 3, // 3, alias for b</div><div> };</div><=
div><br></div><div>I could see a nullflag_type or something which is implic=
itly equal to 0 but also implicitly converts to any flag_type, equivalent t=
o nullptr and nullopt.</div><div><br></div><div>I'm of the opinion a librar=
y solution would be best, but I've been incapable of creating one that work=
s smoothly without a lot of repetition for each flags variant. Minor =
language extensions may be needed to make a library approach feasible, such=
as the ability to inject enum values into another namespace, and the abili=
ty for friend operators of a template to override enum class operations (wh=
ich doesn't work as of GCC 4.8; unsure if that's just a bug or not).</div><=
div><br></div><div>Larger language changes may be necessary to directly sup=
port a "flag_type" built-in, if a library approach can't be made that is ea=
sy to use and isn't too error-prone. The best I can do now (which isn=
't too different from the C++98 days) is to make a template that wraps a pa=
rticular enum. This still make flag definition fragile (coders must b=
e conscious of bit patterns; would be less troublesome with binary literals=
, but only slightly IMO), awkward scoping for values, and enum classes cann=
ot be used if | and & are meant to work with lots of excess casting.</d=
iv><div><br></div><div>I note as prior art of attention to this problem is =
the C# flags attribute, which I consider to fall far short of a full soluti=
on. C#'s built-in enum rules are a mix of C++98 and C++11 are better =
suited to use as flags than either C++ approach currently available.</div><=
div><br></div><div>On a highly related note, something like a std::max_enum=
eration_value<type>::value would be really great. It is _extrem=
ely_ common to see things like:</div><div><br></div><div> enum =
class foo {</div><div> first,</div><div> &n=
bsp;second,</div><div> last // highest "legal" value of =
the enumeration, +1</div><div> };</div><div><br></div><div>Used=
in loops, asserts, and so on. Having such a construct both for enums=
(C++98 and C++11 style) as well as some flag_type would be very handy.</di=
v><div><br></div><div>Sensible or off the deep end?</div>
<p></p>
-- <br />
<br />
--- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" group.<br />
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to std-proposals+unsubscribe@isocpp.org.<br />
To post to this group, send email to std-proposals@isocpp.org.<br />
Visit this group at <a href=3D"http://groups.google.com/a/isocpp.org/group/=
std-proposals/">http://groups.google.com/a/isocpp.org/group/std-proposals/<=
/a>.<br />
<br />
<br />
------=_Part_29_24117747.1374455730955--
.
Author: Nicol Bolas <jmckesson@gmail.com>
Date: Sun, 21 Jul 2013 20:59:25 -0700 (PDT)
Raw View
------=_Part_2149_33113371.1374465565620
Content-Type: text/plain; charset=ISO-8859-1
You can already do this, since strongly typed enums are proper types. You
can simply overload operators |, &, and such for them, disallowing other
such operators.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
------=_Part_2149_33113371.1374465565620
Content-Type: text/html; charset=ISO-8859-1
You can already do this, since strongly typed enums are proper types. You can simply overload operators |, &, and such for them, disallowing other such operators.<br>
<p></p>
-- <br />
<br />
--- <br />
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.<br />
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.<br />
To post to this group, send email to std-proposals@isocpp.org.<br />
Visit this group at <a href="http://groups.google.com/a/isocpp.org/group/std-proposals/">http://groups.google.com/a/isocpp.org/group/std-proposals/</a>.<br />
<br />
<br />
------=_Part_2149_33113371.1374465565620--
.
Author: Sean Middleditch <sean@middleditch.us>
Date: Sun, 21 Jul 2013 21:05:11 -0700
Raw View
And that's part of the "awkward" part. One should not have to
overload the entire set of operators each and every time, just like
one does not have to provide an operator+ for every variant of
integer. I at least have seen a very large number of flag types in
larger applications and frameworks. The only sensible option now is a
set of preprocessor macros (which at least lets you _also_ solve the
other problem of string conversions and introspection, but there's
already a subcommittee looking into that one specifically). It also
still leaves the other issues unaddressed.
On Sun, Jul 21, 2013 at 8:59 PM, Nicol Bolas <jmckesson@gmail.com> wrote:
> You can already do this, since strongly typed enums are proper types. You
> can simply overload operators |, &, and such for them, disallowing other
> such operators.
>
> --
>
> ---
> You received this message because you are subscribed to a topic in the
> Google Groups "ISO C++ Standard - Future Proposals" group.
> To unsubscribe from this topic, visit
> https://groups.google.com/a/isocpp.org/d/topic/std-proposals/1RPxJSJ_0z8/unsubscribe.
> To unsubscribe from this group and all its topics, send an email to
> std-proposals+unsubscribe@isocpp.org.
> To post to this group, send email to std-proposals@isocpp.org.
> Visit this group at
> http://groups.google.com/a/isocpp.org/group/std-proposals/.
>
>
--
Sean Middleditch
http://seanmiddleditch.com
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
.
Author: Jeffrey Yasskin <jyasskin@google.com>
Date: Sun, 21 Jul 2013 21:19:49 -0700
Raw View
I think std::bitset<N> provides most of the functionality you want
here. You'd define a normal enum class with contiguous values, and
you'd use the bitset type for sets of such enums. With the
std::max_enumeration_value<type> you suggest, one could easily write
an enum_set<enum_t> which deduces the proper N for a bitset. Chandler
(involved with the reflection SG) suggested that the best compiler
interface for this might be to provide a (constexpr) function template
that takes an enum type and returns a range containing all values of
that enum.
It might also be convenient to provide an implicit conversion from
enum_t to enum_set<enum_t>. I suspect we'd need a full solution for
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3682.html#33
(adding conversion operators for enum classes) for that to work.
But see if a bitset will get you most of the way to what you want today.
Jeffrey
On Sun, Jul 21, 2013 at 6:15 PM, Sean Middleditch
<sean.middleditch@gmail.com> wrote:
> A very common need in many programs is to store a collection of flags, often
> stored as a collection of bits. In the past, enums were typically used,
> often of a form similar to:
>
> enum flags {
> none = 0,
> first = 0x1,
> second = 0x2,
> third = 0x4,
> };
>
> These would then often be stored in an integral type. Since the enum value
> cast to integral type implicitly, you could use expressions like first|third
> or such with no problem. Common problems, however, were that the enum
> constants were injected into the enclosing namespace, any integral valid
> would implicitly cast to the variable holding the flags, and so on.
>
> C++11 style enum class solve most of these problems, but are an annoyance
> for flag types as they are designed to be strict enumerations. Namely, they
> cannot be used with operators like |, &, ~, or ^, and there is no definitive
> type in which to store values made of these compositions.
>
> Operating overloading and some cleverness can be employed to get around
> these problems, but not without pain.
>
> Purely as an initial idea proposal, and this syntax is entirely made-up just
> to serve for exposition, imagine we had a facilities geared for flags:
>
> flag_type my_flags {
> first, // value is 1
> second, // value is 2
> third, // value is 4
> };
> flag_type other_flags : unsigned int { // signed types are not legal
> none = 0, // value is 0, of course
> first // value is 1
> };
>
> sizeof(my_flags) == sizeof(smallest unsigned integral type that can
> contain all defined values)
> sizeof(other_flags) == sizeof(unsigned int)
>
> my_flags foo = my_flags::first|my_flags::third; // valid, | is defined
> for any flag_type to be the expected result, typed as the corresponding
> flag_type
> my_flags bar = 5; // not valid, no conversion from int to my_flags
> my_flags gar = other_flags::first; // not valid, no conversion from
> other_flags to my_flags
> my_flags baz = foo&my_flags::first; // & is defined as works as expected,
> typed as corresponding flag_type
> bool test = baz; // not valid; no implicit conversion from a flag_type to
> boolean
>
> if (baz) // valid, implicit boolean context is true if value is not zero,
> false if it is zero
>
> The idea is that we get all the strictness of enum class while still being
> usable for flags as pre-C++11 enums were.
>
> The rule for values would be that if a value has an explicit assigned (none
> = 0), the value is as given. If no assignment is given, the next power of
> two from the previous item is chosen, or 1 is chosen if the previous value
> was 0. The first value listed is 1, not 0.
>
> flag_type foo {
> a, // 1
> b = 3, // 3
> c, // 4, next power of 2 after 3
> d = a|c, // 5
> e, // 8, next power of 2 after 5
> f = d|e, // 13
> g, // 16, next power of 2 after 13
> h = 3, // 3, alias for b
> };
>
> I could see a nullflag_type or something which is implicitly equal to 0 but
> also implicitly converts to any flag_type, equivalent to nullptr and
> nullopt.
>
> I'm of the opinion a library solution would be best, but I've been incapable
> of creating one that works smoothly without a lot of repetition for each
> flags variant. Minor language extensions may be needed to make a library
> approach feasible, such as the ability to inject enum values into another
> namespace, and the ability for friend operators of a template to override
> enum class operations (which doesn't work as of GCC 4.8; unsure if that's
> just a bug or not).
>
> Larger language changes may be necessary to directly support a "flag_type"
> built-in, if a library approach can't be made that is easy to use and isn't
> too error-prone. The best I can do now (which isn't too different from the
> C++98 days) is to make a template that wraps a particular enum. This still
> make flag definition fragile (coders must be conscious of bit patterns;
> would be less troublesome with binary literals, but only slightly IMO),
> awkward scoping for values, and enum classes cannot be used if | and & are
> meant to work with lots of excess casting.
>
> I note as prior art of attention to this problem is the C# flags attribute,
> which I consider to fall far short of a full solution. C#'s built-in enum
> rules are a mix of C++98 and C++11 are better suited to use as flags than
> either C++ approach currently available.
>
> On a highly related note, something like a
> std::max_enumeration_value<type>::value would be really great. It is
> _extremely_ common to see things like:
>
> enum class foo {
> first,
> second,
> last // highest "legal" value of the enumeration, +1
> };
>
> Used in loops, asserts, and so on. Having such a construct both for enums
> (C++98 and C++11 style) as well as some flag_type would be very handy.
>
> Sensible or off the deep end?
>
> --
>
> ---
> You received this message because you are subscribed to the Google Groups
> "ISO C++ Standard - Future Proposals" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to std-proposals+unsubscribe@isocpp.org.
> To post to this group, send email to std-proposals@isocpp.org.
> Visit this group at
> http://groups.google.com/a/isocpp.org/group/std-proposals/.
>
>
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
.
Author: Richard Smith <richard@metafoo.co.uk>
Date: Sun, 21 Jul 2013 22:24:56 -0700
Raw View
--089e011778f5a4f46d04e212e5cf
Content-Type: text/plain; charset=ISO-8859-1
On Sun, Jul 21, 2013 at 9:19 PM, Jeffrey Yasskin <jyasskin@google.com>wrote:
> I think std::bitset<N> provides most of the functionality you want
> here. You'd define a normal enum class with contiguous values, and
> you'd use the bitset type for sets of such enums. With the
> std::max_enumeration_value<type> you suggest, one could easily write
> an enum_set<enum_t> which deduces the proper N for a bitset.
Look at Qt's QFlags for a pre-rolled implementation of something similar:
QFlags<enum_type> gives you a type-safe collection of flags, with a
complete set of bitwise operations. The only missing piece is that you need
to manually specify the values for your enumeration constants as powers of
two:
enum flag_values {
none = 0,
first = 0x1,
second = 0x2,
third = 0x4
};
using flags = QFlags<flag_values>;
Given that you can solve nearly all of this problem in a library, adding a
significant new core language feature to support it seems excessive to me.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
--089e011778f5a4f46d04e212e5cf
Content-Type: text/html; charset=ISO-8859-1
Content-Transfer-Encoding: quoted-printable
On Sun, Jul 21, 2013 at 9:19 PM, Jeffrey Yasskin <span dir=3D"ltr"><<a h=
ref=3D"mailto:jyasskin@google.com" target=3D"_blank">jyasskin@google.com</a=
>></span> wrote:<br><div class=3D"gmail_quote"><blockquote class=3D"gmai=
l_quote" style=3D"margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left=
:1ex">
I think std::bitset<N> provides most of the functionality you want<br=
>
here. You'd define a normal enum class with contiguous values, and<br>
you'd use the bitset type for sets of such enums. With the<br>
std::max_enumeration_value<type> you suggest, one could easily write<=
br>
an enum_set<enum_t> which deduces the proper N for a bitset.</blockqu=
ote><div><br></div><div>Look at Qt's QFlags for a pre-rolled implementa=
tion of something similar: QFlags<enum_type> gives you a type-safe co=
llection of flags, with a complete set of bitwise operations. The only miss=
ing piece is that you need to manually specify the values for your enumerat=
ion constants as powers of two:</div>
<div><br></div><div>enum flag_values {</div><div>=A0 none =3D 0,</div><div>=
=A0 first =3D 0x1,</div><div>=A0 second =3D 0x2,</div><div>=A0 third =3D 0x=
4</div><div>};</div><div>using flags =3D QFlags<flag_values>;</div><d=
iv><br></div><div>
Given that you can solve nearly all of this problem in a library, adding a =
significant new core language feature to support it seems excessive to me.<=
/div></div>
<p></p>
-- <br />
<br />
--- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" group.<br />
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to std-proposals+unsubscribe@isocpp.org.<br />
To post to this group, send email to std-proposals@isocpp.org.<br />
Visit this group at <a href=3D"http://groups.google.com/a/isocpp.org/group/=
std-proposals/">http://groups.google.com/a/isocpp.org/group/std-proposals/<=
/a>.<br />
<br />
<br />
--089e011778f5a4f46d04e212e5cf--
.
Author: Sean Middleditch <sean@middleditch.us>
Date: Mon, 22 Jul 2013 00:26:36 -0700
Raw View
QFlags represents the best you can do now, which is exactly what I was
getting at as a nasty workaround. I already know how to handle flags
today; I'm not exactly new at this game. :)
** QFlags and its ilk is the problem I'd like to solve here, not the
solution. ** Macros, no proper scoping of values, doesn't use the new
C++11 enum classes, can't be used with forward-declared enums, the
problem list goes on.
Again, I'm all for library-only solutions. QFlags ain't it.
std::bitset fails due to not using the type system at all (nothing
stops you from accidentally combining flags1::value with flags2::value
into the same bitset, and there's other issues involving how you'd
define "combined flag" constants in an obvious clean way). Pasting
together operator overloads for every set of flags is repetitive and
error-pone.
If the answer is "suck it up and deal with it," fine, that's what we
do already... but should we have to post C++17? Going to the ideal I
listed originally is probably a bit too much in terms of one-off
semantics, I agree, but surely there's a compromise we could discuss
involving much more minor (and useful elsewhere) tweaks enabling a
very clean and robust standard library solution, rather than "use
macros and throw away the type system" ?
If there were an easier way to define sets of operator overloads for
any enum, it could be a bit easier, maybe? Going back to the "old
style" enums, and ignoring temporarily the problem of combined
constants, a template perhaps could be:
namespace std {
struct nullflag_t { };
static constexpr nullflag_t nullflag;
template <typename EnumType>
// this inheritance is not allowed currently, of course; just a
strawman idea of how one could "inject"
// enum names into a class's namespace, I don't think this is the
ideal syntax here
class flag_set : public EnumType {
public:
typedef std::make_unsigned<typename
std::underlying_type<EnumType>::type>::type int_type;
flag_set() = default;
constexpr flag_set(const std::nullflag_t&) : _value(0) { }
constexpr flag_set(EnumType value) : _value(1 << value) { }
explicit constexpr flag_set(int_type value) : _value(value) { }
explicit constexpr operator int_type() const { return int_type(_value); }
friend constexpr flag_set operator|(flag_set lhs, flag_set rhs)
{ return flag_set(int_type(_value)|int_type(_value)); }
// other relevant bitwise operators
private:
EnumType _value;
};
This is very close to what is done today, plus all that macro nonsense
you see in things like QFlags to get around the lack of ability to
inject scopes.
Being able to inject the enum scope gives two things in this case.
First, it means that you don't have one type name meant for use of
values of a flag set and entirely different one for "constants" of the
value set. Two, it means with some additional unpleasantness you
could also introduce "parallel" names for constant _combinations_ of
flags, albeit with some ugly syntax:
enum class flags_values { first, second, third };
struct flags : public std::flag_set<flags_values> {
constexpr flag_set<flags_values> all = first|second|third;
};
// definition is awkward but not usage is super obvious
auto foo = flags::all & ~flags::second; // decltype(foo) ===
std::flag_set<flags_values>, probably not a serious issue
It of course may be cleaner to just treat the base enum type as is
done today (the actual bit pattern and not a bit offset that must be
shifted) and just continue requiring the client user to remember to
use powers of two for unique bits. I've seen people have trouble with
that in the past but I'm thinking binary literals will make it way
better, probably "good enough," for most developers and relatively
easy to teach.
Without at least that, you end up with something similar to this, if
avoiding macros:
enum class flags { none = 0b0000, first = 0b0001, second = 0b0010,
third = 0b0100 };
constexpr auto flags_all = std::flag_set<flags>(flags::first) |
flags::second | flags::third; // necessary cast of first element
auto foo = flags::first; // just a plain enum class, not a flag set,
auto as "dragon typing"
auto foo = std::flag_set<flags>(flags::first);
auto bar = flags::all ^ flags::second; // oops, 'all' is not part of
the flags scope like every other value
auto bar = flags_all ^ flags::second; // better remember where each
value lives!
auto gaz = flags::first | flags::second; // oops, didn't define all
the operator overloads
auto gaz = std::flag_set<flags>(flags::first) | flags::second; // *sigh*
flags baz = 0; // oops, illegal conversion from int to enum class!
flags baz = std::nullflag; // oops, illegal conversion to enum class!
auto baz = flags::none; // not terrible, but have fun with templates
and be sure to consistently name the none value!
Clearly, the above is non-ideal. QFlags' macros make it slightly less
error-prone in some ways, more so in others.
On Sun, Jul 21, 2013 at 10:24 PM, Richard Smith <richard@metafoo.co.uk> wrote:
> On Sun, Jul 21, 2013 at 9:19 PM, Jeffrey Yasskin <jyasskin@google.com>
> wrote:
>>
>> I think std::bitset<N> provides most of the functionality you want
>> here. You'd define a normal enum class with contiguous values, and
>> you'd use the bitset type for sets of such enums. With the
>> std::max_enumeration_value<type> you suggest, one could easily write
>> an enum_set<enum_t> which deduces the proper N for a bitset.
>
>
> Look at Qt's QFlags for a pre-rolled implementation of something similar:
> QFlags<enum_type> gives you a type-safe collection of flags, with a complete
> set of bitwise operations. The only missing piece is that you need to
> manually specify the values for your enumeration constants as powers of two:
>
> enum flag_values {
> none = 0,
> first = 0x1,
> second = 0x2,
> third = 0x4
> };
> using flags = QFlags<flag_values>;
>
> Given that you can solve nearly all of this problem in a library, adding a
> significant new core language feature to support it seems excessive to me.
>
> --
>
> ---
> You received this message because you are subscribed to a topic in the
> Google Groups "ISO C++ Standard - Future Proposals" group.
> To unsubscribe from this topic, visit
> https://groups.google.com/a/isocpp.org/d/topic/std-proposals/1RPxJSJ_0z8/unsubscribe.
> To unsubscribe from this group and all its topics, send an email to
> std-proposals+unsubscribe@isocpp.org.
> To post to this group, send email to std-proposals@isocpp.org.
> Visit this group at
> http://groups.google.com/a/isocpp.org/group/std-proposals/.
>
>
--
Sean Middleditch
http://seanmiddleditch.com
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
.
Author: Martinho Fernandes <martinho.fernandes@gmail.com>
Date: Mon, 22 Jul 2013 09:45:22 +0200
Raw View
On Mon, Jul 22, 2013 at 9:26 AM, Sean Middleditch <sean@middleditch.us> wrote:
> QFlags represents the best you can do now, which is exactly what I was
> getting at as a nasty workaround. I already know how to handle flags
> today; I'm not exactly new at this game. :)
>
> ** QFlags and its ilk is the problem I'd like to solve here, not the
> solution. ** Macros, no proper scoping of values, doesn't use the new
> C++11 enum classes, can't be used with forward-declared enums, the
> problem list goes on.
>
> Again, I'm all for library-only solutions. QFlags ain't it.
> std::bitset fails due to not using the type system at all (nothing
> stops you from accidentally combining flags1::value with flags2::value
> into the same bitset, and there's other issues involving how you'd
> define "combined flag" constants in an obvious clean way). Pasting
> together operator overloads for every set of flags is repetitive and
> error-pone.
>
I have written a library solution that uses a different approach a while back.
You can see it at
https://github.com/rmartinho/wheels/blob/master/include/wheels/enums.h%2B%2B
(note that the macros are merely for code generation; they do not make
part of the interface).
Usage goes as follows:
enum class some_flags { none = 0, first = 1<<1, second = 1<<2,
third = 1<<3 };
namespace wheels { namespace enums {
template <> struct is_flags<some_flags> : std::true_type {};
} }
// in some scope
{
using namespace wheels::enums::operators;
auto first_and_second = some_flags::first | some_flags::second;
}
> If the answer is "suck it up and deal with it," fine, that's what we
> do already... but should we have to post C++17? Going to the ideal I
> listed originally is probably a bit too much in terms of one-off
> semantics, I agree, but surely there's a compromise we could discuss
> involving much more minor (and useful elsewhere) tweaks enabling a
> very clean and robust standard library solution, rather than "use
> macros and throw away the type system" ?
>
> If there were an easier way to define sets of operator overloads for
> any enum, it could be a bit easier, maybe? Going back to the "old
> style" enums, and ignoring temporarily the problem of combined
> constants, a template perhaps could be:
>
> namespace std {
> struct nullflag_t { };
> static constexpr nullflag_t nullflag;
>
> template <typename EnumType>
> // this inheritance is not allowed currently, of course; just a
> strawman idea of how one could "inject"
> // enum names into a class's namespace, I don't think this is the
> ideal syntax here
> class flag_set : public EnumType {
> public:
> typedef std::make_unsigned<typename
> std::underlying_type<EnumType>::type>::type int_type;
>
> flag_set() = default;
> constexpr flag_set(const std::nullflag_t&) : _value(0) { }
> constexpr flag_set(EnumType value) : _value(1 << value) { }
> explicit constexpr flag_set(int_type value) : _value(value) { }
>
> explicit constexpr operator int_type() const { return int_type(_value); }
>
> friend constexpr flag_set operator|(flag_set lhs, flag_set rhs)
> { return flag_set(int_type(_value)|int_type(_value)); }
> // other relevant bitwise operators
>
> private:
> EnumType _value;
> };
>
> This is very close to what is done today, plus all that macro nonsense
> you see in things like QFlags to get around the lack of ability to
> inject scopes.
>
> Being able to inject the enum scope gives two things in this case.
> First, it means that you don't have one type name meant for use of
> values of a flag set and entirely different one for "constants" of the
> value set. Two, it means with some additional unpleasantness you
> could also introduce "parallel" names for constant _combinations_ of
> flags, albeit with some ugly syntax:
>
> enum class flags_values { first, second, third };
> struct flags : public std::flag_set<flags_values> {
> constexpr flag_set<flags_values> all = first|second|third;
> };
>
> // definition is awkward but not usage is super obvious
> auto foo = flags::all & ~flags::second; // decltype(foo) ===
> std::flag_set<flags_values>, probably not a serious issue
>
> It of course may be cleaner to just treat the base enum type as is
> done today (the actual bit pattern and not a bit offset that must be
> shifted) and just continue requiring the client user to remember to
> use powers of two for unique bits. I've seen people have trouble with
> that in the past but I'm thinking binary literals will make it way
> better, probably "good enough," for most developers and relatively
> easy to teach.
>
To be honest, I don't think binary literals really make things more
readable; for readability I personally prefer 1<<n, or a constexpr
bit(n) function.
> Without at least that, you end up with something similar to this, if
> avoiding macros:
>
> enum class flags { none = 0b0000, first = 0b0001, second = 0b0010,
> third = 0b0100 };
> constexpr auto flags_all = std::flag_set<flags>(flags::first) |
> flags::second | flags::third; // necessary cast of first element
>
> auto foo = flags::first; // just a plain enum class, not a flag set,
> auto as "dragon typing"
> auto foo = std::flag_set<flags>(flags::first);
>
> auto bar = flags::all ^ flags::second; // oops, 'all' is not part of
> the flags scope like every other value
> auto bar = flags_all ^ flags::second; // better remember where each
> value lives!
>
> auto gaz = flags::first | flags::second; // oops, didn't define all
> the operator overloads
> auto gaz = std::flag_set<flags>(flags::first) | flags::second; // *sigh*
>
> flags baz = 0; // oops, illegal conversion from int to enum class!
> flags baz = std::nullflag; // oops, illegal conversion to enum class!
> auto baz = flags::none; // not terrible, but have fun with templates
> and be sure to consistently name the none value!
`flags{}` is a perfectly valid "zero" value for any enum. You can
either write `flags baz = {};` or `auto baz = flags{};`.
All that said... Do we really want to carry along the bitwise
operators if we are going to give a more serious treatment to flag
sets? I think the only reason to use these is the legacy of using
numbers for flag sets. These days I prefer to use named constexpr
functions, similar to what I have here:
https://gist.github.com/rmartinho/5456207#file-bit_ops-cpp-L86.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
.
Author: Gabriel Dos Reis <gdr@axiomatics.org>
Date: Mon, 22 Jul 2013 09:09:47 -0500
Raw View
Sean Middleditch <sean@middleditch.us> writes:
| QFlags represents the best you can do now, which is exactly what I was
| getting at as a nasty workaround. I already know how to handle flags
| today; I'm not exactly new at this game. :)
As you may probably have noted, C++ has never been *perfect* at one
single thing. It's consistently been good, better than the average, at
many things, which one would argue is key to its strength.
I suspect a question that your previous messages have not addressed (at
least not adequately) is whether the need to be perfect at bitmask type
is so urgent that one needs to introduce a fourth or fifth way of
defining a type in C++ -- when there are good library solutions. Note,
this is not a frivolous question: if you want your proposal to gain
traction, you would need to answer it forcefully, convincingly, over and over.
-- Gaby
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
.
Author: Jeffrey Yasskin <jyasskin@google.com>
Date: Mon, 22 Jul 2013 07:56:12 -0700
Raw View
--047d7b67794ca15aa404e21ae0d6
Content-Type: text/plain; charset=ISO-8859-1
On Jul 22, 2013 12:26 AM, "Sean Middleditch" <sean@middleditch.us> wrote:
> Again, I'm all for library-only solutions. QFlags ain't it.
> std::bitset fails due to not using the type system at all (nothing
> stops you from accidentally combining flags1::value with flags2::value
> into the same bitset, and there's other issues involving how you'd
> define "combined flag" constants in an obvious clean way). Pasting
> together operator overloads for every set of flags is repetitive and
> error-pone.
Recall that I suggested an enum_set<enum_t> given an extension for
reflecting enum values, not just a raw bitset. It'd be easy to make
enum_set <enum_t>'s operator[] take an enum_t and not just an int. With
today's language you'd define combined constants with
auto combined = make_enum_set(value1) | make_enum_set(value2);
Or possibly make_enum_set(value1, value2) if the library wanted.
With the language extension I mentioned to define conversion operators, I
suspect you could just use "value1 | value2".
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
--047d7b67794ca15aa404e21ae0d6
Content-Type: text/html; charset=ISO-8859-1
Content-Transfer-Encoding: quoted-printable
<p dir=3D"ltr">On Jul 22, 2013 12:26 AM, "Sean Middleditch" <<=
a href=3D"mailto:sean@middleditch.us">sean@middleditch.us</a>> wrote:<br=
>
> Again, I'm all for library-only solutions. =A0QFlags ain't it.=
<br>
> std::bitset fails due to not using the type system at all (nothing<br>
> stops you from accidentally combining flags1::value with flags2::value=
<br>
> into the same bitset, and there's other issues involving how you&#=
39;d<br>
> define "combined flag" constants in an obvious clean way). =
=A0Pasting<br>
> together operator overloads for every set of flags is repetitive and<b=
r>
> error-pone.</p>
<p dir=3D"ltr">Recall that I suggested an enum_set<enum_t> given an e=
xtension for reflecting enum values, not just a raw bitset. It'd be eas=
y to make enum_set <enum_t>'s operator[] take an enum_t and not j=
ust an int. With today's language you'd define combined constants w=
ith<br>
=A0 auto combined =3D make_enum_set(value1) | make_enum_set(value2);</p>
<p dir=3D"ltr">Or possibly make_enum_set(value1, value2) if the library wan=
ted.</p>
<p dir=3D"ltr">With the language extension I mentioned to define conversion=
operators, I suspect you could just use "value1 | value2".</p>
<p></p>
-- <br />
<br />
--- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" group.<br />
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to std-proposals+unsubscribe@isocpp.org.<br />
To post to this group, send email to std-proposals@isocpp.org.<br />
Visit this group at <a href=3D"http://groups.google.com/a/isocpp.org/group/=
std-proposals/">http://groups.google.com/a/isocpp.org/group/std-proposals/<=
/a>.<br />
<br />
<br />
--047d7b67794ca15aa404e21ae0d6--
.
Author: Sean Middleditch <sean@middleditch.us>
Date: Mon, 22 Jul 2013 10:11:46 -0700
Raw View
On Mon, Jul 22, 2013 at 12:45 AM, Martinho Fernandes
<martinho.fernandes@gmail.com> wrote:
>
> I have written a library solution that uses a different approach a while back.
I like your approach. I'll have to give a try and see how it works
out in practice, but it looks very good. I hadn't though to use type
traits and enable_if to constrain generalized global operators for
this case.
I agree that using constexpr or higher level operators makes more
sense than bit operations. I've seen + and - used, but I've never
quite liked those; I can't put good words down as to why.
I also agree that a constexpr bit(n) or the like is better than the
binary literals. I've had no chance to really play with them (no
constexpr yet on a primary compiler our entire industry generally
targets) but I generally today use a macro like:
#define BIT(n) (1U << (n))
The constexpr function version of that combined with a constexpr "next
power of 2" (which is handy for _so_ many other things, of course)
would likely be more than sufficient for defining flagsets.
On Mon, Jul 22, 2013 at 7:09 AM, Gabriel Dos Reis <gdr@axiomatics.org> wrote:
> Sean Middleditch <sean@middleditch.us> writes:
> I suspect a question that your previous messages have not addressed (at
> least not adequately) is whether the need to be perfect at bitmask type
> is so urgent that one needs to introduce a fourth or fifth way of
> defining a type in C++ -- when there are good library solutions. Note,
> this is not a frivolous question: if you want your proposal to gain
> traction, you would need to answer it forcefully, convincingly, over and over.
I'm totally in _favor_ of a library solution, just (a) not one
requiring macros, extensive repetition, or awkwardness, (b) able to be
rolled into the standard library so it's there for everyone, and (c)
future resilient presuming modules and such become the norm 10 years
from now (hence no macros). I only propose language extensions if a
library can't be made to work for some reason. Especially language
changes that have few uses outside of one specific use case; those are
the worst burden of all.
I'd like to implement Martinho's solution in a larger project and see
how it settles with a team of other programmers, and come back to this
if it doesn't work out after some hands-on experience with it
(unfortunately we don't have constexpr on one of our teams' target
compilers).
I'm not terribly fond of needing to specialize templates to enforce
type traits (especially if they end up in namespace std in an
"official" implementation), same reasoning as in N3333 (proposing
ASL-friendly std::hash_value) and why rvalue references are a thing
instead of just using manual movable type traits like some of us did
in C++98. Is there an alternative to that in Martinho's example
without any new extensions?
On Mon, Jul 22, 2013 at 7:56 AM, Jeffrey Yasskin <jyasskin@google.com> wrote:
> On Jul 22, 2013 12:26 AM, "Sean Middleditch" <sean@middleditch.us> wrote:
>> Again, I'm all for library-only solutions. QFlags ain't it.
>> std::bitset fails due to not using the type system at all (nothing
>> stops you from accidentally combining flags1::value with flags2::value
>> into the same bitset, and there's other issues involving how you'd
>> define "combined flag" constants in an obvious clean way). Pasting
>> together operator overloads for every set of flags is repetitive and
>> error-pone.
>
> Recall that I suggested an enum_set<enum_t> given an extension for
> reflecting enum values, not just a raw bitset. It'd be easy to make enum_set
> <enum_t>'s operator[] take an enum_t and not just an int. With today's
> language you'd define combined constants with
> auto combined = make_enum_set(value1) | make_enum_set(value2);
>
> Or possibly make_enum_set(value1, value2) if the library wanted.
>
> With the language extension I mentioned to define conversion operators, I
> suspect you could just use "value1 | value2".
>
> --
>
> ---
> You received this message because you are subscribed to a topic in the
> Google Groups "ISO C++ Standard - Future Proposals" group.
> To unsubscribe from this topic, visit
> https://groups.google.com/a/isocpp.org/d/topic/std-proposals/1RPxJSJ_0z8/unsubscribe.
> To unsubscribe from this group and all its topics, send an email to
> std-proposals+unsubscribe@isocpp.org.
> To post to this group, send email to std-proposals@isocpp.org.
> Visit this group at
> http://groups.google.com/a/isocpp.org/group/std-proposals/.
>
>
--
Sean Middleditch
http://seanmiddleditch.com
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
.
Author: Richard Smith <richard@metafoo.co.uk>
Date: Mon, 22 Jul 2013 12:14:29 -0700
Raw View
--089e013c5b0a51022e04e21e7c18
Content-Type: text/plain; charset=ISO-8859-1
On Mon, Jul 22, 2013 at 12:26 AM, Sean Middleditch <sean@middleditch.us>wrote:
> QFlags represents the best you can do now, which is exactly what I was
> getting at as a nasty workaround. I already know how to handle flags
> today; I'm not exactly new at this game. :)
>
> ** QFlags and its ilk is the problem I'd like to solve here, not the
> solution. **
I think drilling into the specific problems with QFlags would be very
instructive here. I don't yet see any showstopper problems.
> Macros,
QFlags has two macros. One of them just expands to "typedef QFlags<Foo>
Bar;", and there seems to be no problem with writing that directly. The
other is more interesting: it provides an operator| for the underlying enum
type, that produces a QFlags value rather than an integer. This seems
tricky to handle without a core language change.
> no proper scoping of values,
You can write flag_values::first and so on. That's admittedly suboptimal,
because the flags type and the enum type have different names. That can be
fixed with a slightly different mechanism, such as:
// implementation:
template<typename T> struct flags : T {
typename T::type value;
flags(typename T::type);
friend flags operator|(flags a, flags b);
// ...
explicit operator typename std::underlying_type<typename T::type>::type()
const;
};
// usage:
struct my_flags_impl {
enum type {
none = 0,
first = 0x1,
second = 0x2,
third = 0x4
};
};
using my_flags = flags<my_flags_impl>;
my_flags f = my_flags::first;
my_flags g = (f | my_flags::second) & ~my_flags::third;
doesn't use the new C++11 enum classes,
That would be the tail wagging the dog. Who cares? Also, I don't see any
reason why it *wouldn't* work with enum classes.
> can't be used with forward-declared enums,
Why not? The above approach would work fine with a forward-declared enum.
Depending on the resolution of core issue 1485, there's another approach we
can take:
// implementation
template<typename Underlying, typename Tag> struct flags {
enum type : Underlying;
flags(type);
friend flags operator|(flags, flags);
friend flags operator|(type, type);
// ...
};
// use
struct my_flags_tag;
using my_flags = flags<int, my_flags_tag>;
template<> enum my_flags::type : int {
none = 0,
first = 0x1,
second = 0x2,
third = 0x4
};
This approach works today with Clang.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
--089e013c5b0a51022e04e21e7c18
Content-Type: text/html; charset=ISO-8859-1
Content-Transfer-Encoding: quoted-printable
On Mon, Jul 22, 2013 at 12:26 AM, Sean Middleditch <span dir=3D"ltr"><<a=
href=3D"mailto:sean@middleditch.us" target=3D"_blank">sean@middleditch.us<=
/a>></span> wrote:<br><div class=3D"gmail_quote"><blockquote class=3D"gm=
ail_quote" style=3D"margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-le=
ft:1ex">
QFlags represents the best you can do now, which is exactly what I was<br>
getting at as a nasty workaround. =A0I already know how to handle flags<br>
today; I'm not exactly new at this game. :)<br>
<br>
** QFlags and its ilk is the problem I'd like to solve here, not the<br=
>
solution. **</blockquote><div><br></div><div>I think drilling into the spec=
ific problems with QFlags would be very instructive here. I don't yet s=
ee any showstopper problems.</div><div>=A0</div><blockquote class=3D"gmail_=
quote" style=3D"margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1=
ex">
Macros,</blockquote><div><br></div><div>QFlags has two macros. One of them =
just expands to "typedef QFlags<Foo> Bar;", and there seems=
to be no problem with writing that directly. The other is more interesting=
: it provides an operator| for the underlying enum type, that produces a QF=
lags value rather than an integer. This seems tricky to handle without a co=
re language change.</div>
<div>=A0</div><blockquote class=3D"gmail_quote" style=3D"margin:0 0 0 .8ex;=
border-left:1px #ccc solid;padding-left:1ex"> no proper scoping of values,<=
/blockquote><div><br></div><div>You can write flag_values::first and so on.=
That's admittedly suboptimal, because the flags type and the enum type=
have different names. That can be fixed with a slightly different mechanis=
m, such as:</div>
<div><br></div><div>// implementation:</div><div>template<typename T>=
struct flags : T {</div><div>=A0 typename T::type value;</div><div>=A0 fla=
gs(typename T::type);</div><div>=A0 friend flags operator|(flags a, flags b=
);</div>
<div>=A0 // ...</div><div>=A0 explicit operator typename std::underlying_ty=
pe<typename T::type>::type() const;</div><div>};</div><div><br></div>=
<div>// usage:</div><div>struct my_flags_impl {</div><div>=A0 enum type {</=
div>
<div>=A0 =A0 none =3D 0,</div><div>=A0 =A0 first =3D 0x1,</div><div>=A0 =A0=
second =3D 0x2,</div><div>=A0 =A0 third =3D 0x4</div><div>=A0 };</div><div=
>};</div><div>using my_flags =3D flags<my_flags_impl>;</div><div><br>=
</div><div>my_flags f =3D my_flags::first;</div>
<div>my_flags g =3D (f | my_flags::second) & ~my_flags::third;</div><di=
v><br></div><blockquote class=3D"gmail_quote" style=3D"margin:0 0 0 .8ex;bo=
rder-left:1px #ccc solid;padding-left:1ex">doesn't use the new=A0C++11 =
enum classes,</blockquote>
<div><br></div><div>That would be the tail wagging the dog. Who cares? Also=
, I don't see any reason why it *wouldn't* work with enum classes.<=
/div><div>=A0</div><blockquote class=3D"gmail_quote" style=3D"margin:0 0 0 =
..8ex;border-left:1px #ccc solid;padding-left:1ex">
can't be used with forward-declared enums,</blockquote><div><br></div><=
div>Why not? The above approach would work fine with a forward-declared enu=
m.</div><div><br></div><div><br></div><div>Depending on the resolution of c=
ore issue 1485, there's another approach we can take:</div>
<div><br></div><div>// implementation</div><div>template<typename Underl=
ying, typename Tag> struct flags {</div><div>=A0 enum type : Underlying;=
</div><div>=A0 flags(type);</div><div>=A0 friend flags operator|(flags, fla=
gs);</div>
<div>=A0 friend flags operator|(type, type);</div><div>=A0 // ...</div><div=
>};</div><div><br></div><div>// use</div><div>struct my_flags_tag;</div><di=
v>using my_flags =3D flags<int, my_flags_tag>;</div><div>template<=
> enum my_flags::type : int {</div>
<div>=A0 none =3D 0,</div><div>=A0 first =3D 0x1,</div><div>=A0 second =3D =
0x2,</div><div>=A0 third =3D 0x4</div><div>};</div><div><br></div><div>This=
approach works today with Clang.</div></div>
<p></p>
-- <br />
<br />
--- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" group.<br />
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to std-proposals+unsubscribe@isocpp.org.<br />
To post to this group, send email to std-proposals@isocpp.org.<br />
Visit this group at <a href=3D"http://groups.google.com/a/isocpp.org/group/=
std-proposals/">http://groups.google.com/a/isocpp.org/group/std-proposals/<=
/a>.<br />
<br />
<br />
--089e013c5b0a51022e04e21e7c18--
.
Author: Sean Middleditch <sean.middleditch@gmail.com>
Date: Thu, 25 Jul 2013 10:11:08 -0700 (PDT)
Raw View
------=_Part_21_14989894.1374772268839
Content-Type: text/plain; charset=ISO-8859-1
On Monday, July 22, 2013 12:14:29 PM UTC-7, Richard Smith wrote:
> // use
> struct my_flags_tag;
> using my_flags = flags<int, my_flags_tag>;
> template<> enum my_flags::type : int {
> none = 0,
> first = 0x1,
> second = 0x2,
> third = 0x4
> };
>
I had absolutely no idea this kind of use of forward-declared enums in
templates was legal, but in retrospect, it seems obvious. Excellent. What
would use of this type look like?
auto foo = my_flags::type::second; // is that 'type' necessary there?
Following up then, is there maybe a call for the constexpr helpers
mentioned earlier being in the standard library? std::bit,
std::next_power_of_two/std::next_bit, etc (with better names of course).
Some of these are super trivial to just write (like a constexpr bit), but
others are just enough code to get obnoxious even with C++14 constexpr if I
had to rewrite for every sample, helper library, and so on (like constexpr
next_next_of_two).
>
> This approach works today with Clang.
>
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
------=_Part_21_14989894.1374772268839
Content-Type: text/html; charset=ISO-8859-1
Content-Transfer-Encoding: quoted-printable
On Monday, July 22, 2013 12:14:29 PM UTC-7, Richard Smith wrote:<br><blockq=
uote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;border-lef=
t: 1px #ccc solid;padding-left: 1ex;"><div class=3D"gmail_quote"><div>// us=
e</div><div>struct my_flags_tag;</div><div>using my_flags =3D flags<int,=
my_flags_tag>;</div><div>template<> enum my_flags::type : int {</=
div>
<div> none =3D 0,</div><div> first =3D 0x1,</div><div> se=
cond =3D 0x2,</div><div> third =3D 0x4</div><div>};</div></div></bloc=
kquote><div><br></div><div>I had absolutely no idea this kind of use of for=
ward-declared enums in templates was legal, but in retrospect, it seems obv=
ious. Excellent. What would use of this type look like?</div><d=
iv><br></div><div> auto foo =3D my_flags::type::second; // is that 't=
ype' necessary there?</div><div><br></div><div>Following up then, is there =
maybe a call for the constexpr helpers mentioned earlier being in the stand=
ard library? std::bit, std::next_power_of_two/std::next_bit, etc (wit=
h better names of course). Some of these are super trivial to just wr=
ite (like a constexpr bit), but others are just enough code to get obnoxiou=
s even with C++14 constexpr if I had to rewrite for every sample, helper li=
brary, and so on (like constexpr next_next_of_two).</div><div> </div><=
blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;bord=
er-left: 1px #ccc solid;padding-left: 1ex;"><div class=3D"gmail_quote"><div=
><br></div><div>This approach works today with Clang.</div></div>
</blockquote>
<p></p>
-- <br />
<br />
--- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" group.<br />
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to std-proposals+unsubscribe@isocpp.org.<br />
To post to this group, send email to std-proposals@isocpp.org.<br />
Visit this group at <a href=3D"http://groups.google.com/a/isocpp.org/group/=
std-proposals/">http://groups.google.com/a/isocpp.org/group/std-proposals/<=
/a>.<br />
<br />
<br />
------=_Part_21_14989894.1374772268839--
.
Author: Richard Smith <richard@metafoo.co.uk>
Date: Thu, 25 Jul 2013 11:39:11 -0700
Raw View
--047d7b5db9f4a1177604e25a573e
Content-Type: text/plain; charset=ISO-8859-1
On Thu, Jul 25, 2013 at 10:11 AM, Sean Middleditch <
sean.middleditch@gmail.com> wrote:
> On Monday, July 22, 2013 12:14:29 PM UTC-7, Richard Smith wrote:
>
>> // use
>> struct my_flags_tag;
>> using my_flags = flags<int, my_flags_tag>;
>> template<> enum my_flags::type : int {
>> none = 0,
>> first = 0x1,
>> second = 0x2,
>> third = 0x4
>> };
>>
>
> I had absolutely no idea this kind of use of forward-declared enums in
> templates was legal, but in retrospect, it seems obvious. Excellent. What
> would use of this type look like?
>
> auto foo = my_flags::type::second; // is that 'type' necessary there?
>
The ::type is not necessary. The purpose of using an explicit
specialization of the enum, rather than some more conventional mechanism,
is to inject those names into the specialization of the surrounding class
template specialization. Core issue 1485 might make the above mechanism
ill-formed, but isn't resolved yet, so now would be the time to provide a
compelling reason to keep this valid.
Following up then, is there maybe a call for the constexpr helpers
> mentioned earlier being in the standard library? std::bit,
> std::next_power_of_two/std::next_bit, etc (with better names of course).
> Some of these are super trivial to just write (like a constexpr bit), but
> others are just enough code to get obnoxious even with C++14 constexpr if I
> had to rewrite for every sample, helper library, and so on (like constexpr
> next_next_of_two).
>
I think this would make sense, especially for operations which can be
mapped to a single instruction on some hardware (count_set_bits,
count_trailing_zeroes, ...). If the user writes these, it's not easy to get
them to be both valid in constant expressions and efficient outside
constant expressions (if you can't rely on inline asm or compiler builtins,
you've got to hope your optimizer recognizes the code you write and
replaces it with the right operation). If they're part of the
implementation, it's trivial.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
--047d7b5db9f4a1177604e25a573e
Content-Type: text/html; charset=ISO-8859-1
Content-Transfer-Encoding: quoted-printable
On Thu, Jul 25, 2013 at 10:11 AM, Sean Middleditch <span dir=3D"ltr"><<a=
href=3D"mailto:sean.middleditch@gmail.com" target=3D"_blank">sean.middledi=
tch@gmail.com</a>></span> wrote:<br><div class=3D"gmail_quote"><blockquo=
te class=3D"gmail_quote" style=3D"margin:0 0 0 .8ex;border-left:1px #ccc so=
lid;padding-left:1ex">
<div class=3D"im">On Monday, July 22, 2013 12:14:29 PM UTC-7, Richard Smith=
wrote:<br><blockquote class=3D"gmail_quote" style=3D"margin:0;margin-left:=
0.8ex;border-left:1px #ccc solid;padding-left:1ex"><div class=3D"gmail_quot=
e"><div>
// use</div><div>struct my_flags_tag;</div><div>using my_flags =3D flags<=
;int, my_flags_tag>;</div><div>template<> enum my_flags::type : in=
t {</div>
<div>=A0 none =3D 0,</div><div>=A0 first =3D 0x1,</div><div>=A0 second =3D =
0x2,</div><div>=A0 third =3D 0x4</div><div>};</div></div></blockquote><div>=
<br></div></div><div>I had absolutely no idea this kind of use of forward-d=
eclared enums in templates was legal, but in retrospect, it seems obvious. =
=A0Excellent. =A0What would use of this type look like?</div>
<div><br></div><div>=A0 auto foo =3D my_flags::type::second; // is that =
9;type' necessary there?</div></blockquote><div><br></div><div>The ::ty=
pe is not necessary. The purpose of using an explicit specialization of the=
enum, rather than some more conventional mechanism, is to inject those nam=
es into the specialization of the surrounding class template specialization=
.. Core issue 1485 might make the above mechanism ill-formed, but isn't =
resolved yet, so now would be the time to provide a compelling reason to ke=
ep this valid.</div>
<div><br></div><blockquote class=3D"gmail_quote" style=3D"margin:0 0 0 .8ex=
;border-left:1px #ccc solid;padding-left:1ex"><div>Following up then, is th=
ere maybe a call for the constexpr helpers mentioned earlier being in the s=
tandard library? =A0std::bit, std::next_power_of_two/std::next_bit, etc (wi=
th better names of course). =A0Some of these are super trivial to just writ=
e (like a constexpr bit), but others are just enough code to get obnoxious =
even with C++14 constexpr if I had to rewrite for every sample, helper libr=
ary, and so on (like constexpr next_next_of_two).</div>
</blockquote><div><br></div><div>I think this would make sense, especially =
for operations which can be mapped to a single instruction on some hardware=
(count_set_bits, count_trailing_zeroes, ...). If the user writes these, it=
's not easy to get them to be both valid in constant expressions and ef=
ficient outside constant expressions (if you can't rely on inline asm o=
r compiler builtins, you've got to hope your optimizer recognizes the c=
ode you write and replaces it with the right operation). If they're par=
t of the implementation, it's trivial.</div>
</div>
<p></p>
-- <br />
<br />
--- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" group.<br />
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to std-proposals+unsubscribe@isocpp.org.<br />
To post to this group, send email to std-proposals@isocpp.org.<br />
Visit this group at <a href=3D"http://groups.google.com/a/isocpp.org/group/=
std-proposals/">http://groups.google.com/a/isocpp.org/group/std-proposals/<=
/a>.<br />
<br />
<br />
--047d7b5db9f4a1177604e25a573e--
.