我认为像Boost.Fusion一样的解决方案,用于适应结构体和类,使用枚举作为融合序列会很好。他们甚至在某个时候也有这个功能。
所以我只是编写了一些小型宏来生成打印枚举的代码。这并不完美,与Boost.Fusion生成的样板代码无关,但可以像Boost Fusion宏一样使用。我想真正生成Boost.Fusion所需的类型,以便将其集成到允许打印结构体成员名称的基础设施中,但这将在以后发生,现在只是宏:
#ifndef SWISSARMYKNIFE_ENUMS_ADAPT_ENUM_HPP
#define SWISSARMYKNIFE_ENUMS_ADAPT_ENUM_HPP
#include <swissarmyknife/detail/config.hpp>
#include <string>
#include <ostream>
#include <boost/preprocessor/cat.hpp>
#include <boost/preprocessor/stringize.hpp>
#include <boost/preprocessor/seq/for_each.hpp>
#define SWISSARMYKNIFE_ADAPT_ENUM_EACH_ENUMERATION_ENTRY_C( \
R, unused, ENUMERATION_ENTRY) \
case ENUMERATION_ENTRY: \
return BOOST_PP_STRINGIZE(ENUMERATION_ENTRY); \
break;
#define SWISSARMYKNIFE_ADAPT_ENUM(ENUM_TYPE, ENUMERATION_SEQ) \
inline std::string to_string(const ENUM_TYPE& enum_value) { \
switch (enum_value) { \
BOOST_PP_SEQ_FOR_EACH( \
SWISSARMYKNIFE_ADAPT_ENUM_EACH_ENUMERATION_ENTRY_C, \
unused, ENUMERATION_SEQ) \
default: \
return BOOST_PP_STRINGIZE(ENUM_TYPE); \
} \
} \
\
inline std::ostream& operator<<(std::ostream& os, const ENUM_TYPE& value) { \
os << to_string(value); \
return os; \
}
#endif
下面的旧答案非常糟糕,请不要使用它。:)
旧答案:
我一直在寻找一种方法,可以在不太改变枚举声明语法的情况下解决这个问题。我想到了一个解决方案,它使用预处理器从字符串化的枚举声明中检索字符串。
我能够像这样定义非稀疏的枚举:
SMART_ENUM(State,
enum State {
RUNNING,
SLEEPING,
FAULT,
UNKNOWN
})
我可以以不同的方式与它们进行交互:
std::stringstream ss;
ss << State::FAULT;
std::string myEnumStr = ss.str();
std::cout << State::FAULT << std::endl;
std::string myStr = State::to_string(State::FAULT);
State::State myEnumVal = State::from_string(State::FAULT);
基于以下定义:
#define SMART_ENUM(enumTypeArg, ...) \
namespace enumTypeArg { \
__VA_ARGS__; \
std::ostream& operator<<(std::ostream& os, const enumTypeArg& val) { \
os << swissarmyknife::enums::to_string(#__VA_ARGS__, val); \
return os; \
} \
\
std::string to_string(const enumTypeArg& val) { \
return swissarmyknife::enums::to_string(#__VA_ARGS__, val); \
} \
\
enumTypeArg from_string(const std::string &str) { \
return swissarmyknife::enums::from_string<enumTypeArg>(#__VA_ARGS__, str); \
} \
} \
namespace swissarmyknife { namespace enums {
static inline std::string to_string(const std::string completeEnumDeclaration, size_t enumVal) throw (std::runtime_error) {
size_t begin = completeEnumDeclaration.find_first_of('{');
size_t end = completeEnumDeclaration.find_last_of('}');
const std::string identifiers = completeEnumDeclaration.substr(begin + 1, end );
size_t count = 0;
size_t found = 0;
do {
found = identifiers.find_first_of(",}", found+1);
if (enumVal == count) {
std::string identifiersSubset = identifiers.substr(0, found);
size_t beginId = identifiersSubset.find_last_of("{,");
identifiersSubset = identifiersSubset.substr(beginId+1);
boost::algorithm::trim(identifiersSubset);
return identifiersSubset;
}
++count;
} while (found != std::string::npos);
throw std::runtime_error("The enum declaration provided doesn't contains this state.");
}
template <typename EnumType>
static inline EnumType from_string(const std::string completeEnumDeclaration, const std::string &enumStr) throw (std::runtime_error) {
size_t begin = completeEnumDeclaration.find_first_of('{');
size_t end = completeEnumDeclaration.find_last_of('}');
const std::string identifiers = completeEnumDeclaration.substr(begin + 1, end );
size_t count = 0;
size_t found = 0;
do {
found = identifiers.find_first_of(",}", found+1);
std::string identifiersSubset = identifiers.substr(0, found);
size_t beginId = identifiersSubset.find_last_of("{,");
identifiersSubset = identifiersSubset.substr(beginId+1);
boost::algorithm::trim(identifiersSubset);
if (identifiersSubset == enumStr) {
return static_cast<EnumType>(count);
}
++count;
} while (found != std::string::npos);
throw std::runtime_error("No valid enum value for the provided string");
}
}}
当我需要支持稀疏枚举时,或者当我有更多时间时,我将使用boost::xpressive改进to_string和from_string实现,但这会导致编译时间增加,因为执行了重要的模板化操作,生成的可执行文件可能会变得非常大。但这样做的优点是它比这个丑陋的手动字符串操作代码更易读和易于维护。:D
否则,我总是使用boost::bimap来执行枚举值和字符串之间的映射,但必须手动维护。