跟我学c++高级篇——反射的基本实现方式

news/2025/2/14 6:22:33/

一、c++中的反射基本实现

综合反射在各种高级语言中的应用以及前面分析过的反射的原理,就可以明白,一种概念或者说技术在某种语言中是否拥有,更准确的其实是说是否原生拥有。毕竟,没有可以通过某种技术或者手段模拟出来,理论上说,只要达到最后的效果相同,就可以认为支持这项技术。反射亦是如此!
c++原生可以说基本是不支持反射的,但是通过一些技术手段可以实现,特别是随着c++标准的迭代,包括反射的一些技术增加到了标准内,这就意味着,c++原生支持反射的可能性会越来越大,但能支持到何种程度,就是另外一种问题了,做标准可不比开发程序,要严谨很多,不然也不会有不少的技术被标准委员会反复推延。

二、反射实现的方法

在c++中,基本的静态反射,可以通过宏和模板来实现,当然二者结合也可以实现。这里还是强调一下,单纯的为了反射的目的的而模拟实现反射效果的暂时不在此讨论之列。在宏和模板实现反射的机制下,可能会利用到萃取和特化。特别是前面提到的一些SFINA和元编程等中的技术(变参模板和变参宏等)。
正如前面分析提到,要想实现反射,首先知道类的信息,这些信息包括大小,名称,变量数量、地址,函数的地址等等。换句话说,需要通过宏和模板等技术只要能得到这些信息,就基本可以实现反射了。不过,在实现的过程中,如何更简单高效的完成工作和达到目的,这就需要八仙过海的本事了。

三、例程

上面提到了,要想实现反射,必须能得名称、属性(类内部变量定义)、地址等,那么就需要先处理最常见的,拿到名称和处理数量

1、得到名称
这个比较简单毕竟类的名称都是可以各种方法得到的,而且在反射的过程中一般也会给出类的名称。而且用宏来处理这种事情极其简单,字符串的替换即可。当然,c++中也为基础的类型提供了typeid可以得到名称。

2、得到数量
这个可以用模板和宏,下面是宏的方式:

//注意:VC编译器把宏可变参当成一个整体进行预处理展开,可能结果有问题,可启用/Zc:preprocessor编译选项,或引入ZH_EXPAND(...) VA_ARGS 展开,建议在Gcc下展开。
#define GET_NTH_ARG(  _1,  _2,  _3,  _4,  _5,  _6,  _7,  _8,  _9,  _10, _11, _12, _13, _14, _15, _16, n, ...) n
#define GET_ARG_COUNT(...) GET_NTH_ARG (__VA_ARGS__, 16, 15, 14, 13, 12, 11, 10, 9,  8,  7,  6,  5,  4,  3,  2,  1)
int main()
{GET_ARG_COUNT(a,b,3,d);
}

这个代码的参数可以自己按照需要进行扩展,写成1~N,当然N不可能太大,毕竟实际情况有十个参数的都是多的了。不过这个不支持0个的参数的。
下面是模板的方式:

#define TEM_GET_PARAM_SIZE_BY_CONSTEXPR (...) TempGetParmCons(__VA_ARGS__)
#define TEM_GET_PARAM_SIZE_BY_SIZEOF(...) (sizeof(TemGetParmBySize)(__VA_ARGS__))/sizeof(char) -1
#define TEM_GET_TYPE_SIZE(...) TemGetTypeSize<__VA_ARGS__>::valuetemplate<typename ...T>
constexpr size_t TempGetParmCons(T ...params)
{return sizeof ... (T);
}
template<typename ...T>
auto TemGetParmBySize(T&& ...param)->char(&)[sizeof ...(T) + 1];template<typename ...T>
struct TemGetTypeSize
{static constexpr size_t value = sizeof ... (T);
};

这个技术就更简单了,只是简单的使用了sizeof…的各种情况。

3、得到类型和地址
通过宏还可以得到很多的东西,比如利用#,##进行字符串的处理和连接。还可以以去除、增加小括号(也可以同时处理括号内容),这样就可以在宏反射过程中得到具体的类型和变量以及它们的名称。

#define TYPE_NAME_ALLDEL(...)
#define TYPE_NAME_DELP(...) __VA_ARGS__
#define TYPE_NAME_GET(n) TYPE_NAME_DELP n#define CLASSMEM_OFFSET_TYPE(m,...)(reinterpret_cast<size_t>(&reinterpret_cast<char const volatile&>((((__VA_ARGS__*)0)->m))))

4、模板处理表达式展开和元数据处理
在前面的分析提到过,可以用折叠表达式来完成自动的展开,而这种展开就可以通过模板来实现对反射过程中的序列化:

template<typename T>
struct Serializer
{using mSequence = std::make_index_sequence<T::mSize>;template<size_t ...M>static void WriteDataStream(std::index_sequence<M...>, const T& cv){(T::template SeqMeta<M>::WriteData(cv), ...);}static void WriteData(const  T& cv){WriteDataStream(mSequence(),cv);}
};

当然,萃取和SFINAE等技术都可以用到其中,就看对开发的需求了。另外一些新的c++标准中的技术也可以应用,比如Concepts等都可以很好解决一些问题。当然,typeid,sizeof…,decltype等就更不用提了。
5、模板技术和元编程
元编程就不用说了,模板技术也可以广泛的应用到反射中来,比如模板的偏特化等等,这些在前面都有不少的例子,这是就不再展开了。

本来想搞一个简单的应用做个例子,发现还真是有点难度(几十行代码真不好写出来)。还是后面再专门写一篇相关的文章为好。反射确实是比较不好写的容易懂,毕竟当初光宏编程就惹得一众声讨,更别说再加模板和元编程,这个得慢慢来。另外,反射本身在新的c++标准中可能会不断的完善,分析讲解的一些技术可能过几年就不会在反射中应用了,这个还是需要看标准的发展和应用的普及程度。

四、总结

太极生两仪,两仪有四象,四象生八卦。正如道家所说一生二,二生三,三生万物。学习也要从简单抓起,然后反复不停的在此基础上锤练,不断的向上发展,让整个体系愈发的完整并且向着新的未知不断的探索。对个人来说,技术的精湛莫不如是。对技术本身来说,更是如此。
要站在巨人的肩膀上,不断的向前看,看向更高。


http://www.ppmy.cn/news/78872.html

相关文章

IEEE模板使用注意事项

关于单栏双栏 注意区分用的模板是 {IEEEtran} 还是期刊自己的如 {IEEEtaes} 如果是后者&#xff0c;可能不支持通过直接在文档开头加一行代码直接转单栏。 关于公式 When referring to an equation or formula, use simply “(1),” not “Eq. (1)” or “equation (1),” e…

hadoop3.2.4集成flink 1.17.0

前言 flink安装部署有三种方式 local:单机模式&#xff0c;尽量不使用 standalone: flink自带集群&#xff0c;资源管理由flink集群管理&#xff0c;开发环境测试使用&#xff0c;不需要hadoop集群 flink on yarn: 把资源管理交给yarn实现&#xff0c;计算机资源统一由Haoop…

MQTT中间件Eclipse Mosquitto安装和使用(.asc文件)MQTT监控命令mosquitto_sub(mosquitto C++库源码编译)

昨天弄的&#xff0c;今天忘了不少。。。 文章目录 参考链接安装MQTT服务中间件安装启动与查询卸载与清理 MQTT C支持库安装&#xff08;使C能使用相关库函数&#xff09;离线安装&#xff08;通过源码&#xff09;ubuntu官网下载软件包编译mosquitto客户端库 mosquitto Docker…

GameFramework框架

官网 Game Framework | 基于 Unity 引擎的游戏框架 介绍 对常用模块封装&#xff0c;规范开发过程&#xff0c;保证产品质量&#xff0c;内置19个模块。 组成 框架主要分两部分&#xff1a;GameFramework(简称GF)&#xff0c;UnityGameFramework&#xff08;简称UGF&#xff09…

奋斗,然后成功:我的架构狮之梦

与代码结缘 2018年&#xff0c;当时听说了一个很厉害的人——吴瀚清老师&#xff0c;也就是大家所熟知的“道哥”。关于他的事情有很多传说&#xff0c;于是我也很快成为了他的小迷弟&#xff0c;把吴瀚清老师当成了自己的偶像。 也是那一年&#xff0c;我买了人生中第一本关…

1720_Linux学习中的问题处理

全部学习汇总&#xff1a;GreyZhang/little_bits_of_linux: My notes on the trip of learning linux. (github.com) 这个有点学习的方法论的意思&#xff0c;画个滋味导图顺便整理一下。 遇到问题的时候&#xff0c;解决的方法大致有3中&#xff0c;而针对学习的建议有一部分是…

JS中神奇的String.indexOf()

在JavaScript中&#xff0c;String.indexOf(searchvalue) 方法可返回某个指定的字符串值在字符串中首次出现的位置。这是个常见的方法&#xff0c;但却有着不常见的用法。 常用的用法&#xff1a; this is jimson.indexOf(this); //return 0; this is jimson.indexOf(is); …

经典JavaScript手写面试题和答案

文章目录 实现一个函数去重&#xff1f;实现一个函数&#xff0c;判断指定元素在数组中是否存在&#xff1f;实现一个函数&#xff0c;将给定字符串反转&#xff1f;实现一个函数&#xff0c;检测指定字符串是否为回文&#xff08;即从前往后和从后往前的字符序列都相同&#x…