工具类库系列(九)-ReflectEnum

介绍了一种C++枚举反射工具,通过宏定义和Boost Spirit解析枚举内容,实现枚举值与字符串的互相转换,提高日志输出的可读性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

第九个工具类:ReflectEnum


用于枚举的反射,需要boost库spirit的支持


C++是不支持反射的,在一些日志输出的时候,输出一个枚举变量,只能输出这个变量当前的值,而这个值对应的那个枚举字符串是什么,就不好输出了,或者人肉反射,一长串的switch...case


这个工具类,就是为了解决这个问题,可以根据枚举变量的值,输出对应的枚举字符串,增加输出内容的可读性


但是我们肯定是希望能减少对现有代码的修改,或者稍微修改就能使用。

那么能想到的,就是利用宏,将整个enum XXX{...}的内容当成一个字符串,传给一个函数,

在这个函数中,按enum的语法规则,解析每一个枚举项的键值,存到一个map中,

然后提供两个函数,在这个map中跟据键查值, 和根据值查键,


比如,一开始已经写了一个枚举

enum TestEnum
{
	Test_1 = 0x03,
	Test_2 = Test_1 + 1,
	Test_3 = Test_2 + Test_1,
	Test_4 = Test_3 * Test_2 + Test_1,
	Test_5 = (Test_4 << Test_3) * (Test_2 + Test_1),
	Test_6 = (Test_5 & (Test_4 << Test_3)) * (Test_2 + Test_1),
};
将其修改成:

DefineEnum(TestEnum,
	Test_1 = 0x03,
	Test_2 = Test_1 + 1,
	Test_3 = Test_2 + Test_1,
	Test_4 = Test_3 * Test_2 + Test_1,
	Test_5 = (Test_4 << Test_3) * (Test_2 + Test_1),
	Test_6 = (Test_5 & (Test_4 << Test_3)) * (Test_2 + Test_1),
	);

将枚举定义的将头尾修改掉就可以了

那么这里就出现了一个宏:DefineEnum

在这个宏里面,要做两件事:

1,将这个DefineEnum(...)还原成符合c++语法规则的枚举代码。

2,将枚举定义的内容传给一个函数,并且这个函数最好是运行时能自动调用的,这样就不需要使用者手动去调解析函数了,那么全局对象的构造函数显然符合我们的需求。

于是:

#define DefineEnum(name, ...) \
	const common::tool::ReflectEnum g_Reflect##name(#__VA_ARGS__); \
enum name \
{ \
	__VA_ARGS__ \
};


现在出现了ReflectEnum,这个类就是用来解析枚举的内容,生成键值对用的了。

如何解析,就需要spirit出马了。


枚举的语法:每一个枚举项: 枚举名 (= 一个常量表达式),

其中常量表达式可能没有,没有的情况下,如果枚举名是枚举中的第一个,则默认值为0,否则为前一个枚举值+1

最后一个枚举结束可能没有逗号


于是首先我们用StringTool::SplitStr2List对枚举内容按“,”分割得到一系列枚举项

再对枚举项用“=”分割,得到常量表达式

这个常量表达式就是一个可能包含各种运算符和常量值的一个运算表达式,


用EBNF表示:

// 表达式计算的6个优先级:
// O0:(),数字,枚举名
// O0 ::= ('(' , O5 , ')') | (S0 , {S1}) | int | hex)
// O1:乘,除,模
// O1 ::= O0 , { ('*' , O0) | ('/' , O0) | ('%' , O0) }
// O2:加,减
// O2 ::= O1 , { ('+' , O1) | ('-' , O1) }
// O3:左移,右移
// O3 ::= O2 , { (">>" , O2) | ("<<" , O2) } 
// O4:按位与
// O4 ::= O3 , { '&' , O3 } 
// O5:按位或
// O5 ::= O4 , { '|' , O4 }

// 有效的枚举名的字符构成
// S0:首字符为 下划线,字母大/小写
// S0 ::= ([_] | [a-z] | [A-Z])
// S1:其他字符为 下划线,字母大/小写,数字
// S1 ::= ([_] | [a-z] | [A-Z] | [0-9])

按如上范式定义rule,交给spirit解析即可。


当然这个工具类也有一些限制:

1、支持的运算符包括:+,-,*,/,%,<<,>>,|,&,(),其他运算符未支持。

2、支持的常量包括:10/16进制数字,本枚举中前面已定义过的枚举值,不支持本枚举外定义的其他常量,比如另一个枚举中的枚举值,const值,宏定义,sizeof,因为这些值,仅依赖这个枚举自身的代码,是无法计算的

3、不支持值重复的枚举值,对于重复的值,enum XXX {A = 3, B = 3},跟据“A”,"B"可以都返回3,但是根据3,无法确定是返回“A” 还是 “B”(本工具类按最后定义的那个名字返回)


先给一个示例代码:

#include <iostream>

#include "ReflectEnum.h"

DefineEnum(TestEnum,
	Test_1 = 0x03,
	Test_2 = Test_1 + 1,
	Test_3 = Test_2 + Test_1,
	Test_4 = Test_3 * Test_2 + Test_1,
	Test_5 = (Test_4 << Test_3) * (Test_2 + Test_1),
	Test_6 = (Test_5 & (Test_4 << Test_3)) * (Test_2 + Test_1),
	);

int main(int argc, char* argv[])
{
	std::cout << "Test_1:" << Test_1 << "\t" << ReflectEnumName(TestEnum, Test_1) << ":" << ReflectEnumValue(TestEnum, "Test_1") << std::endl;
	std::cout << "Test_2:" << Test_2 << "\t" << ReflectEnumName(TestEnum, Test_2) << ":" << ReflectEnumValue(TestEnum, "Test_2") << std::endl;
	std::cout << "Test_3:" << Test_3 << "\t" << ReflectEnumName(TestEnum, Test_3) << ":" << ReflectEnumValue(TestEnum, "Test_3") << std::endl;
	std::cout << "Test_4:" << Test_4 << "\t" << ReflectEnumName(TestEnum, Test_4) << ":" << ReflectEnumValue(TestEnum, "Test_4") << std::endl;
	std::cout << "Test_5:" << Test_5 << "\t" << ReflectEnumName(TestEnum, Test_5) << ":" << ReflectEnumValue(TestEnum, "Test_5") << std::endl;
	std::cout << "Test_6:" << Test_6 << "\t" << ReflectEnumName(TestEnum, Test_6) << ":" << ReflectEnumValue(TestEnum, "Test_6") << std::endl;

	return 0;
}



输出:




完整代码:

ReflectEnum.h

#ifndef __ReflectEnum_h__
#define __ReflectEnum_h__

#include <string>
#include <map>
#include <stack>

namespace common{
	namespace tool{

		class ReflectEnum
		{
		public:
			ReflectEnum(){}
			~ReflectEnum(){}

		public:
			ReflectEnum(const char* enumValues);

		public:
			// 根据枚举值 获取 枚举字符串
			// 不存在的值返回""
			std::string GetEnumName(unsigned int enumValue) const;

			// 根据枚举字符串 获取 枚举值
			// 不存在的字符串返回0
			unsigned int GetEnumValue(const std::string& enumName) const;

		private:
			// 匹配+,-,*,/,%,<<,>>,|,&
			void Add();
			void Sub();
			void Mul();
			void Div();
			void Mod();
			void Shl();
			void Shr();
			void Or();
			void And();

		private:
			// 匹配数字
			void PushNum(unsigned int v);
			// 匹配枚举名
			void PushEnum(const char* start, const char* end);

		private:
			// 计算表达式的值
			unsigned int Calc(const char* expr);

		private:
			// 用于计算表达式值的栈
			std::stack<unsigned int> m_values;

		private:
			// 枚举键值对
			std::map<std::string, unsigned int> m_kv;
			std::map<unsigned int, std::string> m_vk;
		};

	}
}

////////////////////////////////////////////////////////////////////////////////////

#define DefineEnum(name, ...) \
	const common::tool::ReflectEnum g_Reflect##name(#__VA_ARGS__); \
enum name \
{ \
	__VA_ARGS__ \
};

////////////////////////////////////////////////////////////////////////////////////

#define ReflectEnumName(name, enumValue) (g_Reflect##name.GetEnumName(enumValue))
#define ReflectEnumValue(name, enumName) (g_Reflect##name.GetEnumValue(enumName))

#endif

ReflectEnum.cpp

#include "ReflectEnum.h"

#include <vector>

#include <boost/bind.hpp>
#include <boost/spirit/include/classic.hpp>

using namespace boost;
using namespace boost::spirit::classic;

#include "StringTool.h"

namespace common{
	namespace tool{

		ReflectEnum::ReflectEnum(const char* enumValues)
		{
			// 去制表符
			std::string str(enumValues);
			str = StringTool::TrimAll(str);

			// 分割枚举项
			std::vector<std::string> valuesEnum;
			StringTool::SplitStr2List(str, ",", valuesEnum);

			// 记录上一个枚举值,用于没有赋值的枚举项的值=上一个枚举项的值+1
			unsigned int lastEnumValue = 0;

			for (size_t i = 0; i < valuesEnum.size(); i++)
			{
				// 分割枚举项的键值对
				std::vector<std::string> valuesKV;
				StringTool::SplitStr2List(valuesEnum[i], "=", valuesKV);

				// 存在键值对,进行表达式计算枚举值
				if (2 == valuesKV.size())
				{
					unsigned int currEnumValue = Calc(valuesKV[1].c_str());

					m_kv[valuesKV[0]] = currEnumValue;
					m_vk[currEnumValue] = valuesKV[0];

					lastEnumValue = currEnumValue;
				}
				// 仅存在键,枚举值为上一项值+1,第一项默认为0
				else if (1 == valuesKV.size())
				{
					unsigned int currEnumValue = (0 == i) ? 0 : lastEnumValue + 1;

					m_kv[valuesKV[0]] = currEnumValue;
					m_vk[currEnumValue] = valuesKV[0];

					lastEnumValue = currEnumValue;
				}
				// 非法情况忽略
				else
				{

				}
			}
		}

		std::string ReflectEnum::GetEnumName(unsigned int enumValue) const
		{
			std::map<unsigned int, std::string>::const_iterator it = m_vk.find(enumValue);
			if (it != m_vk.end())
			{
				return it->second;
			}
			else
			{
				return "";
			}
		}

		unsigned int ReflectEnum::GetEnumValue(const std::string& enumName) const
		{
			std::map<std::string, unsigned int>::const_iterator it = m_kv.find(enumName);
			if (it != m_kv.end())
			{
				return it->second;
			}
			else
			{
				return 0;
			}
		}

		void ReflectEnum::Add()
		{
			unsigned int op2 = m_values.top();
			m_values.pop();
			unsigned int op1 = m_values.top();
			m_values.pop();

			m_values.push(op1 + op2);
		}

		void ReflectEnum::Sub()
		{
			unsigned int op2 = m_values.top();
			m_values.pop();
			unsigned int op1 = m_values.top();
			m_values.pop();

			m_values.push(op1 - op2);
		}

		void ReflectEnum::Mul()
		{
			unsigned int op2 = m_values.top();
			m_values.pop();
			unsigned int op1 = m_values.top();
			m_values.pop();

			m_values.push(op1 * op2);
		}

		void ReflectEnum::Div()
		{
			unsigned int op2 = m_values.top();
			m_values.pop();
			unsigned int op1 = m_values.top();
			m_values.pop();

			m_values.push(op1 / op2);
		}

		void ReflectEnum::Mod()
		{
			unsigned int op2 = m_values.top();
			m_values.pop();
			unsigned int op1 = m_values.top();
			m_values.pop();

			m_values.push(op1 % op2);
		}

		void ReflectEnum::Shl()
		{
			unsigned int op2 = m_values.top();
			m_values.pop();
			unsigned int op1 = m_values.top();
			m_values.pop();

			m_values.push(op1 << op2);
		}

		void ReflectEnum::Shr()
		{
			unsigned int op2 = m_values.top();
			m_values.pop();
			unsigned int op1 = m_values.top();
			m_values.pop();

			m_values.push(op1 >> op2);
		}

		void ReflectEnum::Or()
		{
			unsigned int op2 = m_values.top();
			m_values.pop();
			unsigned int op1 = m_values.top();
			m_values.pop();

			m_values.push(op1 | op2);
		}

		void ReflectEnum::And()
		{
			unsigned int op2 = m_values.top();
			m_values.pop();
			unsigned int op1 = m_values.top();
			m_values.pop();

			m_values.push(op1 & op2);
		}

		void ReflectEnum::PushNum(unsigned int v)
		{
			m_values.push(v);
		}

		void ReflectEnum::PushEnum(const char* start, const char* end)
		{
			std::string enumName(start, end - start);

			std::map<std::string, unsigned int>::iterator it = m_kv.find(enumName);
			if (it != m_kv.end())
			{
				m_values.push(it->second);
			}
			else
			{
				m_values.push(0);
			}
		}

		unsigned int ReflectEnum::Calc(const char* expr)
		{
			// 表达式计算的6个优先级:
			// O0:(),数字,枚举名
			// O0 ::= ('(' , O5 , ')') | (S0 , {S1}) | ([1-9] , {[0-9]}) | ("0x" , {[0-F]})
			// O1:乘,除,模
			// O1 ::= O0 , { ('*' , O0) | ('/' , O0) | ('%' , O0) }
			// O2:加,减
			// O2 ::= O1 , { ('+' , O1) | ('-' , O1) }
			// O3:左移,右移
			// O3 ::= O2 , { (">>" , O2) | ("<<" , O2) } 
			// O4:按位与
			// O4 ::= O3 , { '&' , O3 } 
			// O5:按位或
			// O5 ::= O4 , { '|' , O4 }
			rule<> O0, O1, O2, O3, O4, O5;

			// 有效的枚举名的字符构成
			// S0:首字符为 下划线,字母大/小写
			// S0 ::= ([_] | [a-z] | [A-Z])
			// S1:其他字符为 下划线,字母大/小写,数字
			// S1 ::= ([_] | [a-z] | [A-Z] | [0-9])
			rule<> S0, S1;

			O5 = (O4 >>
				*(
				(ch_p('|') >> O4)[bind(&ReflectEnum::Or, this)]
				));

			O4 = (O3 >>
				*(
				(ch_p('&') >> O3)[bind(&ReflectEnum::And, this)]
				));

			O3 = (O2 >>
				*(
				(str_p(">>") >> O2)[bind(&ReflectEnum::Shr, this)] |
				(str_p("<<") >> O2)[bind(&ReflectEnum::Shl, this)]
				));

			O2 = (O1 >>
				*(
				(ch_p('+') >> O1)[bind(&ReflectEnum::Add, this)] |
				(ch_p('-') >> O1)[bind(&ReflectEnum::Sub, this)]
				));

			O1 = (O0 >>
				*(
				(ch_p('*') >> O0)[bind(&ReflectEnum::Mul, this)] |
				(ch_p('/') >> O0)[bind(&ReflectEnum::Div, this)] |
				(ch_p('%') >> O0)[bind(&ReflectEnum::Mod, this)]
				));

			S1 = ch_p('_') | range_p('a', 'z') | range_p('A', 'Z') | range_p('0', '9');
			S0 = ch_p('_') | range_p('a', 'z') | range_p('A', 'Z');

			O0 = (ch_p('(') >> O5 >> ch_p(')')) |
				(
				((S0 >> *(S1))[bind(&ReflectEnum::PushEnum, this, _1, _2)]) |
				((str_p("0x") | str_p("0X")) >> hex_p[bind(&ReflectEnum::PushNum, this, _1)]) |
				(int_p[bind(&ReflectEnum::PushNum, this, _1)])
				);

			if (parse(expr, O5).full)
			{
				return m_values.top();
			}
			else
			{
				return 0xffffffff;
			}
		}

	}
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值