Make (strongly typed) enums castable to char const * const

506 views
Skip to first unread message

Dominic Fandrey

unread,
Aug 25, 2016, 9:47:34 AM8/25/16
to std-pr...@isocpp.org
I searched this list for similar suggestions, but of course I may
have overlooked something.

I frequently use the following pattern:

    enum class FooBar { KEKS, DOSE };
    char const * const FooBarStr[]{"KEKS", "DOSE"};

I use the strings to in error messages, verbose output, etc.

    FooBarStr[static_cast<int>(FooBar::KEKS)] // "KEKS"

This has some disadvantages, e.g. enums with explicitly stated values
may have gaps, negative values and may be defined out of order.
Also, changing the enum also means changing the strings manually.

What I'd like to see:

    static_cast<char const *>(FooBar::KEKS) // "KEKS"

I think this would be simple to add to an existing compiler and it's
very unlikely to clash with existing code, because you have to jump
through some hoops to cast a strongly typed enum to a pointer type.

--
A: Because it fouls the order in which people normally read text.
Q: Why is top-posting such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?


Ville Voutilainen

unread,
Aug 25, 2016, 10:12:58 AM8/25/16
to ISO C++ Standard - Future Proposals
On 25 August 2016 at 16:47, Dominic Fandrey <kami...@bsdforen.de> wrote:
> I searched this list for similar suggestions, but of course I may
> have overlooked something.
>
> I frequently use the following pattern:
>
> enum class FooBar { KEKS, DOSE };
> char const * const FooBarStr[]{"KEKS", "DOSE"};
>
> I use the strings to in error messages, verbose output, etc.
>
> FooBarStr[static_cast<int>(FooBar::KEKS)] // "KEKS"
>
> This has some disadvantages, e.g. enums with explicitly stated values
> may have gaps, negative values and may be defined out of order.
> Also, changing the enum also means changing the strings manually.
>
> What I'd like to see:
>
> static_cast<char const *>(FooBar::KEKS) // "KEKS"


See http://open-std.org/JTC1/SC22/WG21/docs/papers/2016/p0194r1.html

Derek Ross

unread,
Aug 25, 2016, 11:17:12 AM8/25/16
to ISO C++ Standard - Future Proposals
Note that enums can have duplicate values, which throws a wrench into the reflection works.  E.g.:

enum class FooBar { KEKS = 2, DOSE = 2 };

What will the result be for:

FooBar fb = FooBar::KEKS;
cout
<< static_cast<char const*>(fb) ;

Larry Evans

unread,
Aug 25, 2016, 11:41:28 AM8/25/16
to std-pr...@isocpp.org
Could you be more specific. I grepped for enumName, for char const*,
and scanned briefly for other strings that might show where
conversion from enum to char const* might be, without success :(


Jeffrey Yasskin

unread,
Aug 25, 2016, 11:52:23 AM8/25/16
to std-pr...@isocpp.org
In that proposal, the operation you want is spelled get_name_v<reflexpr(FooBar::KEKS)>.

Jeffrey 

D. B.

unread,
Aug 25, 2016, 11:55:53 AM8/25/16
to std-pr...@isocpp.org
On Thu, Aug 25, 2016 at 4:17 PM, Derek Ross <antiq...@gmail.com> wrote:
Note that enums can have duplicate values, which throws a wrench into the reflection works.  E.g.:

enum class FooBar { KEKS = 2, DOSE = 2 };

What will the result be for:

FooBar fb = FooBar::KEKS;
cout
<< static_cast<char const*>(fb) ;

that only "throws a wrench into the reflection works" as proposed by the OP, by no means generally. the WG proposal linked (A) would not support this because it is static and (B) has presumably already accounted for such possibilities and effortlessly gets around them with compile-time wizardry... again, presumably.

Dominic Fandrey

unread,
Aug 25, 2016, 2:12:04 PM8/25/16
to std-pr...@isocpp.org
> wizardry... again, *presumably.*
>

It's not really useful for what I'm doing if it's not runtime capable.

For multiple mentions I'd just leave it implementation defined or
establish a simple first definition rule.

I am assuming that in most cases overlapping definitions are aliases:

    enum class Flags { NONE = 0, COMPATIBLE = 1, COMPAT = 1, EXPERIMENTAL = 2 };

D. B.

unread,
Aug 25, 2016, 2:21:55 PM8/25/16
to std-pr...@isocpp.org
On Thu, Aug 25, 2016 at 7:10 PM, Dominic Fandrey <kami...@bsdforen.de> wrote:
On Thu, 2016-08-25 at 16:55 +0100, D. B. wrote:
> On Thu, Aug 25, 2016 at 4:17 PM, Derek Ross <antiq...@gmail.com> wrote:
>
> >
> > Note that enums can have duplicate values, which throws a wrench into the
> > reflection works.  E.g.:
> >
> > enum class FooBar { KEKS = 2, DOSE = 2 };
> >
> > What will the result be for:
> >
> > FooBar fb = FooBar::KEKS;
> > cout << static_cast<char const*>(fb) ;
> >
> that only "throws a wrench into the reflection works" as proposed by the
> OP, by no means generally. the WG proposal linked (A) would not support
> this because it is static and (B) has presumably already accounted for such
> possibilities and effortlessly gets around them with compile-time
> wizardry... again, *presumably.*
>

It's not really useful for what I'm doing if it's not runtime capable.


Then I am not an 'insider' and might well be wrong... but based on how static C++ is, I would not expect such a feature to arrive, or really any other dynamic features, beyond the 'necessary evils' of dynamic_cast/typeinfo. 

Thiago Macieira

unread,
Aug 25, 2016, 2:36:56 PM8/25/16
to std-pr...@isocpp.org, D. B.
Em quinta-feira, 25 de agosto de 2016, às 19:21:52 PDT, D. B. escreveu:
> On Thu, Aug 25, 2016 at 7:10 PM, Dominic Fandrey <kami...@bsdforen.de>
> > It's not really useful for what I'm doing if it's not runtime capable.
>
> Then I am not an 'insider' and might well be wrong... but based on how
> static C++ is, I would not expect such a feature to arrive, or really any
> other dynamic features, beyond the 'necessary evils' of
> dynamic_cast/typeinfo.

Dynamic reflection is to be a library solution on top of the core language
static reflection.

--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center

Jeffrey Yasskin

unread,
Aug 25, 2016, 2:39:59 PM8/25/16
to std-pr...@isocpp.org
On Thu, Aug 25, 2016 at 11:10 AM, Dominic Fandrey <kami...@bsdforen.de> wrote:
On Thu, 2016-08-25 at 16:55 +0100, D. B. wrote:
> On Thu, Aug 25, 2016 at 4:17 PM, Derek Ross <antiq...@gmail.com> wrote:
>
> >
> > Note that enums can have duplicate values, which throws a wrench into the
> > reflection works.  E.g.:
> >
> > enum class FooBar { KEKS = 2, DOSE = 2 };
> >
> > What will the result be for:
> >
> > FooBar fb = FooBar::KEKS;
> > cout << static_cast<char const*>(fb) ;
> >
> that only "throws a wrench into the reflection works" as proposed by the
> OP, by no means generally. the WG proposal linked (A) would not support
> this because it is static and (B) has presumably already accounted for such
> possibilities and effortlessly gets around them with compile-time
> wizardry... again, *presumably.*
>

It's not really useful for what I'm doing if it's not runtime capable.

For multiple mentions I'd just leave it implementation defined or
establish a simple first definition rule.

I am assuming that in most cases overlapping definitions are aliases:

    enum class Flags { NONE = 0, COMPATIBLE = 1, COMPAT = 1, EXPERIMENTAL = 2 };
 
The static reflection proposal lets you iterate at compile time over all the enum values inside an enumeration, which lets you write a generic library function to do what you want. That function will have to pick an answer to FlagsString(1), and we're hoping that some libraries appear to show us some options before we put a choice into the standard library.

Jeffrey

Moritz Klammler

unread,
Aug 25, 2016, 2:59:52 PM8/25/16
to std-pr...@isocpp.org
>> It's not really useful for what I'm doing if it's not runtime
>> capable.
>
> Then I am not an 'insider' and might well be wrong... but based on how
> static C++ is, I would not expect such a feature to arrive, or really
> any other dynamic features, beyond the 'necessary evils' of
> dynamic_cast/typeinfo.

Well, you'd use `get_enum_values` to obtain all declared values, use
some TMP to build an array of the corresponding names, and then look up
your run-time value in that array. Wrap into a function and you're good
to go. I presume that writing a generic solution for this (working for
all enumeration types) would be fairly straight-forward.

Anyway, I don't think that the fact that static reflection -- should it
ever enter the language -- *could* be used to solve a problem means that
we cannot include a feature *now* that does the same task in a more
accessible manner. (As I've said before, I'd prefer a library solution,
not `static_cast` magic.) If standard library implementors can
re-implement that feature as a thin wrapper around static reflection in
the future, that's fine. Looking up a human-readable name for an
enumeration constant is a reasonable thing a beginner C++ programmer
might want to do. Writing template meta-programs using static
reflection is not an option for them and still not very convenient for
those programmers who would in theory be able to do it.

If you want an analogy, we already have, for example,
`std::underlying_type` in the standard library now and I don't think
that this is a potentially bad thing.

Moritz Klammler

unread,
Aug 25, 2016, 3:08:24 PM8/25/16
to std-pr...@isocpp.org
Oops, I've written "as I've said before" in my last post but just
realized that I've failed to post to the list and accidentally only
replied to the OP then. So here is my initial reply again for the
public.

I do think that this is a frequent issue and C++ ought to provide a
solution for it. However, I think that a more comprehensive approach is
needed that also addresses the other conveniences that users of, say,
Java are used to. Most importantly, obtaining an array of all
enumeration constants. I have other useful features in mind (such as
the inverse of your function, looking up an enumeration constant
by-name) but they can be implemented in terms of the mentioned features.
I don't think that `static_cast`ing offers a pretty solution to these
use-cases. Perhaps worst of all, it cannot be done in a library so
users cannot extend it for their own code. If you are interested in
expanding the scope of your proposal, I'm looking forward to
collaborating with you on a proposal for this issue. I'm currently
working on a code-generation approach to enumerations in order to
explore the space of useful features but I got somewhat distracted on
this.

Nicol Bolas

unread,
Aug 25, 2016, 3:38:58 PM8/25/16
to ISO C++ Standard - Future Proposals, mor...@klammler.eu
On Thursday, August 25, 2016 at 2:59:52 PM UTC-4, Moritz Klammler wrote:
>> It's not really useful for what I'm doing if it's not runtime
>> capable.
>
> Then I am not an 'insider' and might well be wrong... but based on how
> static C++ is, I would not expect such a feature to arrive, or really
> any other dynamic features, beyond the 'necessary evils' of
> dynamic_cast/typeinfo.

Well, you'd use `get_enum_values` to obtain all declared values, use
some TMP to build an array of the corresponding names, and then look up
your run-time value in that array.  Wrap into a function and you're good
to go.  I presume that writing a generic solution for this (working for
all enumeration types) would be fairly straight-forward.

Anyway, I don't think that the fact that static reflection -- should it
ever enter the language -- *could* be used to solve a problem means that
we cannot include a feature *now* that does the same task in a more
accessible manner.  (As I've said before, I'd prefer a library solution,
not `static_cast` magic.)  If standard library implementors can
re-implement that feature as a thin wrapper around static reflection in
the future, that's fine.  Looking up a human-readable name for an
enumeration constant is a reasonable thing a beginner C++ programmer
might want to do.  Writing template meta-programs using static
reflection is not an option for them and still not very convenient for
those programmers who would in theory be able to do it.

The problem is that it's not a library feature. Well, not yet. Until static reflection happens, it is impossible to implement these sorts of things without either compiler magic or macros.

And therefore, you're not really asking for a library feature. You're asking for a compiler feature, that just so happens to be exposed via the library.

Obviously there are several places where it's impossible to implement a standard library feature without using compiler hooks. The majority of chapter 18, and many of the type traits rely on such things. But it's important to recognize that what you're asking for is not a normal library feature. It's not, "here's a library that I've implemented, let's do that." That makes it a bit more complex.

I would say that the best thing your proposal could do is be defined in terms of static reflection. It's not so much that your proposal should require it. But at all times, you're making sure that the library interface you develop could eventually be implemented using static reflection.

And this would probably help the static reflection proposal too, since they've got to make sure that the tools people need to do things like this are available.

The other thing that's important to keep in mind is that runtime reflection takes up memory. Those strings for enums have to come from somewhere. Those arrays of enumeration values have to live somewhere. Whatever library system you come up with needs to have a way to deal with that. You cannot just force every enumerator in a program to have runtime reflected data.

Moritz Klammler

unread,
Aug 25, 2016, 4:21:48 PM8/25/16
to std-pr...@isocpp.org
> The problem is that it's not a library feature. Well, not yet. Until
> static reflection happens, it is *impossible* to implement these sorts
> of things without either compiler magic or macros.
>
> And therefore, you're not really asking for a library feature. You're
> asking for a *compiler* feature, that just so happens to be exposed
> via the library.

Yes, that's what I mean, even though I didn't spell it out very
explicitly.

> I would say that the best thing your proposal could do is be defined
> *in terms of static reflection*. It's not so much that your proposal
> should *require* it. But at all times, you're making sure that the
> library interface you develop could *eventually* be implemented using
> static reflection.

I'm not the OP but I find this an interesting idea I might follow. Do
you happen to know of any place where we can already play with static
reflection as currently proposed? It would be immensely useful for this
task.

> The other thing that's important to keep in mind is that runtime
> reflection takes up *memory*. Those strings for enums have to come
> from somewhere. Those arrays of enumeration values have to live
> somewhere. Whatever library system you come up with needs to have a
> way to deal with that. You cannot just force every enumerator in a
> program to have runtime reflected data.

Indeed. Therefore, I think that using the template machinery for this
would be a good thing. If your program never instantiates
`std::to_string<MyEnum>` (or whatever it is called), no string table of
`MyEnum`s names has to be set aside. And if you do instantiate it, you
just pay for what you've explicitly asked for. While static analysis
could also be used to do this when implemented differently, compilers
already do the right thing for templates so it wouldn't add extra
complexity.

Dominic Fandrey

unread,
Aug 26, 2016, 5:11:55 AM8/26/16
to std-pr...@isocpp.org
That sounds fine to me, I prefer a generic approach over my limited
proposal. I guess it wouldn't be too difficult to build a nested type
containing the string and the enum value and a constexpr function
template that iterates through the type and returns the string for the
first enum match.

You could also build one that would return "COMPATIBLE|EXPERIMENTAL"
for Flags{3}, for enums that only have powers of two values. But that
would probably result in the creation in n! strings, so it might not
be a great idea (even if n is usually limited to 64).

Vicente J. Botet Escriba

unread,
Aug 26, 2016, 7:10:20 AM8/26/16
to std-pr...@isocpp.org
I'm working on a proposal for Ordinal types (types that are isomorphic to 0..N. We could define an ordinal_set<Ordinal>, or an ordinal_array<T, ordinal> or ordinal_range<Ordinal>.

Enums having all enumeration with a different value would be seen as Ordinal. Associated to this enum could be a tag that defines the algorithm to do the mappings. Possible mappings are arithmetic, logarithmic or explicit. This tag can be calculated at compile time from the static reflection library.

Defining a run-time function toString that takes such a ordinal enum type and returns a const char * will be quite simple, once we are able to have the position of a specific enumeration and the static reflection library.

More soon.

Vicente


Ricardo Fabiano de Andrade

unread,
Aug 26, 2016, 11:51:56 AM8/26/16
to std-pr...@isocpp.org
The author of static reflection has been working on an implementation in clang:

It's still a work in progress and few things are little bit different from the latest revision of the proposal.
But if I'm not wrong you should be able to list all the names and values within an enum as suggested by others.



--
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-pr...@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/87vayosip3.fsf%40gmail.com.

szollos...@gmail.com

unread,
Aug 26, 2016, 7:30:57 PM8/26/16
to ISO C++ Standard - Future Proposals
Hi,

I hate to troll in (esp. since I'm top-posting :) ), but you know that you can achieve this using macro-metaprogrammed FOREACH(); or, if you're okay with uintptr_t values and don't care about enum values, you can use string addresses (when you put strings in a struct using a macro), which is way more readable?

Regards,
-lorro

dgutson .

unread,
Aug 26, 2016, 8:11:47 PM8/26/16
to std-proposals

El 26/8/2016 20:30, <szollos...@gmail.com> escribió:
>
> Hi,
>
> I hate to troll in (esp. since I'm top-posting :) ), but you know that you can achieve this using macro-metaprogrammed FOREACH(); or, if you're okay with uintptr_t values and don't care about enum values, you can use string addresses (when you put strings in a struct using a macro), which is way more readable?

Even better, x-macros https://en.wikipedia.org/wiki/X_Macro?wprov=sfla1

>
> Regards,
> -lorro
>
>
> 2016. augusztus 25., csütörtök 15:47:34 UTC+2 időpontban Kamikaze Dominic Fandrey a következőt írta:
>>
>> I searched this list for similar suggestions, but of course I may
>> have overlooked something.
>>
>> I frequently use the following pattern:
>>
>>     enum class FooBar { KEKS, DOSE };
>>     char const * const FooBarStr[]{"KEKS", "DOSE"};
>>
>> I use the strings to in error messages, verbose output, etc.
>>
>>     FooBarStr[static_cast<int>(FooBar::KEKS)] // "KEKS"
>>
>> This has some disadvantages, e.g. enums with explicitly stated values
>> may have gaps, negative values and may be defined out of order.
>> Also, changing the enum also means changing the strings manually.
>>
>> What I'd like to see:
>>
>>     static_cast<char const *>(FooBar::KEKS) // "KEKS"
>>
>> I think this would be simple to add to an existing compiler and it's
>> very unlikely to clash with existing code, because you have to jump
>> through some hoops to cast a strongly typed enum to a pointer type.
>>
>> --
>> A: Because it fouls the order in which people normally read text.
>> Q: Why is top-posting such a bad thing?
>> A: Top-posting.
>> Q: What is the most annoying thing on usenet and in e-mail?
>>
>>

> --
> 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-proposal...@isocpp.org.


> To post to this group, send email to std-pr...@isocpp.org.

> To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/a057117d-4d32-4f39-ac2b-ada996a75ec3%40isocpp.org.

choc...@gmail.com

unread,
Aug 27, 2016, 2:02:19 AM8/27/16
to ISO C++ Standard - Future Proposals
Author of P0194Rx here.

Besides the clang fork, where you already can list all the members of an enum (with the `std::meta::get_enumerators_v` operation) and get the name of the enumerator (the `get_base_name_v` operation) and get the constant value of the enumerator (the `get_constant_v` operation), there is also a set of higher-level, experimental libraries built on top of the clang fork: https://github.com/matus-chochlik/mirror . It's still  W.I.P. and without docs, but you can have a look at the examples.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.

Moritz Klammler

unread,
Aug 27, 2016, 5:46:13 AM8/27/16
to std-pr...@isocpp.org
choc...@gmail.com writes:

> Author of P0194Rx here.
>
> Besides the clang fork, where you already can list all the members of
> an enum (with the `std::meta::get_enumerators_v` operation) and get
> the name of the enumerator (the `get_base_name_v` operation) and get
> the constant value of the enumerator (the `get_constant_v` operation),
> there is also a set of higher-level, experimental libraries built on
> top of the clang fork: https://github.com/matus-chochlik/mirror . It's
> still W.I.P. and without docs, but you can have a look at the
> examples.

Thanks a lot; I'll be looking into these.

Moritz Klammler

unread,
Aug 27, 2016, 5:52:57 AM8/27/16
to std-pr...@isocpp.org
szollos...@gmail.com writes:

> Hi,
>
> I hate to troll in (esp. since I'm top-posting :) ), but you know that
> you can achieve this using macro-metaprogrammed FOREACH(); or, if
> you're okay with uintptr_t values and don't care about enum values,
> you can use string addresses (when you put strings in a struct using a
> macro), which is way more readable?
>
> Regards,
> -lorro


There are many hacks you can use to work around many current
limitations. Besides aesthetic concerns about macro magic, the biggest
drawback I see is that it is something the library author explicitly has
to offer. More redundant code to write means more chances for bugs and
subtly or not so subtly diverging solutions in different libraries. A
general solution where the *consumer* of any library or other component
could just *ask* for the name of an enumerator instead of relying on the
library author to provide a feature for obtaining the desired
information seems sufficiently superior to me to push for such a
feature in the standard.

Avi Kivity

unread,
Aug 28, 2016, 12:35:32 PM8/28/16
to std-pr...@isocpp.org
Likely, he wants to index the array with a non-constexpr value.  So he'd need code to create a static array (or unordered_map, if the enum is sparse) for a dynamic lookup.

Something like

const char* as_string(FooBar v) {
    if constexpr (is_sparse_enum_v<FooBar>) {
        return as_string_sparse(v);
    } else {
        return as_string_dense(b);
    }
}

template <typename Enum>
const char* as_string_dense(Enum v)
    static const char* tab[] = { magic code to populate the table with reflexpr(Enum)... };
    return tab[static_cast<std::underlying_type_t<Enum>>(v)];
}

template <typename Enum>
const char* as_string_sparse(Enum v) {
    static std::unordered_map<std::underlying_type_t<Enum>, const char*> = { more magic code to populate the table with reflexpr(Enum)... };
    return tab[static_cast<std::underlying_type_t<Enum>>(v)];
}

this is complex enough to merit a library, and common enough to be standardized.

Jeffrey Yasskin

unread,
Aug 28, 2016, 2:04:14 PM8/28/16
to std-pr...@isocpp.org
Yeah, we got there later in the thread. 

Jeffrey

Bernd Lörwald

unread,
Aug 28, 2016, 2:19:24 PM8/28/16
to std-pr...@isocpp.org

> common enough to be standardized.

Honestly though, is it really that common? Raw enum value names are probably (hopefully) not what are presented to an end user. If it is used for error codes, you will have some table for actual descriptions. If it is flags, you probably have a proper parser that is able to handle combinations as well, and for printing you need that as well.

I would like to claim that only in very rare cases the raw name of an enumeration value is used, and if, they are for debugging purposes. The more common case with descriptions would not be covered. Languages like C# do that via annotations, and the full fledged runtime reflection. But then comes localization, …

I obviously understand where the desire to just print readable enum values comes from and have been there myself, but this feature would encourage mediocre UX or would not help that much for more than debugging.

Avi Kivity

unread,
Aug 28, 2016, 2:43:37 PM8/28/16
to std-pr...@isocpp.org

Even if it's just used for debugging/logging/tracing, it deserves a place. Those are requirements for nearly every program.

>
> --
> 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-proposal...@isocpp.org.
> To post to this group, send email to std-pr...@isocpp.org.

> To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/01F1ABC6-E4E6-4524-A29A-106FC4FE8470%40googlemail.com.

Nicol Bolas

unread,
Aug 28, 2016, 2:45:17 PM8/28/16
to ISO C++ Standard - Future Proposals

Well, here's the thing: if you give people compile-time reflection over enumerator names (and we will), then they will write this library anyway. However much it will "encourage mediocre UX", people are still going to do it. A lot.

So we can either give people a standardized system to do something that they clearly want to do, or we can make them do the work themselves. But since it's going to happen with our without a standardized library approach, I'd say that it's better to have it happen with one than without one.

At least that way, we can track down code that's using the "mediocre UX".

Moritz Klammler

unread,
Aug 29, 2016, 2:50:14 PM8/29/16
to std-pr...@isocpp.org
>> common enough to be standardized.
>
> Honestly though, is it really that common? Raw enum value names are
> probably (hopefully) not what are presented to an end user. If it is
> used for error codes, you will have some table for actual
> descriptions. If it is flags, you probably have a proper parser that
> is able to handle combinations as well, and for printing you need that
> as well.
>
> I would like to claim that only in very rare cases the raw name of an
> enumeration value is used, and if, they are for debugging
> purposes. The more common case with descriptions would not be
> covered. Languages like C# do that via annotations, and the full
> fledged runtime reflection. But then comes localization, …
>
> I obviously understand where the desire to just print readable enum
> values comes from and have been there myself, but this feature would
> encourage mediocre UX or would not help that much for more than
> debugging.

I don't know how C# does it but I'd like to mention that Java uses the
declared identifier verbatim, too. For example:

public class Demo {

enum Seasons { SPRING, SUMMER, AUTUMN, WINTER; }

public static void main(final String[] args) {
for (final String a : args) {
try {
final Seasons season = Seasons.valueOf(a);
System.out.println(season.toString());
} catch (final IllegalArgumentException e) {
System.err.printf("No valid enumerator: %s%n", a);
}
}
}
}

Here it is in action:

$ javac Demo.java && java -cp . Demo SPRING WINTER FALL
SPRING
WINTER
No valid enumerator: FALL

While I agree that even if the only useful application for this feature
were for debugging, it would still be worthy of standard library
support, I have to say that I've actually used the Java feature for
business logic in the past. Using the declared identifiers of
enumeration constants directly in graphical user-interfaces might not be
the best choice. But I think they are perfectly fine for use in, say,
configuration files or IPC messages.

Patrice Roy

unread,
Aug 29, 2016, 10:14:20 PM8/29/16
to std-pr...@isocpp.org
C# does something similar. The funny thing is that out of bounds enum values print too : https://dotnetfiddle.net/0qwDZk

I often do the enum-to-string tricks. I'd be interested if we could really describe what the expected behavior would be. Not sure C# does that right, but not sure what «right» would be in general...

--
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-pr...@isocpp.org.

Matus Chochlik

unread,
Aug 30, 2016, 1:23:04 AM8/30/16
to std-pr...@isocpp.org
I've added few examples showing simple implementation of
enumerator-to-string and string-to-enumerator using both the TMP and
constexpr MP libraries to the mirror reflection utilities:

https://github.com/matus-chochlik/mirror/blob/develop/example/mirror/005_enum_to_string.cpp
https://github.com/matus-chochlik/mirror/blob/develop/example/mirror/006_string_to_enum.cpp
https://github.com/matus-chochlik/mirror/blob/develop/example/puddle/005_enum_to_string.cpp
https://github.com/matus-chochlik/mirror/blob/develop/example/puddle/006_string_to_enum.cpp
>> email to std-proposal...@isocpp.org.
>> To post to this group, send email to std-pr...@isocpp.org.
>> To view this discussion on the web visit
>> https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/878tvfl89r.fsf%40gmail.com.
>
>
> --
> 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/-VbCUDBqJNM/unsubscribe.
> To unsubscribe from this group and all its topics, send an email to
> std-proposal...@isocpp.org.
> To post to this group, send email to std-pr...@isocpp.org.
> To view this discussion on the web visit
> https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAKiZDp3O2piEZy7Dr4XSPQqO%2BZd-rv06aiQVnaCJ%3DF8d0A83sw%40mail.gmail.com.



--
-- Matus Chochlik

Edward Catmur

unread,
Aug 30, 2016, 7:03:57 AM8/30/16
to ISO C++ Standard - Future Proposals
On Tuesday, 30 August 2016 03:14:20 UTC+1, Patrice Roy wrote:
C# does something similar. The funny thing is that out of bounds enum values print too : https://dotnetfiddle.net/0qwDZk

I often do the enum-to-string tricks. I'd be interested if we could really describe what the expected behavior would be. Not sure C# does that right, but not sure what «right» would be in general...

It depends on context, unfortunately. Sometimes you don't want to waste time on error cases, so a valid enum argument would be required; other times (e.g. logging) it's more important to be able to handle every possibility, and indeed to provide as much information as is available to help diagnose bugs. So there are several desirable behaviors:

char const* toStringUnchecked(Enum e) noexcept;       // #1 UB on invalid value
char const* toStringChecked(Enum e) noexcept(false);  // #2 throws std::invalid_argument
std
::ostream& operator<<(std::ostream&, Enum e);      // #3 writes underlying value if invalid

Plus non-throwing disappointment-handling variants of #2 (optional, expected or error_code).

Edward Catmur

unread,
Aug 30, 2016, 7:05:02 AM8/30/16
to ISO C++ Standard - Future Proposals, choc...@gmail.com
On Tuesday, 30 August 2016 06:23:04 UTC+1, Matus Chochlik wrote:
I've added few examples showing simple implementation of
enumerator-to-string and string-to-enumerator using both the TMP and
constexpr MP libraries to the mirror reflection utilities:

https://github.com/matus-chochlik/mirror/blob/develop/example/mirror/005_enum_to_string.cpp
https://github.com/matus-chochlik/mirror/blob/develop/example/mirror/006_string_to_enum.cpp
https://github.com/matus-chochlik/mirror/blob/develop/example/puddle/005_enum_to_string.cpp
https://github.com/matus-chochlik/mirror/blob/develop/example/puddle/006_string_to_enum.cpp 
 
Is it really necessary to construct the lookup table as a library container? Wouldn't a switch statement perform better?

Patrice Roy

unread,
Aug 30, 2016, 8:59:14 PM8/30/16
to std-pr...@isocpp.org
An important issue I was trying to bring up (but failed :) ) is the handling of values from the underlying type that don't match effective enum values. C# does something, it might not be what we want, and what one of us wants does not necessarily match what another one of us wants.

I think Edward saw this : the difficulty in making standard enum-to-string behavior is (a) what to do with values that don't map to an enum constant, and (b) choosing what to do with scope names (class, namespace, ...) with the relevant string values. These issues don't seem easy to settle in a standard fashion, at least in my eyes, as there are valid arguments for various behaviors.

Now, if someone could write a proposal that both offers language support for some behavior, and backs it up with library functions that let users control the conversion behavior efficiently, then... :)

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-pr...@isocpp.org.

Vicente J. Botet Escriba

unread,
Aug 31, 2016, 4:20:17 PM8/31/16
to std-pr...@isocpp.org, choc...@gmail.com
Le 30/08/2016 à 13:05, Edward Catmur a écrit :
On Tuesday, 30 August 2016 06:23:04 UTC+1, Matus Chochlik wrote:
I've added few examples showing simple implementation of
enumerator-to-string and string-to-enumerator using both the TMP and
constexpr MP libraries to the mirror reflection utilities:

https://github.com/matus-chochlik/mirror/blob/develop/example/mirror/005_enum_to_string.cpp
https://github.com/matus-chochlik/mirror/blob/develop/example/mirror/006_string_to_enum.cpp
https://github.com/matus-chochlik/mirror/blob/develop/example/puddle/005_enum_to_string.cpp
https://github.com/matus-chochlik/mirror/blob/develop/example/puddle/006_string_to_enum.cpp 
 
Is it really necessary to construct the lookup table as a library container? Wouldn't a switch statement perform better?

No, we don't need a map, but I believe that Matus wanted to show a probe of concept.

I don't know how we can generate a switch with the current language. We can do some recursive if and expect that the compiler optimize it.

Vicente

Vicente J. Botet Escriba

unread,
Aug 31, 2016, 4:34:51 PM8/31/16
to std-pr...@isocpp.org
Le 31/08/2016 à 02:59, Patrice Roy a écrit :
> An important issue I was trying to bring up (but failed :) ) is the
> handling of values from the underlying type that don't match effective
> enum values. C# does something, it might not be what we want, and what
> one of us wants does not necessarily match what another one of us wants.
>
> I think Edward saw this : the difficulty in making standard
> enum-to-string behavior is (a) what to do with values that don't map
> to an enum constant, and (b) choosing what to do with scope names
> (class, namespace, ...) with the relevant string values. These issues
> don't seem easy to settle in a standard fashion, at least in my eyes,
> as there are valid arguments for various behaviors.
You are right that there are some variation points.

(a) I believe the C# option is a good one. If the user wants to check
for validity of an enum constant another function can be provided and
the user could combine them. This is a subset of enums.

(b) I believe that having the string for the enumerators is the minimal
feature and it is already beter than the number. We can as well have
another function that allows to obtain the string associated to the scope.

>
> Now, if someone could write a proposal that both offers language
> support for some behavior, and backs it up with library functions that
> let users control the conversion behavior efficiently, then... :)

I hope that someone would propose something once we have reflection
delivered by a major compiler ;-)

Vicente

Edward Catmur

unread,
Aug 31, 2016, 5:11:16 PM8/31/16
to std-pr...@isocpp.org
Fair enough; recursive templates are even worse, so a container is the simplest option for now. You can generate a switch with the preprocessor, but then you have to set an (arbitrary) upper limit on the number of cases. We really need variadic expansion of case labels, but I guess it's a fairly niche interest.

Larry Evans

unread,
Aug 31, 2016, 10:26:21 PM8/31/16
to std-pr...@isocpp.org
On 08/31/2016 04:11 PM, 'Edward Catmur' via ISO C++ Standard - Future
Proposals wrote:
>
>
> On Wed, Aug 31, 2016 at 9:20 PM, Vicente J. Botet Escriba
> <vicent...@wanadoo.fr <mailto:vicent...@wanadoo.fr>> wrote:
[snip]
> No, we don't need a map, but I believe that Matus wanted to show a
> probe of concept.
>
> I don't know how we can generate a switch with the current language.
> We can do some recursive if and expect that the compiler optimize it.
>
>
> Fair enough; recursive templates are even worse, so a container is the
> simplest option for now. You can generate a switch with the
> preprocessor, but then you have to set an (arbitrary) upper limit on the
> number of cases. We really need variadic expansion of case labels, but I
> guess it's a fairly niche interest.
>
Would the proposal here:

https://groups.google.com/a/isocpp.org/forum/#!searchin/std-proposals/statement$20expansion/std-proposals/LoDJDCDDyH8/omuPHEjpJJkJ

help?



sebastian...@gmail.com

unread,
Sep 26, 2018, 6:41:28 AM9/26/18
to ISO C++ Standard - Future Proposals
While the reflection proposal is very nice and generic, has anyone so far brought up the idea of building something like this on top of the "= default" style syntax, e.g. "constexpr std::string to_string(MyEnum) = default;" or a similar style for operator<<, or perhaps even building it upon using attributes "[[printable]]", or even automatically and silently generating enum-to-string functions (as constexpr to_string(MyEnum)" implicitly if ever used in the code, similar to existing default c-tor generation (or always for multiple compile units, with elimination by linker stage optimization if unused)? Out of these options, I prefer the last one - automatic generation if used.

I know that the C++ standards committee policy is usually to prefer generic solutions over specific solutions, but couldn't building "enum to  string" on top of reflection be considered an exaggeration of that rule? It seems to me that the generic approaches listed above may have to build on recursive templates or otherwise potentially slow-to-compile operations, so certainly one argument in favor of more specific language support would be potentially faster compile times for generating what is really a very common pattern in most large C++ code bases.

Moreover, I'm not usre there's any good reason to force the user to implement enum-to-string functionality manually. It's certainly less convenient for the user to do it manually (which the reflection based approach would require), and it's also certainly possible for a compiler to determine whether a call to enum-to-string is ever made in the code base and in that way know whether such a method would need to be generated. If for some reason certain users would desire compiler warnings for unnecessarily generated to_string methods or a way to more easily detect whether a method is implicitly generated, perhaps automatic generation could be expressed by an attribute: [[printable]].

In order to be more generic than only applying to enums, this idea could be supported also for structs/classes, where the default option could be to print all members in a similar to how they are printed in GDB, i.e. delimited by suitable delimiters and with some form of hierarchy in cases where a member is of struct type.


Den torsdag 25 augusti 2016 kl. 15:47:34 UTC+2 skrev Kamikaze Dominic Fandrey:
I searched this list for similar suggestions, but of course I may
have overlooked something.

I frequently use the following pattern:

    enum class FooBar { KEKS, DOSE };
    char const * const FooBarStr[]{"KEKS", "DOSE"};

I use the strings to in error messages, verbose output, etc.

    FooBarStr[static_cast<int>(FooBar::KEKS)] // "KEKS"

This has some disadvantages, e.g. enums with explicitly stated values
may have gaps, negative values and may be defined out of order.
Also, changing the enum also means changing the strings manually.

What I'd like to see:

    static_cast<char const *>(FooBar::KEKS) // "KEKS"

Reply all
Reply to author
Forward
0 new messages