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

chrono类 根据duration 类的周期类型得到对应的周期名称


author: hjjdebug
date: 2025年 05月 21日 星期三 15:48:00 CST
descrip: chrono类 根据duration 类的周期类型得到对应的周期名称


文章目录

  • 1. 测试代码:
  • 2.条件编译代码:
  • 3. main 函数的汇编码.
  • 4. check_unit_ratio\<std::chrono:duration>long,std::ratio<1l,1l>> 函数
  • 5. get_unit_name\<T> 对应的一个函数
  • 5. 小结:

目的:

  1. 熟悉 chrono 定时类的用法,
  2. 熟悉模板函数的写法.
  3. 熟悉运行函数及编译函数的混合用法

c++与gcc 的交互显得越来越多了. 用多了就熟悉了.

1. 测试代码:

$ cat main.cpp
//chrono类 根据duration 类的周期类型得到对应的周期名称
// 不知道标准库里是否有对应函数, 自己写一下吧, 练练手.还可扩展功能.
#include <iostream>
#include <chrono>
#include <string_view>
//功能: 编译期函数, 获取duration 的类型名称, 
//根据传来的数据类型,返回自编的类型名称
//Period, 是模板类型参数, 类型是可变的.
//string_view 字符串视图,不持有字符串数据,仅是对字符串的引用,无数据copy,内存开销极低. 
//虽然它描述的很好, 但也不及指针来得更轻巧! string_view既然做为类出现,会提供一些常用的方法.template <typename Period>
constexpr std::string_view get_unit_name()  {using namespace std; // 指定现在使用std 命名空间//用if constexpr, 可以简小函数的尺寸,根据表达式,生成或不生成代码,类似于#ifdef #endif,但判断标准是编译期变量的值//is_same_v<T,U> 是编译期函数, 在生成的代码中是看不到的,被gcc 优化掉了. if constexpr (is_same_v<Period, ratio<1, 1>>)return "seconds";else if constexpr (is_same_v<Period, milli>) return "milliseconds";else if constexpr (is_same_v<Period, micro>)return "microseconds";else if constexpr (is_same_v<Period, nano>)return "nanoseconds";else if constexpr (is_same_v<Period, ratio<60, 1>>)return "minutes";else if constexpr (is_same_v<Period, ratio<3600, 1>>)return "hours";elsereturn "custom unit";
}// Unit, 模板类型参数template <typename Unit>
void check_unit_ratio() 
{if constexpr (std::is_integral_v<Unit>)  //传来的类型不能是整数,由于本测试其为假,所以该代码块不会出现在执行代码中.{std::cout << "You should Use Duration Type, not int type!"<<"\n";return;}using Period = typename Unit::period; //模板类型必需要有period类型, duration 的period 是分数模板类型.std::cout << "Duration unit: " << get_unit_name<Period>() << "\n"; //由于该调用,会生成一堆get_unit_name<...>函数std::cout << "Numerator: " << Period::num << "\n";std::cout << "Denominator: " << Period::den << "\n";
}//自建一个测试类型
class MyClass
{
};
int main() {using namespace std::chrono;// 预定义数据类型单位检测,seconds,milliseconds,milliseconds 都是chrono中预先定义的类型check_unit_ratio<seconds>(); //由于该调用,会为它生成check_unit_ratio<seconds> 函数check_unit_ratio<milliseconds>(); //由于该调用,会为它生成check_unit_ratio<milliseconds> 函数check_unit_ratio<microseconds>(); //由于该调用,会为它生成check_unit_ratio<microseconds> 函数// 自定义单位检测(例如:定义一个100毫秒的类型0.1)using MyDuration = duration<int, std::ratio<1, 10>>; // 0.1秒check_unit_ratio<MyDuration>(); //由于该调用,会为它生成check_unit_ratio<MyDuration> 函数// 随便给个类型,它肯定干不了, 因为它不能实例化指定的模板函数, 例如下面的代码// main.cpp:37:11: error: ‘double’ is not a class, struct, or union type// 37 |     using Period = typename Unit::period; //模板类型必需要有period类型, period类型是后续要处理的类型//	check_unit_ratio<double>();//main.cpp|37 col 11| error: no type named ‘period’ in ‘class MyClass’//    37 |     using Period = typename Unit::period; //模板类型必需要有period类型, period类型是后续要处理的类型//	check_unit_ratio<MyClass>();
}

执行结果:
$ ./temp2
Duration unit: seconds
Numerator: 1
Denominator: 1
Duration unit: milliseconds
Numerator: 1
Denominator: 1000
Duration unit: microseconds
Numerator: 1
Denominator: 1000000
Duration unit: custom unit
Numerator: 1
Denominator: 10

2.条件编译代码:

if constexpr在编译时评估表达式,并根据结果选择要编译的代码路径。
只有当条件表达式为真时,相应的代码块才会被编译,其他分支的代码则不会被生成.
下面函数是可以和if constexpr 配合的编译期函数
std::is_integral_v
std::is_floating_point_v
std::is_same_v<T,U>

这些是编译期函数,其结果是true或false, 可控制代码编译.

想搞清主代码的执行框架, 看清gcc做了哪些工作.还是要结合看汇编代码了.

3. main 函数的汇编码.

例如: main 函数, 可以看到, 它调用了很多函数. 意味着,模板函数 check_unit_ratio() 生成了很多函数.

int main() {4011d6:	f3 0f 1e fa          	endbr64 4011da:	55                   	push   %rbp4011db:	48 89 e5             	mov    %rsp,%rbpusing namespace std::chrono;// 预定义数据类型单位检测,seconds,milliseconds,milliseconds 都是chrono中预先定义的类型check_unit_ratio<seconds>(); //由于该调用,会为它生成check_unit_ratio<seconds> 函数4011de:	e8 79 01 00 00       	callq  40135c <void check_unit_ratio<std::chrono::duration<long, std::ratio<1l, 1l> > >()>check_unit_ratio<milliseconds>(); //由于该调用,会为它生成check_unit_ratio<milliseconds> 函数4011e3:	e8 2d 02 00 00       	callq  401415 <void check_unit_ratio<std::chrono::duration<long, std::ratio<1l, 1000l> > >()>check_unit_ratio<microseconds>(); //由于该调用,会为它生成check_unit_ratio<microseconds> 函数4011e8:	e8 e1 02 00 00       	callq  4014ce <void check_unit_ratio<std::chrono::duration<long, std::ratio<1l, 1000000l> > >()>// 自定义单位检测(例如:定义一个100毫秒的类型0.1)using MyDuration = duration<int, std::ratio<1, 10>>; // 0.1秒check_unit_ratio<MyDuration>(); //由于该调用,会为它生成check_unit_ratio<MyDuration> 函数4011ed:	e8 95 03 00 00       	callq  401587 <void check_unit_ratio<std::chrono::duration<int, std::ratio<1l, 10l> > >()>// 37 |     using Period = typename Unit::period; //模板类型必需要有period类型, period类型是后续要处理的类型
//	check_unit_ratio<double>();
//main.cpp|37 col 11| error: no type named ‘period’ in ‘class MyClass’
//    37 |     using Period = typename Unit::period; //模板类型必需要有period类型, period类型是后续要处理的类型
//	check_unit_ratio<MyClass>();
}

check_unit_ratio<type>() 会变成对应一堆函数,
而using MyDuration = duration<int ,std::ratio<1,10>> 则不对应任何代码, 它只是一个类型说明.
跟入一个check_unit_ratio<type>, 例如 40135c, 其它的类似,有可比性.

4. check_unit_ratio<std::chrono:duration>long,std::ratio<1l,1l>> 函数

000000000040135c <void check_unit_ratio<std::chrono::duration<long, std::ratio<1l, 1l> > >()>:
void check_unit_ratio() 40135c:	f3 0f 1e fa          	endbr64 401360:	55                   	push   %rbp       //保留rbp,401361:	48 89 e5             	mov    %rsp,%rbp401364:	41 54                	push   %r12    //保留r12, rbx401366:	53                   	push   %rbxstd::cout << "Duration unit: " << get_unit_name<Period>() << "\n"; //由于该调用,会生成一堆get_unit_name<...>函数401367:	48 8d 35 96 0c 00 00 	lea    0xc96(%rip),%rsi        #   //字符串 “Duration unit:", 第2参数40136e:	48 8b 05 6b 2c 00 00 	mov    0x2c6b(%rip),%rax       401375:	48 89 c7             	mov    %rax,%rdi  //cout this 对象指针,第1参数//调用 << 运算符, <<符有多态性.401378:	e8 23 fd ff ff       	callq  4010a0 <std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)@plt>40137d:	49 89 c4             	mov    %rax,%r12 , 保留返回值//调用get_unit_name<T> 函数, 返回string401380:	e8 bb 02 00 00       	callq  401640 <std::basic_string_view<char, std::char_traits<char> > get_unit_name<std::ratio<1l, 1l> >()>401385:	48 89 c1             	mov    %rax,%rcx  //返回值保留401388:	48 89 d3             	mov    %rdx,%rbx40138b:	48 89 d0             	mov    %rdx,%rax40138e:	48 89 ce             	mov    %rcx,%rsi  //返回值给第2参数401391:	48 89 c2             	mov    %rax,%rdx401394:	4c 89 e7             	mov    %r12,%rdi  //第1参数//调用 << 运算符401397:	e8 05 03 00 00       	callq  4016a1 <std::basic_ostream<char, std::char_traits<char> >& std::operator<< <char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, std::basic_string_view<char, std::char_traits<char> >)>40139c:	48 8d 35 71 0c 00 00 	lea    0xc71(%rip),%rsi        # endl 给第2参数4013a3:	48 89 c7             	mov    %rax,%rdi    //返回值给第1参数//调用 << 运算符4013a6:	e8 f5 fc ff ff       	callq  4010a0 <std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)@plt>std::cout << "Numerator: " << Period::num << "\n";4013ab:	48 8d 35 64 0c 00 00 	lea    0xc64(%rip),%rsi        # 402016,2参数 "Numerator:" 字符串 4013b2:	48 8b 05 27 2c 00 00 	mov    0x2c27(%rip),%rax        # 403fe0 第1参数 std::cout 对象地址4013b9:	48 89 c7             	mov    %rax,%rdi//调用 << 运算符4013bc:	e8 df fc ff ff       	callq  4010a0 <std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)@plt>4013c1:	be 01 00 00 00       	mov    $0x1,%esi       # 第2参数, Period::num 为常数14013c6:	48 89 c7             	mov    %rax,%rdi       # 返回值给第1参数//调用 << 运算符4013c9:	e8 12 fd ff ff       	callq  4010e0 <std::basic_ostream<char, std::char_traits<char> >::operator<<(long)@plt>4013ce:	48 8d 35 3f 0c 00 00 	lea    0xc3f(%rip),%rsi        # 402014 "\n"字符串4013d5:	48 89 c7             	mov    %rax,%rdi         # 返回值//调用 << 运算符4013d8:	e8 c3 fc ff ff       	callq  4010a0 <std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)@plt>.... //其它忽略了!40140f:	90                   	nop401410:	5b                   	pop    %rbx # 恢复现场, 返回401411:	41 5c                	pop    %r12401413:	5d                   	pop    %rbp401414:	c3                   	retq   

5. get_unit_name<T> 对应的一个函数

分析一个get_unit_name<T> 函数, 例如:401640, 实际返回的是 “seconds” 字符串的string_view视图

0000000000401640 <std::basic_string_view<char, std::char_traits<char> > get_unit_name<std::ratio<1l, 1l> >()>:
constexpr std::string_view get_unit_name()  //string_view 字符串视图,不持有字符串数据,仅是对字符串的引用,无数据copy,内存开销极低. 401640:	f3 0f 1e fa          	endbr64 401644:	55                   	push   %rbp       //保留rbp401645:	48 89 e5             	mov    %rsp,%rbp  //调整栈帧 401648:	48 83 ec 20          	sub    $0x20,%rsp  //开辟局部变量空间40164c:	64 48 8b 04 25 28 00 	mov    %fs:0x28,%rax401653:	00 00 401655:	48 89 45 f8          	mov    %rax,-0x8(%rbp) //保留%0xfs:0x28到局部变量401659:	31 c0                	xor    %eax,%eaxreturn "seconds";40165b:	48 8d 45 e0          	lea    -0x20(%rbp),%rax40165f:	48 8d 35 ca 09 00 00 	lea    0x9ca(%rip),%rsi        # 402030 字符串seconds 地址401666:	48 89 c7             	mov    %rax,%rdi    //this 指针,局部变量地址# 调用std::string_view 的构造函数401669:	e8 74 01 00 00       	callq  4017e2 <std::basic_string_view<char, std::char_traits<char> >::basic_string_view(char const*)>40166e:	48 8b 45 e0          	mov    -0x20(%rbp),%rax , 把局部变量-0x20(%rbp)内容送返回值401672:	48 8b 55 e8          	mov    -0x18(%rbp),%rdx
}401676:	48 8b 4d f8          	mov    -0x8(%rbp),%rcx40167a:	64 48 33 0c 25 28 00 	xor    %fs:0x28,%rcx  %fs:-x28 与局部变量-0x8(%rbp)相异或应该为0401681:	00 00 # 安全判别,0正常则退出401683:	74 05                	je     40168a <std::basic_string_view<char, std::char_traits<char> > get_unit_name<std::ratio<1l, 1l> >()+0x4a>401685:	e8 26 fa ff ff       	callq  4010b0 <__stack_chk_fail@plt> //堆栈被破环40168a:	c9                   	leaveq 40168b:	c3                   	retq   

5. 小结:

类模板会生成很多类. 函数模板会生成很多函数. 模板以类型为参数生成类,生成函数, 这是c++的静态多态
其最终的函数名称由"基本名+参数类型确定"
c++的静态多态还有另一种表现形式,同一作用域下多个同名函数,但参数不同.
其最终名称还是由"基本名+参数类型确定".这种类型估计大家都熟悉了.

相关文章:

  • 基于AI大语言模型的历史文献分析在气候与灾害重建中的技术-以海南岛千年台风序列重建为例
  • 现代生活中的健康养生之道
  • 传统Spring MVC + RESTful 与 Vue3 结合 JWT Token 验证的示例
  • 双检锁(Double-Checked Locking)单例模式
  • 管理会议最佳实践:高效协同与价值最大化
  • 卫星互联网:构建全球无缝通信网络的未来
  • C#SQLServer数据库通用访问类
  • Seata源码—8.Seata Saga模式的事务处理二
  • 线程、线程池、异步
  • OpenHarmony外设驱动使用 (九),Pin_auth
  • [Harmony]自定义导航栏
  • OpenHarmony外设驱动使用 (十),Sensor
  • DeepSeek 的强化学习优化策略:RLHF 与 DPO 的应用
  • canvas(二)-动画(2d)
  • 如果有三个服务实例部署在三台不同的服务器上,这三个服务实例的本地缓存,是存储一模一样的数据?还是各自只存一部分?
  • 《算法笔记》12.1小节——字符串专题->字符串hash进阶 问题 A: 求最长公共子串(串)
  • vscode连接本地Ubuntu
  • Docker安装Fluentd采集中间件
  • OpenCV CUDA模块特征检测与描述------一种基于快速特征点检测和旋转不变的二进制描述符类cv::cuda::ORB
  • 嵌入式学习的第二十五天-系统编程-文件相关函数-标准I0+文件IO
  • 网页网站公司如何做备份/域名访问网站
  • 做网站都不赚钱了吗/企业培训课程价格
  • 唐山建网站公司/百度推广费用
  • 网站怎么做让PC和手机自动识别/网站接广告
  • 云南网站开发建设/百度登录入口