You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The main advantage for my use case: Let's say you use (GREEN, "GREEN") and the string is persistently stored in configuration files / XML / JSON etc. Later you rename the GREEN enum value in a refactor. This way it remains compatible with the old files because the string "GREEN" is independent. If you want, you can keep the old string, or decide to write custom code for backwards compatibility. But the compatibility doesn't break without notice.
The thing is that probably the single biggest thing that this library achieves is avoiding repeating all the enumerator names. The enumerator name itself doesn't matter much, so usually you just pick something based on how you want it to display (for this reason I'm also a bit confused at the necessity to rename an enum value in a refactor). Once you're writing out all the enumerator strings by hand, couldn't you just write a free function(s) that wrap the to/from string methods of wise enum, and handle any special cases yourself?
There's also a technical reason why this is difficult. As you may know, macro tokenization is only aware of commas and parens. That means that if you have something like (GREEN, foo<3,4>()), the preprocessor actually sees 3 tokens. This means that you can have valid C++ expressions that get turned into multiple preprocessor arguments (which are individually each nonsense). Currently, this is ok because my "tuple" has two arguments, one of which cannot contain commas or any nonsense. So I know that the first entry is the identifier, and I stitch together the rest to make the integer. However, given something like (GREEN, get_identifier<Foo, 0>(), get_value<Foo, 3>()), it's just completely impossible to understand how to process this.
So to support this feature I would start to have all these caveats. Currently, I'm very happy with the fact that I avoid any of these caveats (which people tend to hate about macros); you can use (AFAIK) any valid C++ expression for the enumerator value and it should work just fine.
The closest I could imagine easily at this point is a CUSTOM_ADAPT macro. In that situation, you would declare your enum "unwisely", and then adapt it with custom strings. However, you would still have to repeat all the enumerators themselves, reducing the value. I'm not yet convinced that this is worth it, but I'm happy to hear from you. Or perhaps if you're familiar with the preprocessor, you have some alternate approaches.
Thanks for the extensive reply, that was very insightful 👍 I understand your point, and that what you have now is probably the "sweet spot" of macros.
I'll use a free function layer as you suggested, it's not a big deal. And it's nice that in most cases the code can fall through to the auto-generated strings, without having to type them twice.
Hi, I just wanted to chime in on this, as this is a use-case I am also looking at.
Once you're writing out all the enumerator strings by hand, couldn't you just write a free function(s) that wrap the to/from string methods of wise enum, and handle any special cases yourself?
The reason is that we really want to have one canonical way of defining these enums. In our case we use them for values that has some hard-coded meaning towards databases. And the database strings sometimes contains characters or combinations thereof, that are not valid in c++ enums. In those cases, we want to specify the enum in c++, and simply add the overriding string representation. If we were to write these by hand, this is a PITA because we then would have to emulate the ergonomics of whatever enum library we use to avoid unpleasant surprises with difference in behaviour (e.g for wise enum, using the custom optional when compiling for c++11, std::optional for c++17, and ALL other behaviour).
We could do what you suggest (which is almost what we are doing now), and then use the enum-library internally in the mapping functions instead, so that those are easy to create in the cases where the strings are legal enum names in c++. We do this currently with better-enums, and then use better-enums make_map functionality for the custom strings.
But all this results in a lot of boilerplate and unpleasantries compared to just being able to use the enum-library without the wrapping on top, if it had some way to customize the enum to string mapping(and back) in the few cases where its needed.
If wise-enum provides this, we would probably switch from better-enums, because the overall ergonomics (with the enums actually being enums) seems better too.
@andoks Ok, you raise good points. What would you say to declaring the enum plainly, and then having the adapt macro take optional strings for each enum? You'd have to repeat the enumerators, which is a downside (and keep the enum and adapter in sync). The problem with doing a single macro, as I mentioned before, is the fact that you have three possible arguments for each enumerator: the c++ name, the value, and the string. The latter two are both ideally optional, which I can't really deal with, and worse they're not guaranteed to be single macro tokens. So it's just very difficult to imagine what a single macro would look like. I could have a convenience macro that would allow defining enums with custom strings but not controlling the values, but inevitably someone will ask for that too. And of course, those convenience macros would all have to be multiplied by 4 (for enum/enum class, inside/outside a struct). Another reason why I like the solution of forcing a vanilla enum declaration, and then allowing custom strings in the adapter.
@quicknir : for my use-case, the one related to databases and the strings only sometimes not matching what is legal c++ enum names, I have absolutely no issue with declaring it as an enum + some kind of adaptor.
What I think is the most important is not the (relatively) few places we have to define the custom-stringed enums, but rather that the use (to-string, from-string) of the custom-string enums and the not-custom-string enums are identical with respect to the enum-library (wise-enum in this case) API.
If there are differences in the usage API, I foresee huge pains with devs having to learn through painful bugs, that they always have to check the implementation-file for the enum they want to use every time, and check if it is custom-string or not (or automate it by using project-specific functions, but this carries somewhat the same problems, as devs might end up using the enum-API for tostring/fromstring regardless).
Thanks for this nice software!
The feature described here would be very useful for our case too, because the resulted string doesn't fit C++ constraints
Activity
quicknir commentedon Feb 26, 2019
Np :-).
The thing is that probably the single biggest thing that this library achieves is avoiding repeating all the enumerator names. The enumerator name itself doesn't matter much, so usually you just pick something based on how you want it to display (for this reason I'm also a bit confused at the necessity to rename an enum value in a refactor). Once you're writing out all the enumerator strings by hand, couldn't you just write a free function(s) that wrap the to/from string methods of wise enum, and handle any special cases yourself?
There's also a technical reason why this is difficult. As you may know, macro tokenization is only aware of commas and parens. That means that if you have something like
(GREEN, foo<3,4>()), the preprocessor actually sees 3 tokens. This means that you can have valid C++ expressions that get turned into multiple preprocessor arguments (which are individually each nonsense). Currently, this is ok because my "tuple" has two arguments, one of which cannot contain commas or any nonsense. So I know that the first entry is the identifier, and I stitch together the rest to make the integer. However, given something like(GREEN, get_identifier<Foo, 0>(), get_value<Foo, 3>()), it's just completely impossible to understand how to process this.So to support this feature I would start to have all these caveats. Currently, I'm very happy with the fact that I avoid any of these caveats (which people tend to hate about macros); you can use (AFAIK) any valid C++ expression for the enumerator value and it should work just fine.
The closest I could imagine easily at this point is a
CUSTOM_ADAPTmacro. In that situation, you would declare your enum "unwisely", and then adapt it with custom strings. However, you would still have to repeat all the enumerators themselves, reducing the value. I'm not yet convinced that this is worth it, but I'm happy to hear from you. Or perhaps if you're familiar with the preprocessor, you have some alternate approaches.martinfinke commentedon Feb 26, 2019
Thanks for the extensive reply, that was very insightful 👍 I understand your point, and that what you have now is probably the "sweet spot" of macros.
I'll use a free function layer as you suggested, it's not a big deal. And it's nice that in most cases the code can fall through to the auto-generated strings, without having to type them twice.
andoks commentedon Mar 8, 2019
Hi, I just wanted to chime in on this, as this is a use-case I am also looking at.
The reason is that we really want to have one canonical way of defining these enums. In our case we use them for values that has some hard-coded meaning towards databases. And the database strings sometimes contains characters or combinations thereof, that are not valid in c++ enums. In those cases, we want to specify the enum in c++, and simply add the overriding string representation. If we were to write these by hand, this is a PITA because we then would have to emulate the ergonomics of whatever enum library we use to avoid unpleasant surprises with difference in behaviour (e.g for wise enum, using the custom optional when compiling for c++11, std::optional for c++17, and ALL other behaviour).
We could do what you suggest (which is almost what we are doing now), and then use the enum-library internally in the mapping functions instead, so that those are easy to create in the cases where the strings are legal enum names in c++. We do this currently with better-enums, and then use better-enums
make_mapfunctionality for the custom strings.But all this results in a lot of boilerplate and unpleasantries compared to just being able to use the enum-library without the wrapping on top, if it had some way to customize the enum to string mapping(and back) in the few cases where its needed.
If wise-enum provides this, we would probably switch from better-enums, because the overall ergonomics (with the enums actually being enums) seems better too.
quicknir commentedon Mar 11, 2019
@andoks Ok, you raise good points. What would you say to declaring the enum plainly, and then having the adapt macro take optional strings for each enum? You'd have to repeat the enumerators, which is a downside (and keep the enum and adapter in sync). The problem with doing a single macro, as I mentioned before, is the fact that you have three possible arguments for each enumerator: the c++ name, the value, and the string. The latter two are both ideally optional, which I can't really deal with, and worse they're not guaranteed to be single macro tokens. So it's just very difficult to imagine what a single macro would look like. I could have a convenience macro that would allow defining enums with custom strings but not controlling the values, but inevitably someone will ask for that too. And of course, those convenience macros would all have to be multiplied by 4 (for enum/enum class, inside/outside a struct). Another reason why I like the solution of forcing a vanilla enum declaration, and then allowing custom strings in the adapter.
Thoughts?
andoks commentedon Mar 12, 2019
@quicknir : for my use-case, the one related to databases and the strings only sometimes not matching what is legal c++ enum names, I have absolutely no issue with declaring it as an enum + some kind of adaptor.
What I think is the most important is not the (relatively) few places we have to define the custom-stringed enums, but rather that the use (to-string, from-string) of the custom-string enums and the not-custom-string enums are identical with respect to the enum-library (wise-enum in this case) API.
If there are differences in the usage API, I foresee huge pains with devs having to learn through painful bugs, that they always have to check the implementation-file for the enum they want to use every time, and check if it is custom-string or not (or automate it by using project-specific functions, but this carries somewhat the same problems, as devs might end up using the enum-API for tostring/fromstring regardless).
whyman commentedon Mar 25, 2022
Just a quick note that this would be great for us too, similar use case: Database column names, whilst keeping a consistent code style.
apmanol commentedon Feb 7, 2023
Thanks for this nice software!
The feature described here would be very useful for our case too, because the resulted string doesn't fit
C++constraints