我开始使用Boost的PP库,目的是编写一些小工具来帮助将枚举转换为字符串。
我已经得到了一个解决方案,其中使用宏定义的枚举也将生成补充的ToString函数。
我玩过各种语法风格,但我无法让一个特定的安排工作。
这就是我所拥有的。
namespace Examples
{
// This will define an enum class with an underlying uint8_t type
ENUM_W_STR
(fruit, uint8_t,
(apple)
(banana)
);
// Like above but with a different construction syntax
ENUM_W_STR_2
(fruit2, uint8_t,
apple,
banana
);
// This variant allows values to be assigned
ENUM_W_STR_VAL
(fruit3, uint8_t,
(apple) (1)
(banana) (2)
(orange) (3)
);
}
但是,这就是我想要的。
ENUM_W_STR_2
(fruit2, uint8_t,
apple = 10,
banana = 20
);
我遇到了分割' apple = 10'串 在ToString中生成switch语句时。即,我可以建造 枚举但不是ToString函数。
这可以实现吗?如果没有Boost PP,那么其他一些方法呢?
这是源代码(g ++ 4.7.2,boost 1.50; clang 3.3 boost 1.57)
#include <boost/preprocessor.hpp>
#include <iostream>
namespace EnumUtils{
#define ENUMUTLS_STR_SWITCH_CASE(r, data, elem)\
case data::elem : return BOOST_PP_STRINGIZE(elem);
#define ENUMUTIL_EVEN(r, data,i, v) BOOST_PP_IIF(BOOST_PP_MOD_D(r, i, 2), ,v)
#define ENUMUTIL_ODD( r, data,i, v) BOOST_PP_IIF(BOOST_PP_MOD_D(r, i, 2),= v, )
#define ENUMUTIL_ENUM_LINE(r, data, i, v) ENUMUTIL_EVEN(r,data,i,v) ENUMUTIL_ODD(r,data,i,v) BOOST_PP_COMMA_IF(BOOST_PP_MOD_D(r,i,2))
#define ENUMUTLS_STR_SWITCH_CASE_VAL_BASE(r, data, i, v) BOOST_PP_IIF(BOOST_PP_MOD_D(r, i, 2),,ENUMUTLS_STR_SWITCH_CASE(r, data, v))
#define ENUMUTLS_STR_SWITCH_CASE_VAL( r, data, i, v) ENUMUTLS_STR_SWITCH_CASE_VAL_BASE(r,data,i,v)
#define ENUM_W_STR(name, type, seq)\
enum class name : type {\
BOOST_PP_SEQ_ENUM(seq)\
};\
inline const char* ToString(name v)\
{\
switch (v)\
{\
BOOST_PP_SEQ_FOR_EACH(\
ENUMUTLS_STR_SWITCH_CASE,\
name,\
seq)\
default: return "unknown " BOOST_PP_STRINGIZE(name);\
}\
}
#define ENUM_W_STR_2(name, type, args...)\
enum class name : type {\
BOOST_PP_SEQ_ENUM(BOOST_PP_VARIADIC_TO_SEQ(args))\
};\
inline const char* ToString(name v)\
{\
switch (v)\
{\
BOOST_PP_SEQ_FOR_EACH(\
ENUMUTLS_STR_SWITCH_CASE,\
name,\
BOOST_PP_VARIADIC_TO_SEQ(args))\
default: return "unknown " BOOST_PP_STRINGIZE(name);\
}\
}
#define ENUM_W_STR_VAL(name, type, seq)\
enum class name : type {\
BOOST_PP_SEQ_FOR_EACH_I(\
ENUMUTIL_ENUM_LINE,\
name,\
seq)\
};\
inline const char* ToString(name v)\
{\
switch (v)\
{\
BOOST_PP_SEQ_FOR_EACH_I(\
ENUMUTLS_STR_SWITCH_CASE_VAL,\
name,\
seq)\
default: return "unknown " BOOST_PP_STRINGIZE(name) ;\
}\
};
}
namespace Examples
{
ENUM_W_STR
(fruit, uint8_t,
(apple)
(banana)
);
ENUM_W_STR_2
(fruit2, uint8_t,
apple,
banana
);
ENUM_W_STR_VAL
(fruit3, uint8_t,
(apple) (1)
(banana) (2)
(orange) (3)
);
}
int main ()
{
std::cout << Examples::ToString(Examples::fruit::apple) << std::endl;
std::cout << Examples::ToString(Examples::fruit::banana) << std::endl;
std::cout << Examples::ToString(Examples::fruit2::apple) << std::endl;
std::cout << Examples::ToString(Examples::fruit2::banana) << std::endl;
std::cout << Examples::ToString(Examples::fruit3::apple) << std::endl;
std::cout << Examples::ToString(Examples::fruit3::banana) << std::endl;
return 0;
}
输出:
apple
banana
apple
banana
apple
banana
答案 0 :(得分:0)
使用C ++ 11 constexpr
函数和宏可以实现这种修剪。以下是该方法的概述:
#include <iostream>
constexpr const char terminators[] = " \t\r\n=";
// Note that this count includes the null terminator, which is deliberate.
constexpr size_t terminator_count = sizeof(terminators);
// Checks if a character is a terminator at compile time.
constexpr bool is_terminator(char c, size_t index = 0)
{
return
index >= terminator_count ? false :
c == terminators[index] ? true :
is_terminator(c, index + 1);
}
// Computes enum constant length at compile time.
constexpr size_t constant_length(const char *s, size_t index = 0)
{
return is_terminator(s[index]) ? index : constant_length(s, index + 1);
}
// Evaluates to characters from "from" up to length of "from".
constexpr char select(const char *from, size_t from_length, size_t index)
{
return index >= from_length ? '\0' : from[index];
}
constexpr const char *foo = "apple = 10";
constexpr size_t foo_length = constant_length(foo);
// You should use a macro to generate this, either from Boost, or your own.
// Ideally, the user would be able to change the maximum string length.
constexpr const char trimmed[] =
{select(foo, foo_length, 0), select(foo, foo_length, 1),
select(foo, foo_length, 2), select(foo, foo_length, 3),
select(foo, foo_length, 4), select(foo, foo_length, 5),
select(foo, foo_length, 6), select(foo, foo_length, 7)};
int main()
{
std::cout << foo << std::endl;
std::cout << trimmed << std::endl;
return 0;
}
输出
apple = 10
apple
常量名称的最大长度存在限制,您可以从trimmed
的定义方式中看出。正如它所说,最大长度应该由一个可选宏控制,用户可以定义它们是否遇到问题。当然,默认值应该大于我在上面草图中看到的8:)
如果您感兴趣,我刚刚发布了一个枚举库,其声明语法几乎与您在问题中描述的相同(尽管内部结构完全不同):http://aantron.github.io/better-enums
我将很快将这种编译时字符串修剪添加到库中以使其to_string
方法constexpr
- 它是目前唯一被迫延迟到运行时的库。