如何在实际开发中深入使用 yalantinglibs 编译期反射库

news2024/9/30 9:12:51

yaLanTingLibs 是阿里云开源的现代 C++ 基础工具库的集合,包括序列化、http、rpc、协程、编译期反射、metric 和日志等库,可以帮助 C++ 开发者快速构建高性能的 C++ 应用。2024 云栖大会操作系统技术 Workshop 上,阿里云智能集团高级技术专家、purecpp社区负责人、《深入应用C++11》作者祁宇分享了《深入应用C++20编译期反射》,本文将重点介绍 yaLanTingLibs 编译期反射库,通过一些案例介绍其应用场景、功能和特色,以及如何将它用于实际开发。

本文的例子推荐安装龙蜥操作系统的 Alibaba Cloud Compiler 编译器编译,安装方法见链接:安装并使用Alibaba Cloud Compiler构建高性能的C++应用_Alibaba Cloud Linux(Alinux)-阿里云帮助中心

(图/阿里云智能集团高级技术专家、purecpp社区负责人、《深入应用C++11》作者祁宇)

编译期反射一个对象

首先,从一个简单的例子开始:

#include "ylt/reflection/member_value.hpp"

struct simple {
  int color;
  int id;
  std::string str;
  int age;
};

int main() {
  using namespace ylt::reflection;

  // 编译期获取simple 字段个数
  static_assert(member_count_v<simple> == 4);

  // 编译期获取simple 类型的名称
  static_assert(get_struct_name<simple>() == "simple");

  // 编译期获取simple 字段名称列表
  static_assert(get_member_names<simple>() == std::array<std::string_view, 4>{"color", "id", "str", "age"});

  // 根据类型遍历其字段名和索引
  for_each<simple>([](std::string_view field_name) {
    std::cout << field_name << "\n";
  }); 

  for_each<simple>([](std::string_view field_name, size_t index) {
    std::cout << index << ", " << field_name << "\n";
  });
}

除了获取这些最基本的对象元数据的 API 之外,ylt::reflection 还提供了更多有用的 API:

int main() {
  simple p{.color = 2, .id = 10, .str = "hello reflection", .age = 6};

  // 通过编译期索引获取字段值
  CHECK(std::get<0>(p) == 2);
  CHECK(std::get<2>(p) == "hello reflection");

  // 根据编译期索引获取字段名
  static_assert(name_of<simple, 1>()== "id");

  // 根据编译期字段名获取字段值
  CHECK(get<"age"_ylts>(p) == 6);
  CHECK(get<"str"_ylts>(p) == "hello reflection");

  // 根据编译期字段名获取字段索引
  static_assert(index_of<simple2, "str"_ylts>() == 2);

  // 遍历对象的字段、字段名、字段索引, 并打印
  for_each(p, [](auto& field, auto name, auto index) {
    std::cout << field << ", " << name << ", " << index << "\n";
  });

  // 访问对象的所有字段值
  visit_members(p, [](auto&&... args) {
    ((std::cout << args << " "), ...);
    std::cout << "\n";
  });  
}

有了这些编译期反射的 API 之后就可以做一些有趣的事了,比如做序列化,理论上可以通过编译期反射将一个对象序列化到任意格式。事实上 yalantinglibs 也是这样做的,通过统一的编译期反射 API 将对象转换为 json,xml,yaml,protobuf 等格式。

基于编译期反射的序列化

基于 yalantinglibs 反射库可以非侵入式的将一个对象序列化到多种数据格式,也可以扩展支持自定义的格式。

#include "ylt/struct_json/json_reader.h"
#include "ylt/struct_json/json_writer.h"
#include "ylt/struct_xml/xml_reader.h"
#include "ylt/struct_xml/xml_writer.h"
#include "ylt/struct_yaml/yaml_reader.h"
#include "ylt/struct_yaml/yaml_writer.h"
#include "ylt/struct_pb.hpp"

struct simple {
  int color;
  int id;
  std::string str;
  int age;
};

int main() {
  simple p{.color = 2, .id = 10, .str = "hello reflection", .age = 6};

  std::string json;
  struct_json::to_json(p, json);

  std::string xml;
  struct_xml::to_xml(p, xml);

  std::string yaml;
  struct_yaml::to_yaml(p, yaml);

  std::string protobuf;
  struct_pb::to_pb(p, protobuf);

  simple p1;
  struct_json::from_json(p1, json);
  struct_xml::from_xml(p1, xml);
  struct_yaml::from_yaml(p1, xml);
  struct_pb::from_pb(p1, xml);
}

使用yalantinglibs reflection库实现自定义格式的序列化

使用 yalantinglibs reflection 库可以很方便的将对象序列化到自定义格式,比如将一个对象序列化成一个简单的 json5 格式,json5 的 key 是没有引号的。

只需要通过反射 API for_each 拿到对象的字段值和字段名就够了。

struct point {
  int x;
  int y;
};

void test_json5() {
  point pt{2, 4};
  std::string json5;
  json5.append("{");
  ylt::reflection::for_each(pt, [&](auto& field, auto name) {
    json5.append(name).append(":").append(std::to_string(field)).append(",");
  });
  json5.back() = '}';
  CHECK(json5 == "{x:2,y:4}");
}

低版本编译器的支持

上述中的 example 只能在 C++20 高版本编译器(clang13+, gcc11+, msvc2022)中才能编译运行。如果编译器版本较低只支持 C++17 能用 yalantinglibs 的反射库吗?

答案也是肯定的,yalantinglibs 的反射库兼容了 C++17,支持的最低版本是 gcc9,不过需要宏定义一个额外的宏才能使用反射 API(C++20 高版本编译器中是不需宏的),比如之前的例子:

struct simple {
  int color;
  int id;
  std::string str;
  int age;
};
YLT_REFL(simple, color, id, str, age);

int main() {
  using namespace ylt::reflection;

  // 编译期获取simple 字段个数
  static_assert(member_count_v<simple> == 4);

  // 编译期获取simple 类型的名称
  static_assert(get_struct_name<simple>() == "simple");

  // 编译期获取simple 字段名称列表
  static_assert(get_member_names<simple>() == std::array<std::string_view, 4>{"color", "id", "str", "age"});

  // 根据类型遍历其字段名和索引
  for_each<simple>([](std::string_view field_name, size_t index) {
    std::cout << index << ", " << field_name << "\n";
  });
}

定义了 YLT_REFL 宏之后就可以使用反射的 API 了,如果对象的字段都是私有的,则需要将宏定义到对象内部:

struct simple {
YLT_REFL(simple, color, id, str, age);
private:
  int color;
  int id;
  std::string str;
  int age;
};

这种方式可以反射私有字段但有侵入性,如果对象有 public 的访问私有字段的方法也可做到非侵入式:

struct dummy_t5 {
 private:
  int id = 42;
  std::string name = "tom";
  int age = 20;

 public:
  int& get_id() { return id; }
  std::string& get_name() { return name; }
  int& get_age() { return age; }

  const int& get_id() const { return id; }
  const std::string& get_name() const { return name; }
  const int& get_age() const { return age; }
};
YLT_REFL(dummy_t5, get_id(), get_name(), get_age());

如果对象字段都是私有的,并且没有提供 public 的访问方法还能反射吗?也是可以的,可以通过 yalantinglibs reflection 另外一个宏 YLT_REFL_PRIVATE 非侵入式的反射对象的私有字段:

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);

现在就可以反射对象的私有字段了:

  private_struct st(2, 4);

  for_each(st, [](auto& field, auto name, auto index){
    std::cout << field << ", " << name << ", " << index << "\n";
  });

  visit_members(st, [](auto&... args) {
    ((std::cout << args << " "), ...);
    std::cout << "\n";
  });  

总结

编译期反射的应用场景很广泛,非常适合用在序列化场景、ORM(实体-映射)场景、非侵入式访问对象等场景,yalantinglibs 的反射库在支持 C++20 的同时也兼容了 C++17,不论是低版本编译器还是高版本编译器,或者对象存在私有字段等场景,都能使用统一的一套易用的 API。

—— 完 ——

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2179246.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

快手:数据库升级实践,实现PB级数据的高效管理|OceanBase案例

本文作者&#xff1a;胡玉龙&#xff0c;快手技术专家 快手在较初期采用了OceanBase 3.1版本成功替换了多个核心业务、数百套的MySQL集群。至2023年&#xff0c;快手的数据量已突破800TB大关&#xff0c;其中最大集群的数据量更是达到了数百TB级别。为此&#xff0c;快手将数据…

Docker安装consul + go使用consul + consul知识

1. 什么是服务注册和发现 假如这个产品已经在线上运行&#xff0c;有一天运营想搞一场促销活动&#xff0c;那么我们相对应的【用户服务】可能就要新开启三个微服务实例来支撑这场促销活动。而与此同时&#xff0c;作为苦逼程序员的你就只有手动去 API gateway 中添加新增的这…

基于AI的智能化渗透测试技术研究

《网安面试指南》http://mp.weixin.qq.com/s?__bizMzkwNjY1Mzc0Nw&mid2247484339&idx1&sn356300f169de74e7a778b04bfbbbd0ab&chksmc0e47aeff793f3f9a5f7abcfa57695e8944e52bca2de2c7a3eb1aecb3c1e6b9cb6abe509d51f&scene21#wechat_redirect 《Java代码审…

问题:vscode 打印中文时终端输出乱码

文章目录 问题分析解决 问题 在 vscode 编辑器中的终端运行出来的中文是乱码 分析 乱码原因&#xff1a;因windows中文版系统cmd编码默认为GBK&#xff0c;而vscode默认新建文件的编码为UTF-8所以会出现中文乱码情况 解决 终端下输入 chcp 查看当前的cmd编码设置。如图&…

在windows下编译 chromium 的问题汇总(103.0.5060.68 之四)

其实&#xff0c;按照chromium 官方文档来看&#xff0c;大概率是不会出错的&#xff0c;但由于各自的系统差异化&#xff0c;当中遇到的坑也是各不相同。 尤其是在国内的网络情况下&#xff0c;出错是再所难免的&#xff0c;关于这一点&#xff0c;chromium官方文档是没有提及…

哈希-01-数据分类处理

文章目录 1. 题目描述2. 思路3. 代码 1. 题目描述 信息社会&#xff0c;有海量的数据需要分析处理&#xff0c;比如公安局分析身份证号码、 QQ 用户、手机号码、银行帐号等信息及活动记录。 采集输入大数据和分类规则&#xff0c;通过大数据分类处理程序&#xff0c;将大数据…

基于SSM+Vue+MySQL的在线视频学习系统

系统展示 用户前台界面 管理员后台界面 系统背景 随着互联网的飞速发展&#xff0c;在线视频学习已成为人们获取知识、提升技能的重要途径。然而&#xff0c;传统的视频学习平台往往存在内容管理不便、用户体验不佳等问题。因此&#xff0c;开发一款基于SSM&#xff08;SpringS…

12、echarts 没有显示折线图

一、问题描述 echarts 没有显示折线图&#xff0c;但是&#xff0c;有数据显示&#xff1a; 看图表展示&#xff0c;y轴数据全部没有显示&#xff0c;直接可以判定是数据结构出问题了。 检查 series.data[] 数据结构&#xff1a; dataList [{"dateStr":"202…

06.C/C++内存管理

在这里C的内存管理相较于C作出了许多升级&#xff0c;下面我们就来了解一下。 1.C/C内存分布 我们先来看一下下面的问题&#xff0c;温习一下C的内存分布 int globalVar 1; static int staticGlobalVar 1; void Test() {static int staticVar 1;int localVar 1;int num1…

Web安全 - 跨站点请求伪造CSRF(Cross Site Request Forgery)

文章目录 OWASP 2023 TOP 10CSRF 导图CSRF的基本概念CSRF的工作原理常见CSRF攻击模式CSRF防御策略补充建议应用场景实战防御策略选择1. CSRF Token&#xff08;首选&#xff09;2. SameSite Cookie属性3. 验证Referer和Origin4. 多因素认证 实现方案CSRF Token实现SameSite Coo…

JavaWeb——Vue组件库Element(3/6):常见组件:Dialog对话框、Form表单(介绍、使用、实际效果)

目录 Dialog对话框 介绍 使用 实际效果 Form表单 介绍 使用 实际效果 Dialog对话框 介绍 Dialog对话框&#xff1a;在保留当前页面状态的情况下&#xff0c;告知用户并承载相关操作。 Dialog 对话框组件可以在保留当前页面信息的状态下弹出一个对话框&#xff0c;并…

初始MYSQL数据库(8)—— JDBC编程

找往期文章包括但不限于本期文章中不懂的知识点&#xff1a; 个人主页&#xff1a;我要学编程(ಥ_ಥ)-CSDN博客 所属专栏&#xff1a; MYSQL 目录 JDBC的概念 JDBC的使用 加载驱动包 建立连接 创建 statement 对象 定义并执行SQL语句 处理结果集 关闭资源 SQL注入 …

taobao.item_get_appAPI接口原app数据测试指南

在电商竞争日益激烈的当下&#xff0c;数据成为了商家们争夺市场的重要武器。淘宝&#xff0c;作为中国最大的在线零售平台&#xff0c;其庞大的商品库和用户群体为商家提供了巨大的商机。为了帮助商家更好地了解市场动态&#xff0c;优化库存和营销策略&#xff0c;淘宝推出了…

详细分析CollUtil的基本知识(附Demo)

目录 前言1. 集合交并差2. CRUD3. 常用 前言 java框架 零基础从入门到精通的学习路线 附开源项目面经等&#xff08;超全&#xff09;【Java项目】实战CRUD的功能整理&#xff08;持续更新&#xff09; CollUtil 是一个工具类&#xff0c;通常用于集合的操作&#xff0c;提供…

用友U8+CRM leadconversion.php SQL注入复现

0x01 产品描述&#xff1a; 用友U8-CRM是企业利用信息技术&#xff0c;是一项商业策略&#xff0c;它通过依据市场细分组织企业资源、培养以客户为中心的经营行为、执行以客户为中心的业务流程等手段来优化企业的客户满意度和获利能力。 0x02 漏洞描述&#xff1a; 用友 U8 CR…

<<迷雾>第5章 从逻辑学到逻辑电路(1)--继电器 示例电路

布尔代数可以用来解释复杂的开关电路 info::操作说明 鼠标单击开关切换开合状态 注: 开关不能全部都合上, 否则会导致模拟器异常 该异常与模拟器把开关和导线当作了零电阻的理想部件有关, 为避免此问题, 可在开关形成的回路上添加一个额外的电阻 另: 异常出现后, 右上方的运行按…

Nagle 算法:优化 TCP 网络中小数据包的传输

1. 前言 在网络通信中&#xff0c;TCP&#xff08;传输控制协议&#xff09;是最常用的协议之一&#xff0c;广泛应用于各种网络应用&#xff0c;如网页浏览、文件传输和在线游戏等。然而&#xff0c;随着互联网的普及&#xff0c;小数据包的频繁传输成为一个不容忽视的问题。…

行为设计模式 -策略设计模式- JAVA

策略设计模式 一 .简介二. 案例2.1 抽象策略&#xff08;Strategy&#xff09;类2.2 具体策略&#xff08;Concrete Strategy&#xff09;类2.3 环境&#xff08;Context&#xff09;类2.4 测试 三. 结论3.1 优缺点3.2 使用场景 前言 这是我在这个网站整理的笔记,有错误的地方请…

Git大框架总结

下面首先是我对于git的一个小总结&#xff0c;主要是大框架 首先是四区&#xff0c;因为大部分你所有的工作都是在这四个区里的实现的&#xff0c;包括要提交一个东西&#xff0c;是先是在工作区修改&#xff0c;后用add添加到暂存区&#xff0c;后提交到本地仓库&#xff0c;当…

文献阅读——电力系统安全域边界通用搜索模型与近似方法

文章标题 DOI&#xff1a;10.13334/j.0258-8013.pcsee.190884 ©2020 Chin.Soc.for Elec.Eng. 4411 文章编号&#xff1a;0258-8013 (2020) 14-4411-19 中图分类号&#xff1a;TM 74 电力系统安全域边界通用搜索模型与近似方法 姜涛&#xff0c;李晓辉&#xff0c;李雪*&a…