patterncppMinor
Automatic C++ enum to string mapping macro
Viewed 0 times
enummacromappingautomaticstring
Problem
I have long been looking for an elegant, one-line solution to map
I know that a lot of macros have been proposed to achieve that, but I never found a really simple scheme, with only one macro call.
This is my attempt, and I would be interested to discuss the advantages and limitations of this approach...
Definitions:
```
#include
#include
#include
#include
#include
#include
#include
#define MAKE_STRING(str, ...) #str, MAKE_STRING1_(__VA_ARGS__)
#define MAKE_STRING1_(str, ...) #str, MAKE_STRING2_(__VA_ARGS__)
#define MAKE_STRING2_(str, ...) #str, MAKE_STRING3_(__VA_ARGS__)
#define MAKE_STRING3_(str, ...) #str, MAKE_STRING4_(__VA_ARGS__)
#define MAKE_STRING4_(str, ...) #str, MAKE_STRING5_(__VA_ARGS__)
#define MAKE_STRING5_(str, ...) #str, MAKE_STRING6_(__VA_ARGS__)
#define MAKE_STRING6_(str, ...) #str, MAKE_STRING7_(__VA_ARGS__)
#define MAKE_STRING7_(str, ...) #str, MAKE_STRING8_(__VA_ARGS__)
#define MAKE_STRING8_(str, ...) #str, MAKE_STRING9_(__VA_ARGS__)
#define MAKE_STRING9_(str, ...) #str, MAKE_STRING10_(__VA_ARGS__)
#define MAKE_STRING10_(str) #str
#define MAKE_ENUM(name, ...) MAKE_ENUM_(, name, __VA_ARGS__)
#define MAKE_CLASS_ENUM(name, ...) MAKE_ENUM_(friend, name, __VA_ARGS__)
#define MAKE_ENUM_(attribute, name, ...) name { __VA_ARGS__ }; \
attribute std::istream& operator>>(std::istream& is, name& e) { \
const char* name##Str[] = { MAKE_STRING(__VA_ARGS__) }; \
std::string str; \
std::istream& r = is >> str; \
const size_t len = sizeof(name##Str)/sizeof(name##Str[0]); \
const std::vector enumStr(name##Str, name##Str + len); \
const std::vector::const_iterator it = std::find(enumStr.begin(), enumStr.end(), str); \
if (it != enumStr.end())\
e = name(it - enumStr.begin()); \
else \
throw std::runtime_error("Value \"" + str + "\" is not part of enum "#name); \
return r; \
}; \
enum to strings automatically, for use with the > stream operators.I know that a lot of macros have been proposed to achieve that, but I never found a really simple scheme, with only one macro call.
This is my attempt, and I would be interested to discuss the advantages and limitations of this approach...
Definitions:
```
#include
#include
#include
#include
#include
#include
#include
#define MAKE_STRING(str, ...) #str, MAKE_STRING1_(__VA_ARGS__)
#define MAKE_STRING1_(str, ...) #str, MAKE_STRING2_(__VA_ARGS__)
#define MAKE_STRING2_(str, ...) #str, MAKE_STRING3_(__VA_ARGS__)
#define MAKE_STRING3_(str, ...) #str, MAKE_STRING4_(__VA_ARGS__)
#define MAKE_STRING4_(str, ...) #str, MAKE_STRING5_(__VA_ARGS__)
#define MAKE_STRING5_(str, ...) #str, MAKE_STRING6_(__VA_ARGS__)
#define MAKE_STRING6_(str, ...) #str, MAKE_STRING7_(__VA_ARGS__)
#define MAKE_STRING7_(str, ...) #str, MAKE_STRING8_(__VA_ARGS__)
#define MAKE_STRING8_(str, ...) #str, MAKE_STRING9_(__VA_ARGS__)
#define MAKE_STRING9_(str, ...) #str, MAKE_STRING10_(__VA_ARGS__)
#define MAKE_STRING10_(str) #str
#define MAKE_ENUM(name, ...) MAKE_ENUM_(, name, __VA_ARGS__)
#define MAKE_CLASS_ENUM(name, ...) MAKE_ENUM_(friend, name, __VA_ARGS__)
#define MAKE_ENUM_(attribute, name, ...) name { __VA_ARGS__ }; \
attribute std::istream& operator>>(std::istream& is, name& e) { \
const char* name##Str[] = { MAKE_STRING(__VA_ARGS__) }; \
std::string str; \
std::istream& r = is >> str; \
const size_t len = sizeof(name##Str)/sizeof(name##Str[0]); \
const std::vector enumStr(name##Str, name##Str + len); \
const std::vector::const_iterator it = std::find(enumStr.begin(), enumStr.end(), str); \
if (it != enumStr.end())\
e = name(it - enumStr.begin()); \
else \
throw std::runtime_error("Value \"" + str + "\" is not part of enum "#name); \
return r; \
}; \
Solution
I agree with the comments: macros are a mess. Avoid them when possible.
If you're willing to make certain sacrifices (a little repetition), it's possible to avoid macros here. Doing so will also avoid some of the arbitrary limitations in your macro implementation, such as the 10 or so cap on enum values.
Using C++11, it's not hard to wire up Loki Astari's approach to
It's also not hard to pull Loki Astari's approach into the
This general approach should be available before C++11 as well, but
If you're willing to make certain sacrifices (a little repetition), it's possible to avoid macros here. Doing so will also avoid some of the arbitrary limitations in your macro implementation, such as the 10 or so cap on enum values.
Using C++11, it's not hard to wire up Loki Astari's approach to
operator> with some type traits template metaprogramming. I personally think enums are much cleaner with the strongly typed enum class ... variant introduced by C++11, so I would suggest that anyway, but the enum_serializable approach I show here works on both strongly and weakly typed enums.#include
#include
#include
template
struct enum_serializable : std::false_type
{};
// enable operator> for `Essai` values
template <>
struct enum_serializable : std::true_type
{};
template
typename std::enable_if::value, std::ostream&
>::type operator
typename std::enable_if::value, std::istream&
>::type operator>>(std::istream& is, Enum& e)
{
return is >> enumFromString(e);
}It's also not hard to pull Loki Astari's approach into the
operator> functions above, removing the separate enumToString and enumFromString functions and the enumRefHolder and enumConstRefHolder structs entirely.This general approach should be available before C++11 as well, but
std::enable_if will have to be implemented instead of #included.Code Snippets
#include <type_traits>
#include <iostream>
#include <https://codereview.stackexchange.com/a/14315/507 by reference>
template <typename Enum>
struct enum_serializable : std::false_type
{};
// enable operator<< and operator>> for `Essai` values
template <>
struct enum_serializable<Essai> : std::true_type
{};
template <typename Enum>
typename std::enable_if<enum_serializable<Enum>::value, std::ostream&
>::type operator<<(std::ostream& os, Enum e)
{
return os << enumToString(e);
}
template <typename Enum>
typename std::enable_if<enum_serializable<Enum>::value, std::istream&
>::type operator>>(std::istream& is, Enum& e)
{
return is >> enumFromString(e);
}Context
StackExchange Code Review Q#43725, answer score: 2
Revisions (0)
No revisions yet.