当前位置: 首页 > news >正文

2408C++,C++20的无侵入式反射

原文

C++17基于结构绑定的编译期反射

事实上不需要宏的编译期反射C++17中已用得很多了,比如struct_pack编译期反射就不需要宏,因为C++17结构绑定可直接得到一个聚集类的成员的引用.

struct person {int id;std::string name;int age;
};
int main() {person p{1, "tom", 20};auto &[id, name, age] = p;std::cout << name << "\n";
}

没有宏也没有侵入式,一切都很完美,但是有两个比较大的问题:

问题一
结构绑定方式无法取字段名,这是一个主要问题,如果想序化对象到json,xml时,需要字段名时就无法满足需求了.
问题二
除此外还有另外一个问题,如果一个对象有构造器私有成员时,则它就不是一个聚集类型了,无法再用结构绑定反射内部的字段了.

基于结构绑定的编译期反射无法解决这两个主要问题,导致用途不大.

yalantinglibs.reflection反射库

yalantinglibs.reflection反射库直面并解决了这两个问题,提供了统一的编译期反射方法,无论对象是否是聚集类型,无论对象是否有私有字段都可用统一的api编译期反射得到其元信息.

来看看yalantinglibs.reflection如何反射一个聚集类型的:

struct simple {int color;int id;std::string str;int age;
};
using namespace ylt::reflection;
int main() {simple p{.color = 2, .id = 10, .str = "hello reflection", .age = 6};//取对象字段个数static_assert(members_count_v<simple> == 4);//取对象所有字段名 constexpr auto arr = member_names<simple>; //std::array<std::string_view, N> //根据字段索引取字段值 CHECK(std::get<3> == 6); //get age CHECK(std::get<2> == "hello reflection"); //get str //根据字段名取字段值auto& age2 = get<"age"_ylts>(p);CHECK(age2 == 6);//遍历对象,得到字段值和字段名for_each(p, [](auto& field_value, auto field_name) {std::cout << field_value << ", " << field_name << "\n";});
}

yalantinglibs的编译期反射相比前结构绑定方式的反射更进一步了,不仅是无宏非侵入式,还能得到字段名,这样就把第一个问题解决掉了,可用在数格xml等需要字段名的场景下了.

yalantinglibs.reflection是如何完成非侵入式取得聚集对象的字段名的呢?因为reflectcpp这道光!

该库的作者发现了一个新方法可在C++20高版本的编译器中非侵入式的取得聚集对象的字段名.能发现该方法我只能说你真是个天才!

该方法说起来也不算复杂,分两步实现:
第一步:在编译期取得对象字段值的指针;
第二步:在编译期第一步得到的指针解析出字段名;
是不是很简单,接着看看具体是如何实现的吧.

在编译期取得对象字段值的指针

reflectcpp在实现这一步时做得比较复杂,yalantinglibs大幅简化了这一步的实现.

template <class T, std::size_t n>
struct object_tuple_view_helper {static constexpr auto tuple_view(){}
};
template <class T>
struct object_tuple_view_helper<T, 0> {static constexpr auto tuple_view() { return std::tie(); }
};
template <class T>
struct object_tuple_view_helper<T, 4> {static constexpr auto tuple_view() {auto& [a, b, c, d] = get_fake_object<remove_cvref_t<T>>();auto ref_tup = std::tie(a, b, c, d);auto get_ptrs = [](auto&... _refs) {return std::make_tuple(&_refs...);};return std::apply(get_ptrs, ref_tup);}
};

偏特化模板类object_tuple_view_helper,在tuple_view函数中,先结构绑定得到字段值的引用,然后按指针转换它,并放到元组中,来返回给用户.

这里偏特化的关键在于n,它表示聚集对象字段的个数,该字段个数是可在编译期取的.为了避免针对不同字段个数聚集类型写重复的偏特化的代码,可用脚本生成这些代码.

#define RFL_INTERNAL_OBJECT_IF_YOU_SEE_AN_ERROR_REFER_TO_DOCUMENTATION_ON_C_ARRAYS( \n, ...)                                                                         \template <class T>                                                                \struct object_tuple_view_helper<T, n> {                                           \static constexpr auto tuple_view() {                                            \auto& [__VA_ARGS__] = get_fake_object<remove_cvref_t<T>>();                   \auto ref_tup = std::tie(__VA_ARGS__);                                         \auto get_ptrs = [](auto&... _refs) {                                          \return std::make_tuple(&_refs...);                                          \};                                                                            \return std::apply(get_ptrs, ref_tup);                                         \}                                                                               \}
/*The following boilerplate code was generated using a Python script:
macro =
"RFL_INTERNAL_OBJECT_IF_YOU_SEE_AN_ERROR_REFER_TO_DOCUMENTATION_ON_C_ARRAYS"
with open("generated_code4.cpp", "w", encoding="utf-8") as codefile:codefile.write("\n".join([f"{macro}({i}, {', '.join([f'f{j}' for j in range(i)])});"for i in range(1, 256)]))
*/
RFL_INTERNAL_OBJECT_IF_YOU_SEE_AN_ERROR_REFER_TO_DOCUMENTATION_ON_C_ARRAYS(1, f0);
RFL_INTERNAL_OBJECT_IF_YOU_SEE_AN_ERROR_REFER_TO_DOCUMENTATION_ON_C_ARRAYS(2, f0, f1);
RFL_INTERNAL_OBJECT_IF_YOU_SEE_AN_ERROR_REFER_TO_DOCUMENTATION_ON_C_ARRAYS(3, f0, f1, f2);
RFL_INTERNAL_OBJECT_IF_YOU_SEE_AN_ERROR_REFER_TO_DOCUMENTATION_ON_C_ARRAYS( 4, f0, f1, f2, f3);
RFL_INTERNAL_OBJECT_IF_YOU_SEE_AN_ERROR_REFER_TO_DOCUMENTATION_ON_C_ARRAYS( 5, f0, f1, f2, f3, f4);
RFL_INTERNAL_OBJECT_IF_YOU_SEE_AN_ERROR_REFER_TO_DOCUMENTATION_ON_C_ARRAYS( 6, f0, f1, f2, f3, f4, f5);
...

生成偏特化代码后,就可简单取得聚集对象字段的指针.

template <class T>
inline constexpr auto struct_to_tuple() {return object_tuple_view_helper<T, members_count_v<T>>::tuple_view();
}

调用struct_to_tuple就能在编译期取得T所有字段的指针了,其中编译期取T的字段个数的方法members_count_v就是来自于struct_pack中的方法,前面在介绍struct_pack的文章里已详细讲过,就不再赘述了.

根据字段指针取字段名

有了编译期得到的字段指针后就很容易取其字段名了:

template <auto ptr>
inline constexpr std::string_view get_member_name() {
#if defined(_MSC_VER)constexpr std::string_view func_name = __FUNCSIG__;
#elseconstexpr std::string_view func_name = __PRETTY_FUNCTION__;
#endif
#if defined(__clang__)auto split = func_name.substr(0, func_name.size() - 2);return split.substr(split.find_last_of(":.") + 1);
#elif defined(__GNUC__)auto split = func_name.substr(0, func_name.rfind(")}"));return split.substr(split.find_last_of(":") + 1);
#elif defined(_MSC_VER)auto split = func_name.substr(0, func_name.rfind("}>"));return split.substr(split.rfind("->") + 2);
#elsestatic_assert(false,"You are using an unsupported compiler. Please use GCC, Clang ""or MSVC or switch to the rfl::Fieldsyntax.");
#endif
}

template<auto ptr>C++17的特性,可用来声明一个非类型模板参数,来避免写具体类型.

有了该编译期的后,剩下的就是根据编译产生的符号去截取需要的部分串了,注意每个平台生成的符号有差异,需要宏来区分各个平台的截取方式.

为什么用指针可以取字段名?C++17或以下的编译器是不是也可这样来取呢?
第一个问题的答案是:reflectcpp作者发现的该方法,很黑客,但是工作!
第二个问题的答案是:不可以,该方法只在支持C++20gcc11,clang13,msvc2022以上编译器中才有效!

所以该非侵入式取字段名的方法也是有约束的,不适合低版本的编译器.

完整的取字段列表的实现:

template <class T>
struct Wrapper {using Type = T;T v;
};
template <class T>
Wrapper(T) -> Wrapper<T>;
//针对clang.
template <class T>
inline constexpr auto wrap(const T& arg) noexcept {return Wrapper{arg};
}
template <typename T>
inline constexpr std::array<std::string_view, members_count_v<T>>
get_member_names() {constexpr auto tp = struct_to_tuple<T>();std::array<std::string_view, Count> arr;[&]<size_t... Is>(std::index_sequence<Is...>) mutable {((arr[Is] = get_member_name<wrap(std::get<Is>(tp))>()), ...);}(std::make_index_sequence<Count>{});return arr;
}

至此,两步完成,可用get_member_names函数非侵入式的取得聚集对象的字段名列表了.

如何处理非聚集类型?

在高版本的编译器中无宏非侵入式得到聚集对象字段名列表固然很好,但是非聚集类型要如何处理呢?如果编译器版本不够,只有C++17又该怎么办?

yalantinglibs.reflection未来远不止你想象的,想能统一整个编译期反射的内容,无论对象是聚集还是非聚集,无论对象是否含有私有字段都提供统一的反射接口!

比如像这样一个对象:

class private_struct {int a;int b;public:private_struct(int x, int y) : a(x), b(y) {}
};private_struct st(2, 4);ylt::reflection::refl_visit_members(st, [](auto&... args) {((std::cout << args << " "), ...);std::cout << "\n";});

private_struct是一个含私有字段非聚集类型,但是ylt::reflection也能反射它.但是这里还漏掉了一个宏,是,还是需要宏,在编译期反射进入到标准库前,对非聚集类型仍需要的.

class private_struct {int a;int b;public:private_struct(int x, int y) : a(x), b(y) {}
};
YLT_REFL_PRIVATE(private_struct, a, b);

对没有字段的非聚集类型来说,也适合C++17,可这样定义宏:

struct dummy_t {int id;std::string name;int age;YLT_REFL(dummy_t, id, name, age);
};
struct dummy_t2 {int id;std::string name;int age;
};
YLT_REFL(dummy_t2, id, name, age);

总结

高版本的编译器中可完全不使用宏,低版本或非聚集类型宏来实现编译器反射,无论是聚集还是非聚集都是使用同一套反射接口,这样可覆盖所有要用编译期反射的场景,这就是yalantinglibs.reflection提供的能力!

后面struct_pack,struct_pb,struct_json,struct_xml,struct_yaml都会使用yalantinglibs.reflection提供的统一的编译期反射接口来实现序化和反序化.

相关文章:

2408C++,C++20的无侵入式反射

原文 C17基于结构绑定的编译期反射 事实上不需要宏的编译期反射在C17中已用得很多了,比如struct_pack的编译期反射就不需要宏,因为C17结构绑定可直接得到一个聚集类的成员的引用. struct person {int id;std::string name;int age; }; int main() {person p{1, "tom&qu…...

抽象工厂模式(Abstract factory pattern)- python实现

抽象工厂模式的通俗示例 想象一下&#xff0c;你正在经营一家家具店&#xff0c;你需要从不同的供应商那里采购不同的家具系列。有的供应商提供的是现代风格家具&#xff0c;包括现代沙发、现代椅子和现代桌子&#xff1b;而有的供应商提供的是古典风格家具&#xff0c;包括古…...

adb Connection reset by peer的解决方法

本文同步发于&#xff1a;https://www.cnblogs.com/yeshen-org/p/18350232 最近在编译一个老项目&#xff0c;项目中依赖了很多第三方库&#xff0c;用gradle编译要20-30分钟&#xff0c;而且内存开销很大。 公司配的15G内存的电脑&#xff0c;一次编译能用到14G。 编译的时候&…...

111111111

1111111111111111111...

搜维尔科技:Varjo XR-4使用UE5 打造最具沉浸感的混合现实环境

Varjo XR-4使用UE5打造最具沉浸感的混合现实环境 搜维尔科技&#xff1a;Varjo XR-4使用UE5 打造最具沉浸感的混合现实环境...

从分散到集中:TSINGSEE青犀EasyCVR视频汇聚网关在视频整体监控解决方案中的整合作用

边缘计算视频汇聚网关是基于开放式、大融合、全兼容、标准化的设计架构理念&#xff0c;依据《安全防范视频监控联网系统信息传输、交换、控制技术要求》&#xff08;GB/T28181-2011&#xff09;标准开发&#xff0c;集流媒体转发、视频编码、视频管理、标准通信协议、网络穿透…...

React学习-jsx语法

jsx语法&#xff0c;浏览器不认识&#xff0c;需要经过babel编译 https://babeljs.io/ 面试题&#xff1a;jsx的作用&#xff1f; 普通回答&#xff1a;可以在js中返回dom&#xff0c;经过babel编译成js认识的代码import { jsx as _jsx, jsxs as _jsxs } from "react/j…...

uniapp多图上传uni.chooseImage上传照片uni.uploadFile

uniapp多图上传uni.chooseImage上传照片uni.uploadFile 代码示例&#xff1a; /**上传照片 多图*/getImage() {uni.chooseImage({count: 9, //默认9sizeType: [original, compressed], //可以指定是原图还是压缩图&#xff0c;默认二者都有sourceType: [album], //从相册选择/…...

鸿蒙(API 12 Beta2版)媒体开发【处理音频焦点事件】

音频打断策略 多音频并发&#xff0c;即多个音频流同时播放。此场景下&#xff0c;如果系统不加管控&#xff0c;会造成多个音频流混音播放&#xff0c;容易让用户感到嘈杂&#xff0c;造成不好的用户体验。为了解决这个问题&#xff0c;系统预设了音频打断策略&#xff0c;对…...

c语言第12天

指针的引入 为函数修改实参提供支持。 为动态内存管理提供支持。 为动态数据结构提供支持。 为内存访问提供另一种途径。 指针概述 内存地址&#xff1a;系统为了内存管理的方便&#xff0c;将内存划分为一个个的内存单元&#xff08;1个内存单元占1个字 节&#xff09;&…...

回归预测|一种多输入多输出的粒子群优化支持向量机数据回归预测Matlab程序PSO-MSVR非for循环实现 原理上进行修改多输出

回归预测|一种多输入多输出的粒子群优化支持向量机数据回归预测Matlab程序PSO-MSVR非for循环实现 原理上进行修改多输出 文章目录 前言回归预测|一种多输入多输出的粒子群优化支持向量机数据回归预测Matlab程序PSO-MSVR非for循环实现 原理上进行修改多输出 一、PSO-MSVR模型1. …...

《花100块做个摸鱼小网站! 》第二篇—后端应用搭建和完成第一个爬虫

一、前言 大家好呀&#xff0c;我是summo&#xff0c;前面已经教会大家怎么去阿里云买服务器&#xff08;链接在这&#xff0c;需要自取&#xff1a;https://developer.aliyun.com/huodong/dashiblogger?userCodemtbtcjr1&#xff09;&#xff0c;以及怎么搭建JDK、Redis、My…...

Mapreduce_csv_averageCSV文件计算平均值

csv文件求某个平均数据 查询每个部门的平均工资&#xff0c;最后输出 数据处理过程 employee_noheader.csv&#xff08;没做关于首行的处理&#xff0c;运行时请自行删除&#xff09; EmployeeID,EmployeeName,DepartmentID,Salary 1,ZhangSan,101,5000 2,LiSi,102,6000…...

将UEC++项目转码成UTF-8

方法一 如果文件不多的话&#xff0c;可以手动一个一个进行修改。添加 “高级保存选项” 手动改为UTF-8 方法二 使用editorconfig文件&#xff0c;统一编码问题。通过&#xff1a;“工具” > “选项”>"文本编辑器" > "C/C" > "代码样式…...

深入探索MySQL C API:使用C语言操作MySQL数据库

目录 引言 一. MySQL C API简介 二. MySQL C API核心函数 2.1 初始化和连接 2.2 配置和执行 2.3 处理结果 2.4 清理和关闭 2.5 错误处理 三. MySQL使用过程 四. 实现CRUD操作 4.1 创建数据库并建立表 ​编辑 4.2 添加数据&#xff08;Create&#xff09; ​编辑 …...

武汉流星汇聚:亚马逊助力跨境电商扬帆起航,海外影响力显著提升

在全球化浪潮的推动下&#xff0c;跨境电商已成为连接世界市场的重要桥梁。而在这场跨越国界的商业盛宴中&#xff0c;亚马逊作为全球电商的领军者&#xff0c;以其独特的商业模式、庞大的用户基础&#xff0c;为无数企业提供了前所未有的发展机遇。武汉流星汇聚电子商务有限公…...

C语言:设计模式

C语言和设计模式&#xff08;总结篇&#xff09; 书籍&#xff1a;《大话设计模式》 2、C语言和设计模式&#xff1a;原型模式&#xff08;复制自己&#xff0c;生成另外一个实例对象&#xff09; 17、C语言实现面向对象编程 : 封装、继承、多态 ---- C语言可&#xff1a;封…...

Pandas数据选择的艺术:深入理解loc和iloc

在数据科学领域&#xff0c;Pandas是处理和分析数据的瑞士军刀。掌握Pandas中的数据选择技巧&#xff0c;尤其是loc和iloc的使用&#xff0c;对于提高数据处理效率至关重要。本文将深入探讨loc和iloc的用法&#xff0c;通过丰富的示例&#xff0c;帮助你精确地选取所需的数据&a…...

<数据集>固定视角监控牧场绵羊识别数据集<目标检测>

数据集格式&#xff1a;VOCYOLO格式 图片数量&#xff1a;3615张 标注数量(xml文件个数)&#xff1a;3615 标注数量(txt文件个数)&#xff1a;3615 标注类别数&#xff1a;1 标注类别名称&#xff1a;[Sheep] 序号类别名称图片数框数1Sheep361529632 使用标注工具&#…...

浙大数据结构慕课课后题(06-图2 Saving James Bond - Easy Version)(拯救007)

题目要求&#xff1a; This time let us consider the situation in the movie "Live and Let Die" in which James Bond, the worlds most famous spy, was captured by a group of drug dealers. He was sent to a small piece of land at the center of a lake fi…...

Unity安卓构建72小时实战指南:从零到真机运行

1. 这不是“又一本Unity教程”&#xff0c;而是我带三个新人从零上线第一款安卓游戏的真实路径你点开这个标题&#xff0c;大概率正站在两个路口之间&#xff1a;一边是满屏“30天速成Unity”“零基础做爆款”的短视频封面&#xff0c;一边是你刚下载完Unity Hub、卡在Android …...

从电磁炉到户外电源:拆解单相SVPWM如何让你的逆变器更安静、更高效

从电磁炉到户外电源&#xff1a;单相SVPWM如何实现静音与高效的双重突破当你深夜用电磁炉煮面时&#xff0c;是否曾被突然的蜂鸣声吓一跳&#xff1f;或是发现户外电源给设备充电时&#xff0c;散热风扇的噪音盖过了山林鸟鸣&#xff1f;这些常见问题背后&#xff0c;隐藏着一个…...

《我看见的世界:李飞飞自传》第1-6章阅读笔记:从移民少女到AI教母的“看见“之旅

前言 当我们谈论人工智能时&#xff0c;我们谈论的是算法、数据、算力&#xff0c;是那些冰冷的代码和复杂的模型。但在《我看见的世界&#xff1a;李飞飞自传》中&#xff0c;李飞飞用她独特的视角告诉我们&#xff1a;AI的本质&#xff0c;是人类对"看见"世界的渴望…...

AI写的论文双率如何压到20%以下?这几款工具实测有效

毕业季、投稿季用AI写论文已经成为不少人的高效选择&#xff0c;但查重率飘红、AIGC疑似率超标两大问题&#xff0c;让很多人犯了难。2026年学术检测标准持续收紧&#xff0c;知网、维普及主流AIGC检测系统同步上线双检规则&#xff0c;两项指标均控制在20%以下才符合基本提交要…...

告别硬编码!在UE5.1里用蓝图动态配置MySQL连接参数(控件蓝图实战)

动态配置MySQL连接&#xff1a;UE5.1控件蓝图的工程化实践在游戏开发中&#xff0c;数据库连接往往是项目架构中不可或缺的一环。传统硬编码方式虽然简单直接&#xff0c;却带来了维护困难、安全性差、灵活性低等一系列问题。本文将深入探讨如何在UE5.1中构建一个完全动态化的M…...

【python】ImportError: DLL load failed while importing QtWidgets: 找不到指定的程序。重新安装后搞定

文章目录前言一、PyQt6引用后报错二、使用步骤总结前言 想做个好看的界面&#xff0c;引用了PyQt6&#xff0c;却产生了新问题。 pip install pyqt6-tools&#xff0c;优先做这个动作进行修复。 一、PyQt6引用后报错 python里引用&#xff1a; from PyQt6.QtWidgets import…...

如何用WaveTools终极优化《鸣潮》游戏性能:从卡顿到丝滑的完整指南

如何用WaveTools终极优化《鸣潮》游戏性能&#xff1a;从卡顿到丝滑的完整指南 【免费下载链接】WaveTools &#x1f9f0;鸣潮工具箱 项目地址: https://gitcode.com/gh_mirrors/wa/WaveTools 如果你正在玩《鸣潮》却频繁遭遇帧率波动、画面卡顿或操作延迟&#xff0c;那…...

从模糊到电影级景深:Midjourney + Topaz Gigapixel联调方案(含LUT预设包+PSD分层模板)

更多请点击&#xff1a; https://codechina.net 第一章&#xff1a;从模糊到电影级景深&#xff1a;Midjourney Topaz Gigapixel联调方案&#xff08;含LUT预设包PSD分层模板&#xff09; 当Midjourney生成的图像存在主体边缘柔化、背景层次缺失或分辨率不足等问题时&#xf…...

Redis 客户端连接详解

Redis 客户端连接详解 引言 Redis 是一款高性能的内存数据结构存储系统,常用于缓存、会话管理、实时排行榜等功能。客户端连接是 Redis 生态系统中的重要组成部分,本文将详细介绍 Redis 客户端连接的相关知识,包括连接方式、连接配置、连接管理等方面。 Redis 客户端连接…...

告别繁琐审核!实测AI Agent如何重塑复杂非结构化票据与合同处理流程?

摘要&#xff1a;在企业数字化转型步入深水区的2026年&#xff0c;处理复杂非结构化票据与合同已成为横亘在财务、法务部门面前的“最后一公里”难题。传统RPA因UI变动易崩溃、主流智能体因缺乏API适配而无法落地&#xff0c;导致大量业务仍依赖低效的人工操作。本文由「企服AI…...