背景
业务中涉及枚举的打印,一般采用%d
的方式打印输出,此方式打印效果不直观,需要一种将枚举转为字符串类型的打印方法。本文提出的解决思路不一定适合所有的场景,请各位小伙伴自行斟酌。
实现思路
利用boost
库中的BOOST_DESCRIBE_ENUM
和mp_for_each
配合,可实现枚举的打印,官方提供的示例代码如下:
此种枚举转字符串的方式有个限制,若是枚举项超出52个,则无法使用BOOST_DESCRIBE_ENUM
,这个受限于boost
的实现。
基于上面的这种方式可以实现枚举转字符串,在利用%s
打印输出的时候,为了提升使用的便捷性,可以定义TOCHAR宏,如下:
#define TOCHAR(e) ToString(e)
// 使用示例
enum class Color {
RED = 0,
YELLOW = 1,
GREEN = 2
}
LOG("Current color is %s", TOCHAR(Color::Red)); // 输出:Current color is RED(1)
扩展
基于上面的实现,可以尝试将所有的类型都纳入到ToString
函数中,实现对所有类型转string。
#define TOCHAR(T) ToString(T)
// 枚举类型转换为字符串的模板函数
template <typename E>
inline typename std::enable_if<std::is_enum<E>::value, std::string>::type ToString(const E& enumerator)
{
std::string result = "[UNDEFINED]";
boost::mp11::mp_for_each< boost::describe::describe_enumerators<E> >([&](auto description){
if (enumerator == description.value) {
result = description.name;
}
});
return result + std::string("(") + std::to_string(static_cast<int>(enumerator)) + std::string(")");
}
// 基本类型转换为字符串的模板函数
template <typename T>
inline typename std::enable_if<!std::is_enum<T>::value, std::string>::type ToString(T value)
{
return boost::lexical_cast<std::string>(value);
}
// boolean转换为字符串的模板函数
inline std::string ToString(bool boolean)
{
return boolean ? "true" : "false";
}
// vector转换为字符串的模板函数
template <typename T>
inline std::string ToString(const std::vector<T>& vec)
{
std::stringstream ss;
ss << "[";
if (!vec.empty())
{
// 输出第一个元素
ss << ToString(vec[0]);
// 输出剩余元素
for (size_t i = 1; i < vec.size(); ++i)
{
ss << ", " << ToString(vec[i]);
}
}
ss << "]";
return ss.str();
}
// set转换为字符串的模板函数
template<typename T>
inline std::string ToString(const std::set<T>& container) {
std::ostringstream oss;
oss << "[";
for (const auto& element : container) {
oss << ToString(element) << ", ";
}
std::string result = oss.str();
if (!result.empty()) {
result.pop_back(); // 移除最后一个逗号
result.pop_back(); // 移除空格
}
result += "]";
return result;
}
// map转换为字符串的模板函数
template<typename KeyType, typename ValueType>
inline std::string ToString(const std::map<KeyType, ValueType>& inputMap) {
std::ostringstream oss;
oss << "{";
for (const auto& pair : inputMap) {
oss << "{"<< ToString(pair.first) << ": " << ToString(pair.second) << "}, ";
}
std::string result = oss.str();
if (!result.empty()) {
result.pop_back(); // 移除最后一个逗号
result.pop_back(); // 移除空格
}
result += "}}";
return result;
}
// unordered_map转换为字符串的模板函数
template<typename KeyType, typename ValueType>
inline std::string ToString(const std::unordered_map<KeyType, ValueType>& inputMap) {
std::ostringstream oss;
oss << "{";
for (const auto& pair : inputMap) {
oss << "{"<< ToString(pair.first) << ": " << ToString(pair.second) << "}, ";
}
std::string result = oss.str();
if (!result.empty()) {
result.pop_back(); // 移除最后一个逗号
result.pop_back(); // 移除空格
}
result += "}}";
return result;
}
测试用例
TEST_F(TestBoost, ToString_for_basic_type)
{
Value e = Value::Value2;
int intVar = 123;
short shortVar = 456;
long longVar = 789;
long long longLongVar = 1234567890;
char charVar = 'A';
unsigned int unsignedIntVar = 234;
float floatVar = 3.14f;
double doubleVar = 2.71828;
long double longDoubleVar = 1.618033988749895;
bool boolVar = true;
EXPECT_EQ(ToString(e), "Value2(2)");
EXPECT_EQ(ToString(intVar), "123");
EXPECT_EQ(ToString(shortVar), "456");
EXPECT_EQ(ToString(longVar), "789");
EXPECT_EQ(ToString(longLongVar), "1234567890");
EXPECT_EQ(ToString(charVar), "A");
EXPECT_EQ(ToString(unsignedIntVar), "234");
EXPECT_EQ(ToString(floatVar), "3.1400001");
EXPECT_EQ(ToString(doubleVar), "2.71828");
EXPECT_EQ(ToString(longDoubleVar), "1.6180339887498949");
EXPECT_EQ(ToString(boolVar), "true");
}
TEST_F(TestBoost, ToString_for_container_type)
{
std::vector<std::string> stringVector = {"x", "y", "z"};
std::set<double> doubleSet = {1.1, 2.2, 3.3, 4.4, 5.5};
std::map<std::string, int> doubleMap {{"apple", 5.1}, {"banana", 3.2}, {"orange", 8.3}};
std::unordered_map<std::string, int> doubleUnorderedMap {{"apple", 5.1}, {"banana", 3.2}, {"orange", 8.3}};
std::unordered_map<std::string, std::vector<int>> nestedUnorderedMap {{"a", {1, 2}}, {"b", {3}}, {"c", {4, 5, 6}}};
EXPECT_EQ(ToString(stringVector), "[x, y, z]");
EXPECT_EQ(ToString(doubleSet), "[1.1000000000000001, 2.2000000000000002, 3.2999999999999998, 4.4000000000000004, 5.5]");
EXPECT_EQ(ToString(doubleMap), "{{apple: 5}, {banana: 3}, {orange: 8}}}");
EXPECT_EQ(ToString(doubleUnorderedMap), "{{orange: 8}, {banana: 3}, {apple: 5}}}");
EXPECT_EQ(ToString(nestedUnorderedMap), "{{c: [4, 5, 6]}, {b: [3]}, {a: [1, 2]}}}");
}
若是需要对自定义的结构体也纳入TOCHAR
需要定义一套该结构体的ToString
方法。