Query for interest in library: reflective enum
Hello, I recently needed a reflective enum for work (that is an enum where you can programmatically convert to and from strings, know how many enumerators there are, and iterate over all enumerators). I looked online but I didn't find anything that met my requirements. I was surprised that there wasn't one in Boost. So I'd like to see if there is interest; I wrote an implementation that I think has several nice features. The most polished existing smart enum implementation I could find was http://aantron.github.io/better-enums/. However, the enum implemented there is not a true enum; it uses a nested member of a class kind of approach. So for example, the is_enum type trait will not recognize is as an enum. You cannot use it as a non-type template parameter. I strongly think that a good generic smart enum macro should generate the exact "vanilla" enum (or enum class) declaration. My library's syntax looks like this: WISE_ENUM(Foo, BAR, (BAZ, 3)) This generates, among other things, exactly: enum Foo { BAR, BAZ=3}; I intend this as an explicit guarantee of the library that the programmer can count on. That way when Foo is used for things unrelating to reflection, the developer can be confident that there will not be any surprises. Beyond compatability with C++ facilities for enums, and minimizing surprise, another advantage of this approach is that it's guaranteed to be backwards compatible. An existing codebase with an existing enum/enum class can switch to this smart enum with very high confidence that existing code will continue to work as before. The WISE_ENUM macro, beyond generating the vanilla enum, also generates a switch-case function for to-string conversion (for maximal efficiency), and finally it generates a constexpr function that returns a std::array<std::pair<const char*, Foo>> listing all of the arrays and their string descriptions. This constexpr function, which is found via ADL, allows writing a sequence of (constexpr) variables and functions providing the rest of the desired functionality. Usage looks like this: std::cerr << "My enum's size is : " << wise_enum::size<Foo> // this is constexpr template variable auto x = wise_enum::from_string<Foo>("BAR").value(); // from_string returns an optional auto y = wise_enum::to_string(x); // y is string literal, no heap allocation My implementation (POC level) is here: https://github.com/quicknir/wise_enum. I don't yet have separate macros to cover enum/enum class/explicit/implicit backing type combinations. It's only about 100 lines of code, leaning heavily on BOOST_PP to do the heavy lifting. It requires variadic macros, and can probably target C++11 (if it doesn't already). I probably need to "comma harden" the macros for bizarre stuff like WISE_ENUM(Foo, (BAR, my_func<3,4>())). It has the limitation that the macro cannot be used inside a class (this is hard to workaround for reflective macros). It's also unclear how repeated enumeration values would be handled (enum Foo { BAR = 1, BAZ = 1}). As it stands this will cause a compilation failure in the generated switch case of the to_string function. Can discuss more in subsequent emails. One suggested feature is an is_contiguous trait. Another reasonable bit of functionality might be an is_wise_enum trait. Version 2 of this library could target enum sets. Although it's not fancy, I think this nicely covers a piece of functionality I often need, and it does so avoiding any downside compared to using a simple enum (beyond having to use macros). Hope other people will agree! Cheers, Nir
Hah, already realized I made a silly mistake. The current implementation uses variable templates and therefore isn't 11 compatible of course. The template variables can be optionally compiled for C++14, and C++11 can use static members of template structs. On Wed, Jan 17, 2018 at 3:21 PM, Nir Friedman <quicknir@gmail.com> wrote:
Hello,
I recently needed a reflective enum for work (that is an enum where you can programmatically convert to and from strings, know how many enumerators there are, and iterate over all enumerators). I looked online but I didn't find anything that met my requirements. I was surprised that there wasn't one in Boost. So I'd like to see if there is interest; I wrote an implementation that I think has several nice features.
The most polished existing smart enum implementation I could find was http://aantron.github.io/better-enums/. However, the enum implemented there is not a true enum; it uses a nested member of a class kind of approach. So for example, the is_enum type trait will not recognize is as an enum. You cannot use it as a non-type template parameter.
I strongly think that a good generic smart enum macro should generate the exact "vanilla" enum (or enum class) declaration. My library's syntax looks like this:
WISE_ENUM(Foo, BAR, (BAZ, 3))
This generates, among other things, exactly:
enum Foo { BAR, BAZ=3};
I intend this as an explicit guarantee of the library that the programmer can count on. That way when Foo is used for things unrelating to reflection, the developer can be confident that there will not be any surprises. Beyond compatability with C++ facilities for enums, and minimizing surprise, another advantage of this approach is that it's guaranteed to be backwards compatible. An existing codebase with an existing enum/enum class can switch to this smart enum with very high confidence that existing code will continue to work as before.
The WISE_ENUM macro, beyond generating the vanilla enum, also generates a switch-case function for to-string conversion (for maximal efficiency), and finally it generates a constexpr function that returns a std::array<std::pair<const char*, Foo>> listing all of the arrays and their string descriptions. This constexpr function, which is found via ADL, allows writing a sequence of (constexpr) variables and functions providing the rest of the desired functionality. Usage looks like this:
std::cerr << "My enum's size is : " << wise_enum::size<Foo> // this is constexpr template variable auto x = wise_enum::from_string<Foo>("BAR").value(); // from_string returns an optional auto y = wise_enum::to_string(x); // y is string literal, no heap allocation
My implementation (POC level) is here: https://github.com/ quicknir/wise_enum. I don't yet have separate macros to cover enum/enum class/explicit/implicit backing type combinations. It's only about 100 lines of code, leaning heavily on BOOST_PP to do the heavy lifting. It requires variadic macros, and can probably target C++11 (if it doesn't already). I probably need to "comma harden" the macros for bizarre stuff like WISE_ENUM(Foo, (BAR, my_func<3,4>())).
It has the limitation that the macro cannot be used inside a class (this is hard to workaround for reflective macros). It's also unclear how repeated enumeration values would be handled (enum Foo { BAR = 1, BAZ = 1}). As it stands this will cause a compilation failure in the generated switch case of the to_string function. Can discuss more in subsequent emails.
One suggested feature is an is_contiguous trait. Another reasonable bit of functionality might be an is_wise_enum trait. Version 2 of this library could target enum sets.
Although it's not fancy, I think this nicely covers a piece of functionality I often need, and it does so avoiding any downside compared to using a simple enum (beyond having to use macros). Hope other people will agree!
Cheers,
Nir
Le 17/01/2018 à 21:21, Nir Friedman via Boost a écrit :
Hello,
I recently needed a reflective enum for work (that is an enum where you can programmatically convert to and from strings, know how many enumerators there are, and iterate over all enumerators). I looked online but I didn't find anything that met my requirements. I was surprised that there wasn't one in Boost. So I'd like to see if there is interest; I wrote an implementation that I think has several nice features. Hi,
maybe you could be interested in locking at C++03 version https://htmlpreview.github.io/?https://github.com/viboes/enums/blob/master/l... I don't pretend anymore to propose it to Boost now, but I'm working on proposing to the standard the ordinal part and his containers. https://github.com/viboes/std-make/tree/master/include/experimental/fundamen... Maybe you can find there something inspiring you. Best, Vicente
The most polished existing smart enum implementation I could find was http://aantron.github.io/better-enums/. However, the enum implemented there is not a true enum; it uses a nested member of a class kind of approach. So for example, the is_enum type trait will not recognize is as an enum. You cannot use it as a non-type template parameter.
I strongly think that a good generic smart enum macro should generate the exact "vanilla" enum (or enum class) declaration. My library's syntax looks like this:
WISE_ENUM(Foo, BAR, (BAZ, 3))
This generates, among other things, exactly:
enum Foo { BAR, BAZ=3};
I intend this as an explicit guarantee of the library that the programmer can count on. That way when Foo is used for things unrelating to reflection, the developer can be confident that there will not be any surprises. Beyond compatability with C++ facilities for enums, and minimizing surprise, another advantage of this approach is that it's guaranteed to be backwards compatible. An existing codebase with an existing enum/enum class can switch to this smart enum with very high confidence that existing code will continue to work as before.
The WISE_ENUM macro, beyond generating the vanilla enum, also generates a switch-case function for to-string conversion (for maximal efficiency), and finally it generates a constexpr function that returns a std::array<std::pair<const char*, Foo>> listing all of the arrays and their string descriptions. This constexpr function, which is found via ADL, allows writing a sequence of (constexpr) variables and functions providing the rest of the desired functionality. Usage looks like this:
std::cerr << "My enum's size is : " << wise_enum::size<Foo> // this is constexpr template variable auto x = wise_enum::from_string<Foo>("BAR").value(); // from_string returns an optional auto y = wise_enum::to_string(x); // y is string literal, no heap allocation
My implementation (POC level) is here: https://github.com/quicknir/wise_enum. I don't yet have separate macros to cover enum/enum class/explicit/implicit backing type combinations. It's only about 100 lines of code, leaning heavily on BOOST_PP to do the heavy lifting. It requires variadic macros, and can probably target C++11 (if it doesn't already). I probably need to "comma harden" the macros for bizarre stuff like WISE_ENUM(Foo, (BAR, my_func<3,4>())).
It has the limitation that the macro cannot be used inside a class (this is hard to workaround for reflective macros). It's also unclear how repeated enumeration values would be handled (enum Foo { BAR = 1, BAZ = 1}). As it stands this will cause a compilation failure in the generated switch case of the to_string function. Can discuss more in subsequent emails.
One suggested feature is an is_contiguous trait. Another reasonable bit of functionality might be an is_wise_enum trait. Version 2 of this library could target enum sets.
Although it's not fancy, I think this nicely covers a piece of functionality I often need, and it does so avoiding any downside compared to using a simple enum (beyond having to use macros). Hope other people will agree!
Cheers,
Nir
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
It seems to me (if I understand correctly) that the additional features from ordinal could be added to wise enum with just a few lines of code because wise enum generates a constexpr array of enumerators. The index into that array is the ordinality of the enum. So perhaps it could be integrated into wise enum? Thoughts? On Jan 18, 2018 1:52 AM, "Vicente J. Botet Escriba" < vicente.botet@wanadoo.fr> wrote:
Le 17/01/2018 à 21:21, Nir Friedman via Boost a écrit :
Hello,
I recently needed a reflective enum for work (that is an enum where you can programmatically convert to and from strings, know how many enumerators there are, and iterate over all enumerators). I looked online but I didn't find anything that met my requirements. I was surprised that there wasn't one in Boost. So I'd like to see if there is interest; I wrote an implementation that I think has several nice features.
Hi,
maybe you could be interested in locking at C++03 version
https://htmlpreview.github.io/?https://github.com/viboes/enu ms/blob/master/libs/enums/doc/html/index.html
I don't pretend anymore to propose it to Boost now, but I'm working on proposing to the standard the ordinal part and his containers.
https://github.com/viboes/std-make/tree/master/include/exper imental/fundamental/v3/ordinal
Maybe you can find there something inspiring you.
Best, Vicente
The most polished existing smart enum implementation I could find was
http://aantron.github.io/better-enums/. However, the enum implemented there is not a true enum; it uses a nested member of a class kind of approach. So for example, the is_enum type trait will not recognize is as an enum. You cannot use it as a non-type template parameter.
I strongly think that a good generic smart enum macro should generate the exact "vanilla" enum (or enum class) declaration. My library's syntax looks like this:
WISE_ENUM(Foo, BAR, (BAZ, 3))
This generates, among other things, exactly:
enum Foo { BAR, BAZ=3};
I intend this as an explicit guarantee of the library that the programmer can count on. That way when Foo is used for things unrelating to reflection, the developer can be confident that there will not be any surprises. Beyond compatability with C++ facilities for enums, and minimizing surprise, another advantage of this approach is that it's guaranteed to be backwards compatible. An existing codebase with an existing enum/enum class can switch to this smart enum with very high confidence that existing code will continue to work as before.
The WISE_ENUM macro, beyond generating the vanilla enum, also generates a switch-case function for to-string conversion (for maximal efficiency), and finally it generates a constexpr function that returns a std::array<std::pair<const char*, Foo>> listing all of the arrays and their string descriptions. This constexpr function, which is found via ADL, allows writing a sequence of (constexpr) variables and functions providing the rest of the desired functionality. Usage looks like this:
std::cerr << "My enum's size is : " << wise_enum::size<Foo> // this is constexpr template variable auto x = wise_enum::from_string<Foo>("BAR").value(); // from_string returns an optional auto y = wise_enum::to_string(x); // y is string literal, no heap allocation
My implementation (POC level) is here: https://github.com/quicknir/wi se_enum. I don't yet have separate macros to cover enum/enum class/explicit/implicit backing type combinations. It's only about 100 lines of code, leaning heavily on BOOST_PP to do the heavy lifting. It requires variadic macros, and can probably target C++11 (if it doesn't already). I probably need to "comma harden" the macros for bizarre stuff like WISE_ENUM(Foo, (BAR, my_func<3,4>())).
It has the limitation that the macro cannot be used inside a class (this is hard to workaround for reflective macros). It's also unclear how repeated enumeration values would be handled (enum Foo { BAR = 1, BAZ = 1}). As it stands this will cause a compilation failure in the generated switch case of the to_string function. Can discuss more in subsequent emails.
One suggested feature is an is_contiguous trait. Another reasonable bit of functionality might be an is_wise_enum trait. Version 2 of this library could target enum sets.
Although it's not fancy, I think this nicely covers a piece of functionality I often need, and it does so avoiding any downside compared to using a simple enum (beyond having to use macros). Hope other people will agree!
Cheers,
Nir
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman /listinfo.cgi/boost
On Wed, Jan 17, 2018 at 9:21 PM, Nir Friedman via Boost < boost@lists.boost.org> wrote:
I recently needed a reflective enum for work (that is an enum where you can programmatically convert to and from strings, know how many enumerators there are, and iterate over all enumerators). I looked online but I didn't find anything that met my requirements. I was surprised that there wasn't one in Boost. So I'd like to see if there is interest; I wrote an implementation that I think has several nice features.
I'm interested! I also have something similar, inspired from http://goo.gl/WBLL6, but less sophisticated (no constexpr nor explicit enum integral, nor like yours enum class support).
...
My implementation (POC level) is here: https://github.com/quicknir/wise_enum
. I don't yet have separate macros to cover enum/enum class/explicit/implicit backing type combinations. It's only about 100 lines of code, leaning heavily on BOOST_PP to do the heavy lifting.
ditto
It requires variadic macros, and can probably target C++11 (if it doesn't already).
Mine is fine with C++03. What needs C++11, beside optionally making it constexpr? For backward-compatibility with 3rd party SDKs, I need support for ancient compilers.
I probably need to "comma harden" the macros for bizarre stuff like WISE_ENUM(Foo, (BAR, my_func<3,4>())).
Not of problem with mine, since does not support explicit integral values :). I've several times missed that though, this I'd welcome replacing mine with yours. It has the limitation that the macro cannot be used inside a class (this is
hard to workaround for reflective macros). It's also unclear how repeated enumeration values would be handled (enum Foo { BAR = 1, BAZ = 1}). As it stands this will cause a compilation failure in the generated switch case of the to_string function. Can discuss more in subsequent emails.
I also needed "nested" declarations, so this limitation is too restrictive IMHO. Mine uses two macros, the one to declare the enum, nested or not, and another one outside any namespace to declare the trait specializations that the template free functions equivalent to your _wise::to_string _wise::from_string use. One suggested feature is an is_contiguous trait. Another reasonable bit of
functionality might be an is_wise_enum trait. Version 2 of this library could target enum sets.
FWIW, here's my API, which shows the features I already depend on. Notably instead of using an optional to convert to the enum from text, the declaration can specify an "invalid" / "unknown" enum value to map to, which when not specified throws instead of returning the enum value. template <typename TEnum> std::string enum_name_of() { BOOST_STATIC_ASSERT((boost::is_enum<TEnum>::value)); return EnumTypeTraits<TEnum>::name(); } template <typename TEnum> std::vector<TEnum> enum_values_of() { BOOST_STATIC_ASSERT((boost::is_enum<TEnum>::value)); return EnumTypeTraits<TEnum>::values(); } template <typename TEnum> std::string enum_string_of(TEnum value) { BOOST_STATIC_ASSERT((boost::is_enum<TEnum>::value)); return EnumTypeTraits<TEnum>::toString(value); } template <typename TEnum> TEnum enum_value_of(const std::string& value_string) { BOOST_STATIC_ASSERT((boost::is_enum<TEnum>::value)); return EnumTypeTraits<TEnum>::fromString(value_string); } /*! * \throw std::runtime_error if \a value_string is invalid and TEnum has no * \em invalid enum value. * * \note \b Silently returns the \em invalid enum on invalid \a value_string, * if TEnum has one. Throws otherwise. */ template <typename TEnum> void set_enum_value_from(const std::string& value_string, TEnum& value) { BOOST_STATIC_ASSERT((boost::is_enum<TEnum>::value)); value = enum_value_of<TEnum>(value_string); } template <typename TEnum> bool enum_has_invalid_value() { BOOST_STATIC_ASSERT((boost::is_enum<TEnum>::value)); return EnumTypeTraits<TEnum>::hasInvalid(); } template <typename TEnum> TEnum enum_invalid_value() { BOOST_STATIC_ASSERT((boost::is_enum<TEnum>::value)); return EnumTypeTraits<TEnum>::invalid(); } template <typename TEnum> bool enum_is_valid_value(TEnum value) { BOOST_STATIC_ASSERT((boost::is_enum<TEnum>::value)); return EnumTypeTraits<TEnum>::isValidCode(value); }
Although it's not fancy, I think this nicely covers a piece of functionality I often need, and it does so avoiding any downside compared to using a simple enum (beyond having to use macros). Hope other people will agree!
I definitely do! --DD
Any progress on this? On 01/19/2018 12:27 AM, Dominique Devienne via Boost wrote: On Wed, Jan 17, 2018 at 9:21 PM, Nir Friedman via Boost < [1]boost@lists.boost.org> wrote: I recently needed a reflective enum for work (that is an enum where you can programmatically convert to and from strings, know how many enumerators there are, and iterate over all enumerators). I looked online but I didn't find anything that met my requirements. I was surprised that there wasn't one in Boost. So I'd like to see if there is interest; I wrote an implementation that I think has several nice features. I'm interested! I also have something similar, inspired from [2]http://goo.gl/WBLL6, but less sophisticated (no constexpr nor explicit enum integral, nor like yours enum class support). ... My implementation (POC level) is here: [3]https://github.com/quicknir/wise_enum . I don't yet have separate macros to cover enum/enum class/explicit/implicit backing type combinations. It's only about 100 lines of code, leaning heavily on BOOST_PP to do the heavy lifting. ditto It requires variadic macros, and can probably target C++11 (if it doesn't already). Mine is fine with C++03. What needs C++11, beside optionally making it constexpr? For backward-compatibility with 3rd party SDKs, I need support for ancient compilers. I probably need to "comma harden" the macros for bizarre stuff like WISE_ENUM(Foo, (BAR, my_func<3,4>())). Not of problem with mine, since does not support explicit integral values :). I've several times missed that though, this I'd welcome replacing mine with yours. It has the limitation that the macro cannot be used inside a class (this is hard to workaround for reflective macros). It's also unclear how repeated enumeration values would be handled (enum Foo { BAR = 1, BAZ = 1}). As it stands this will cause a compilation failure in the generated switch case of the to_string function. Can discuss more in subsequent emails. I also needed "nested" declarations, so this limitation is too restrictive IMHO. Mine uses two macros, the one to declare the enum, nested or not, and another one outside any namespace to declare the trait specializations that the template free functions equivalent to your _wise::to_string _wise::from_string use. One suggested feature is an is_contiguous trait. Another reasonable bit of functionality might be an is_wise_enum trait. Version 2 of this library could target enum sets. FWIW, here's my API, which shows the features I already depend on. Notably instead of using an optional to convert to the enum from text, the declaration can specify an "invalid" / "unknown" enum value to map to, which when not specified throws instead of returning the enum value. template <typename TEnum> std::string enum_name_of() { BOOST_STATIC_ASSERT((boost::is_enum<TEnum>::value)); return EnumTypeTraits<TEnum>::name(); } template <typename TEnum> std::vector<TEnum> enum_values_of() { BOOST_STATIC_ASSERT((boost::is_enum<TEnum>::value)); return EnumTypeTraits<TEnum>::values(); } template <typename TEnum> std::string enum_string_of(TEnum value) { BOOST_STATIC_ASSERT((boost::is_enum<TEnum>::value)); return EnumTypeTraits<TEnum>::toString(value); } template <typename TEnum> TEnum enum_value_of(const std::string& value_string) { BOOST_STATIC_ASSERT((boost::is_enum<TEnum>::value)); return EnumTypeTraits<TEnum>::fromString(value_string); } /*! * \throw std::runtime_error if \a value_string is invalid and TEnum has no * \em invalid enum value. * * \note \b Silently returns the \em invalid enum on invalid \a value_string, * if TEnum has one. Throws otherwise. */ template <typename TEnum> void set_enum_value_from(const std::string& value_string, TEnum& value) { BOOST_STATIC_ASSERT((boost::is_enum<TEnum>::value)); value = enum_value_of<TEnum>(value_string); } template <typename TEnum> bool enum_has_invalid_value() { BOOST_STATIC_ASSERT((boost::is_enum<TEnum>::value)); return EnumTypeTraits<TEnum>::hasInvalid(); } template <typename TEnum> TEnum enum_invalid_value() { BOOST_STATIC_ASSERT((boost::is_enum<TEnum>::value)); return EnumTypeTraits<TEnum>::invalid(); } template <typename TEnum> bool enum_is_valid_value(TEnum value) { BOOST_STATIC_ASSERT((boost::is_enum<TEnum>::value)); return EnumTypeTraits<TEnum>::isValidCode(value); } Although it's not fancy, I think this nicely covers a piece of functionality I often need, and it does so avoiding any downside compared to using a simple enum (beyond having to use macros). Hope other people will agree! I definitely do! --DD _______________________________________________ Unsubscribe & other changes: [4]http://lists.boost.org/mailman/listinfo.cgi/boo st References 1. mailto:boost@lists.boost.org 2. http://goo.gl/WBLL6 3. https://github.com/quicknir/wise_enum 4. http://lists.boost.org/mailman/listinfo.cgi/boost
participants (4)
-
Dominique Devienne
-
Jeffrey Graham
-
Nir Friedman
-
Vicente J. Botet Escriba