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. 小结:
目的:
- 熟悉 chrono 定时类的用法,
- 熟悉模板函数的写法.
- 熟悉运行函数及编译函数的混合用法
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++的静态多态还有另一种表现形式,同一作用域下多个同名函数,但参数不同.
其最终名称还是由"基本名+参数类型确定".这种类型估计大家都熟悉了.