c++ so 反射_c++反射----现有解决方案

本文探讨了C++中实现行为树运行时遇到的反射问题,包括枚举与字符串映射、自定义类型序列化和反序列化、成员函数按名调用以及函数接口注释导出。通过宏、magic_enum库、idl工具(如protobuf、thrift、bond)以及函数接口注释导出工具,分析了现有解决方案及其优缺点,为C++反射问题提供了参考。
摘要由CSDN通过智能技术生成

c++反射----现有解决方案

根据我们上一篇中的介绍,拦在我们实现行为树运行时路上有如下几个问题:枚举与字符串之间的相互映射

自定义类型的序列化和反序列化

成员函数的按名调用

函数接口的注释导出

这几个问题,难度逐次递进,下面我们来依次来分析一下这几个问题的现有解决方案。

枚举类型的反射

手动进行字符串映射

枚举类型的反射需求不仅仅在行为树的运行时需要,行为树的编辑器和调试器也是需要的。下面就是我们现有的所有节点的类型枚举:

enum class node_type

{

invalid = 0,

root,

negative,

sequence,

always_seq,

random_seq,

if_else,

while_loop,

wait_event,

reset,

sub_tree,

parallel,

action,

always_true,

select,

probility

};

对应的编辑器里面插入节点的时候需要提供所有可选节点类型的列表:

在代码里是通过人工遍历枚举的字符串形式来处理的:

std::vector<:string> node_types;

node_types.push_back("negative");

node_types.push_back("sequence");

node_types.push_back("always_seq");

node_types.push_back("random_seq");

node_types.push_back("select");

node_types.push_back("probility");

node_types.push_back("if_else");

node_types.push_back("while_loop");

node_types.push_back("wait_event");

node_types.push_back("reset");

node_types.push_back("sub_tree");

node_types.push_back("parallel");

node_types.push_back("action");

node_types.push_back("always_true");

然后行为树运行时里面又有从节点类型的字符串形式构造对象的方法:

node_type str_to_node_type(const std::string& cur_str)

{

static unordered_map<:string node_type> str_to_types = {

{"root", node_type::root},

{"always_seq", node_type::always_seq},

{"if_else", node_type::if_else},

{"reset", node_type::reset},

{"while_loop", node_type::while_loop}

// and more }

auto cur_iter = str_to_types.find(cur_str);

if(cur_iter == str_to_types.end())

{

return node_type::invalid;

}

else

{

return cur_iter->second;

}

}

这样的转换代码不仅仅是这两个地方有,每次人工填写的时候都怕自己写错了映射。

使用宏来生成枚举的反射

说起自动化生成模式化代码,很多人的第一反应就是宏,而宏的确能够在一定程度上解决这样的枚举发射的问题。下面就是一个

的示例:

#define DECLARE_ENUM_WITH_TYPE(E, T, ...) enum class E : T

{

__VA_ARGS__

};

std::map E##MapName(generateEnumMap(#__VA_ARGS__));

std::ostream &operator<

{

os << E##MapName[static_cast(enumTmp)];

return os;

}

size_t operator*(E enumTmp) { (void) enumTmp; return E##MapName.size(); }

std::string operator~(E enumTmp) { return E##MapName[static_cast(enumTmp)]; }

std::string operator+(std::string &&str, E enumTmp) { return str + E##MapName[static_cast(enumTmp)]; }

std::string operator+(E enumTmp, std::string &&str) { return E##MapName[static_cast(enumTmp)] + str; }

std::string &operator+=(std::string &str, E enumTmp)

{

str += E##MapName[static_cast(enumTmp)];

return str;

}

E operator++(E &enumTmp)

{

auto iter = E##MapName.find(static_cast(enumTmp)); // some content deleted enumTmp = static_cast(iter->first);

return enumTmp;

}

bool valid##E(T value) { return (E##MapName.find(value) != E##MapName.end()); }

#define DECLARE_ENUM(E, ...) DECLARE_ENUM_WITH_TYPE(E, int32_t, __VA_ARGS__) template

std::map generateEnumMap(std::string strMap)

{

STRING_REMOVE_CHAR(strMap, ' ');

STRING_REMOVE_CHAR(strMap, '(');

std::vector<:string> enumTokens(splitString(strMap));

std::map retMap;

T inxMap;

inxMap = 0;

for (auto iter = enumTokens.begin(); iter != enumTokens.end(); ++iter)

{

// Token: [EnumName | EnumName=EnumValue] std::string enumName;

T enumValue;

if (iter->find('=') == std::string::npos)

{

enumName = *iter;

}

else

{

std::vector<:string> enumNameValue(splitString(*iter, '='));

enumName = enumNameValue[0];

//inxMap = static_cast(enumNameValue[1]); inxMap = static_cast(std::stoll(enumNameValue[1], 0, 0));

}

retMap[inxMap++] = enumName;

}

return retMap;

}

上面的代码里面就有两个特别巨大的宏,一眼望去好多字符串拼接,咱也不懂什么意思,咱也不敢问。反正用法是这样的:

DECLARE_ENUM_WITH_TYPE(TestEnumClass, int32_t, ZERO = 0x00, TWO = 0x02, ONE = 0x01, THREE = 0x03, FOUR);

int main(void) {

TestEnumClass first, second;

first = TestEnumClass::FOUR;

second = TestEnumClass::TWO;

std::cout << first << "(" << static_cast(first) << ")" << std::endl; // FOUR(4)

std::string strOne;

strOne = ~first;

std::cout << strOne << std::endl; // FOUR

std::string strTwo;

strTwo = ("Enum-" + second) + (TestEnumClass::THREE + "-test");

std::cout << strTwo << std::endl; // Enum-TWOTHREE-test

std::string strThree("TestEnumClass: ");

strThree += second;

std::cout << strThree << std::endl; // TestEnumClass: TWO std::cout << "Enum count=" << *first << std::endl;

}

用宏来实现枚举发射最大的问题是他把我们所期望的枚举类型转变成了非枚举类型,同时实现也很扭曲令人生畏。

利用magic_enum进行字符串映射

在进一步搜索中发现,github上有一个magic_enum(https://github.com/Neargye/magic_enum)的库可以非侵入的实现枚举的遍历

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值