我已经开始使用 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
最佳答案
使用 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
——这是目前库中唯一的东西被迫延迟到运行时。
关于c++ - 使用 Boost PP 枚举到 std::string - 改进语法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29299991/