在过去的一周里,我一直在寻找一种方法来做你所要求的,等等。这是我想出的:
#define STD_ENUM_ENTRY_WITHOUT_VALUE__(ENUM, NAME) NAME,
#define STD_ENUM_ENTRY_WITHOUT_VALUE_(ENUM, NAME) STD_ENUM_ENTRY_WITHOUT_VALUE__(ENUM, NAME)
#define STD_ENUM_ENTRY_WITHOUT_VALUE(ENUM, SPLIT...) STD_ENUM_ENTRY_WITHOUT_VALUE_(ENUM, SPLIT)
#define STD_ENUM_ENTRY_WITH_VALUE__(ENUM, NAME, VALUE) NAME = VALUE,
#define STD_ENUM_ENTRY_WITH_VALUE_(ENUM, NAME, VALUE) STD_ENUM_ENTRY_WITH_VALUE__(ENUM, NAME, VALUE)
#define STD_ENUM_ENTRY_WITH_VALUE(ENUM, SPLIT...) STD_ENUM_ENTRY_WITH_VALUE_(ENUM, SPLIT)
#define FP_PP_ENUM_STD_ENTRY_1(ENUM, SPLIT...) STD_ENUM_ENTRY_WITHOUT_VALUE(ENUM, SPLIT)
#define FP_PP_ENUM_STD_ENTRY_2(ENUM, SPLIT...) STD_ENUM_ENTRY_WITH_VALUE(ENUM, SPLIT)
#define FP_PP_ENUM_STD_ENTRY__(N, ENUM, SPLIT...) FP_PP_ENUM_STD_ENTRY_##N(ENUM, SPLIT)
#define FP_PP_ENUM_STD_ENTRY_(N, ENUM, VALUE) FP_PP_ENUM_STD_ENTRY__(N, ENUM, FP_PP_EXPAND VALUE)
#define FP_PP_ENUM_STD_ENTRY(ENUM, VALUE) FP_PP_ENUM_STD_ENTRY_(FP_PP_NUM_ARGS VALUE, ENUM, VALUE)
#define FP_PP_ENUM_EXT_ENTRY_WITHOUT_VALUE__(ENUM, NAME) ::fp::enum_entry<ENUM>(ENUM::NAME, #NAME),
#define FP_PP_ENUM_EXT_ENTRY_WITHOUT_VALUE_(ENUM, NAME) FP_PP_ENUM_EXT_ENTRY_WITHOUT_VALUE__(ENUM, NAME)
#define FP_PP_ENUM_EXT_ENTRY_WITHOUT_VALUE(ENUM, SPLIT...) FP_PP_ENUM_EXT_ENTRY_WITHOUT_VALUE_(ENUM, SPLIT)
#define FP_PP_ENUM_EXT_ENTRY_WITH_VALUE__(ENUM, NAME, VALUE) ::fp::enum_entry<ENUM>(ENUM::NAME, #NAME),
#define FP_PP_ENUM_EXT_ENTRY_WITH_VALUE_(ENUM, NAME, VALUE) FP_PP_ENUM_EXT_ENTRY_WITH_VALUE__(ENUM, NAME, VALUE)
#define FP_PP_ENUM_EXT_ENTRY_WITH_VALUE(ENUM, SPLIT...) FP_PP_ENUM_EXT_ENTRY_WITH_VALUE_(ENUM, SPLIT)
#define FP_PP_ENUM_EXT_ENTRY_1(ENUM, SPLIT...) FP_PP_ENUM_EXT_ENTRY_WITHOUT_VALUE(ENUM, SPLIT)
#define FP_PP_ENUM_EXT_ENTRY_2(ENUM, SPLIT...) FP_PP_ENUM_EXT_ENTRY_WITH_VALUE(ENUM, SPLIT)
#define FP_PP_ENUM_EXT_ENTRY__(N, ENUM, SPLIT...) FP_PP_ENUM_EXT_ENTRY_##N(ENUM, SPLIT)
#define FP_PP_ENUM_EXT_ENTRY_(N, ENUM, VALUE) FP_PP_ENUM_EXT_ENTRY__(N, ENUM, FP_PP_EXPAND VALUE)
#define FP_PP_ENUM_EXT_ENTRY(ENUM, VALUE) FP_PP_ENUM_EXT_ENTRY_(FP_PP_NUM_ARGS VALUE, ENUM, VALUE)
#define DEFINE_EXT_ENUM(ENUM, ...) \
enum class ENUM { \
FP_PP_SEQ_FOR_EACH(FP_PP_ENUM_STD_ENTRY, ENUM, __VA_ARGS__) \
}; \
template<typename> \
struct enum_descriptor; \
\
template<> \
struct enum_descriptor<ENUM> { \
public: \
using enum_type = ENUM; \
using entry_type = ::fp::enum_entry<enum_type>; \
using this_type = enum_descriptor<enum_type>; \
using const_iterator = entry_type const *; \
using const_reverse_iterator = std::reverse_iterator<const_iterator>; \
using size_type = std::size_t; \
private: \
constexpr static std::size_t Size = FP_PP_NUM_ARGS(__VA_ARGS__); \
using container_type = std::array<entry_type, Size>; \
\
constexpr static container_type const _entries \
{ \
{ \
FP_PP_SEQ_FOR_EACH(FP_PP_ENUM_EXT_ENTRY, ENUM, __VA_ARGS__) \
} \
}; \
\
template<std::size_t... Is > \
constexpr static char const * name_of_impl(enum_type v, ::fp::indices<Is...>) { \
using std::get; \
return ::fp::enum_helper<enum_type>::get_name(v, get<Is>(_entries)...); \
} \
\
template<std::size_t... Is > \
constexpr static enum_type value_of_impl(char const * n, ::fp::indices<Is...>) { \
using std::get; \
return ::fp::enum_helper<enum_type>::get_value(n, get<Is>(_entries)...); \
} \
\
template<typename V, std::size_t... Is > \
static bool try_parse_impl(V val, enum_type & res, ::fp::indices<Is...>) { \
using std::get; \
return (::fp::enum_helper<enum_type>::is_valid_entry(val, get<Is>(_entries)...)) ? \
((res = static_cast<enum_type> (val)), true) \
: false; \
} \
\
template<typename V, std::size_t... Is > \
constexpr static enum_type parse_impl(V val, ::fp::indices<Is...>) { \
using std::get; \
return (::fp::enum_helper<enum_type>::parse(val, get<Is>(_entries)...)); \
} \
public: \
constexpr enum_descriptor() = default; \
enum_descriptor(enum_descriptor const &) = delete; \
enum_descriptor(enum_descriptor &&) = delete; \
\
constexpr static char const * name() noexcept { \
return #ENUM; \
} \
\
constexpr static char const * name_of(enum_type value) { \
return name_of_impl(value, ::fp::build_indices<Size>()); \
} \
\
constexpr static enum_type value_of(char const * name) { \
return value_of_impl(name, ::fp::build_indices<Size>()); \
} \
\
constexpr static size_type size() noexcept { \
return Size; \
} \
\
constexpr static const_iterator begin() { \
using std::get; \
return const_iterator(&get<0>(_entries)); \
} \
\
constexpr static const_iterator end() { \
using std::get; \
return const_iterator(&get<(Size - 1)>(_entries) + 1); \
} \
\
template<typename T, \
typename = typename std::enable_if<std::is_integral<T>::value>::type> \
static bool try_parse(T value, enum_type & res){ \
return try_parse_impl(value, res, ::fp::build_indices<Size>()); \
} \
\
template<typename T, \
typename = typename std::enable_if<std::is_integral<T>::value>::type> \
constexpr static enum_type parse(T value){ \
return parse_impl(value, ::fp::build_indices<Size>()); \
} \
}; \
template<> \
constexpr std::array<::fp::enum_entry<ENUM>, FP_PP_NUM_ARGS(__VA_ARGS__)> const enum_descriptor<ENUM>::_entries;
您可以在我的github上查看完整的代码(包括示例)。
尽管这符合我的预期,但它不能(直接)用作现有枚举的替代品。
为了节省大量的编码,你想要支持的枚举应该像这样定义:
DEFINE_EXT_ENUM(my_1st_enum, (fread, 3), (fwrite), (fflush, fread << 2));
DEFINE_EXT_ENUM(my_2nd_enum, (fopen), (fclose, 1));
最后一件事:不要尝试使用 -Werr 标志在 GCC 上编译它,因为编译会出错(我明白为什么,但我目前不知道如何解决它)。
编辑:
根据您的示例,这就是使用我的 DEFINE_EXT_ENUM 的方式:
#include "enum_pp_def.hpp"
#include <cassert>
#include <iostream>
DEFINE_EXT_ENUM(turn, (right,1), (left,2));
DEFINE_EXT_ENUM(hand, (right,1), (left,2));
using turn_descr = enum_descriptor<turn>;
using hand_descr = enum_descriptor<hand>;
/* 2 */
constexpr char const * foo(turn t) {
return (turn_descr::is_valid((int)t)) ? (turn::right == t) ? "right turn" : "left turn" : throw t;
}
constexpr char const * foo(hand h) {
return (hand_descr::is_valid((int)h)) ? (hand::right == h) ? "right hand" : "left hand" : throw h;
}
/* End 2 */
int main(int argc, char ** argv) {
turn t1 = turn_descr::parse(1); // 3.a OK
turn t2(turn::right); // 3b. OK
hand h1(hand::left); // 3c. OK
hand h2 = hand_descr::parse(2); // 3d. OK
assert(t1 == turn::right); // 4. OK
/* 8 */
switch(t1) {
case turn::right:
std::cout << "right turn" << std::endl;
break;
case turn::left:
std::cout << "left turn" << std::endl;
break;
}
/* End 8 */
std::cout << foo(hand::left) << std::endl;
std::cout << foo(turn::right) << std::endl;
constexpr turn t3 = turn_descr::parse(3) // throw at compile time
turn t4 = turn_descr::parse(3); // throw at runtime
}