目录
(三)错误代码 error_code 和 error_condition
跨平台、可移植一直是很多 C ++ 程序员所追求的目标。由于 C++ 是一种“中级”语言,很多时候它都要与操作系统打交道,所以其功能代码常常与平台结合得很紧密,难以做到真正的跨平台,在向其他平台移植代码的时候修改与操作系统相关的代码通常是必不可少的工作。Boost提供了数个与操作系统相关的库,在一定程度上屏蔽了系统的底层细节,能够提高程序的可移植性。
一、boost.system
操作系统和许多底层 API 不具有这个能力 , 它们一般使用更通用、也更难操作的错误代码来表示出错原因 , 不同操作系统的错误代码通常是不兼容的 , 这给编写跨平台的程序带来了很大的麻烦。
system库使用轻量级的对封装了操作系统底层的错误代码和错误信息,为上层提供了一个可移植的统一处理接口,使调用操作系统功能的程序可以被很容易地移植到其他操作系统。system库已经被收入 C++11 标准(C ++11.19.5)。
system库的核心类是【用于标识错误代码类别的error_category】、【用于表示错误代码的error_code 和 error_condition】。
(一)错误值枚举
system库在名字空间 boost::system::errc 里定义了一个很大的枚举类 errc_t ,它定义了通用的可移植的错误代码,我们应当尽量使用它来标记错误原因。
system库在头文件<boost/system/linux_error.hpp>和<boost/system/windows_error.hpp>里分别定义了Linux和Windows操作系统特定的错误值枚举,分别位于boost::system::linux_error和boost::system::windows_error。如果针对这两个操作系统做特定编程,则可以包含这两个头文件。
#include <boost/system/error_code.hpp>
#include <boost/system/windows_error.hpp>
void TestSystem()
{
std::cout <<"通用的错误码:"<< boost::system::errc::value_too_large << std::endl;//通用的错误码:132
std::cout <<"windows下的错误码:"<< boost::system::windows_error::already_exists << std::endl;//windows下的错误码:183
}
(二)错误类别 error_category
error_category是一个抽象基类,用于标识错误代码的类别。所以error_category不能直接使用,必须使用继承的方式。
system库预定义了error_category的两个子类:system_error_category和generic_error_category,用于表示系统错误和通用的可移植错误,可以用boost::system的自由函数system_category( ) 和 generic_category( )间接访问它们。
error _ category 的核心函数共有以下 4 个:
- 成员函数 name ( ) 可以获得类别的名称。
- message ( ) 可以获得错误代码 ev 对应的描述信息。
- default error condition ( ) 是一个工厂方法,它由错误代码 ev 产生一个 error_condition 对象。
- equivalent ( ) 用于比较两个错误代码是否相等。
当我们从error_category 派生一个新类时,name( )和 message( )是必须实现的,因为它们是纯虚函数,其余的函数可以使用error_category 的默认实现。
class MyCategory :public boost::system::error_category {
public:
virtual const char* name() const BOOST_SYSTEM_NOEXCEPT
{
return "My Category";
}
virtual std::string message(int ev)const {
std::string msg;
switch (ev)
{
case 0:
msg = "ok";
break;
default:
msg = "has error";
break;
}
return msg;
}
virtual boost::system::error_condition default_error_condition(int ev) const BOOST_NOEXCEPT {
return boost::system::error_condition(ev,boost::system::system_category());
}
};
MyCategory myCategory;
std::cout << "自定义错误类别名称:" << myCategory.name() << ",错误码0表示:" << myCategory.message(0) << std::endl;
//自定义错误类别名称:My Category,错误码0表示:ok
(三)错误代码 error_code 和 error_condition
error_code和error_condition都用于表示错误代 码,error_code更接近操作系统和底层 API,而error_condition更偏重于可移植(用于跨平台)。两者的声明类似,但error_code可通过default_error_condition ( ) 的方法把 error_code 转换成 error_condition。
error_code和error_condition都可以从一个整数错误值或 errc_t 枚举构造,同时可以指定所属的错误类别。如果无参构造,那么错误值将会是 0 ( 无错误 )。
error_code对象内部的值可以用 value( )获得,获得错误描述要使用 message( ),它将调用error_category 的 message ( )返回描述信息。
不同的错误类别决定了 error_code 的含义,相同的错误代码如果属于不同的类别,那么将具有不同的含义。在使用自定义错误类别的时候,我们不能向error_code传递临时对象构造,因为 error code 的构造函数接收的是error_category 的引用,它不会拷贝副本,运行时因为临时对象在语句结束后析构,再调用成员函数时会发生未定义行为。
//error_code 使用自定义错误类别
boost::system::error_code error_code1(10,myCategory);
std::cout <<"value:"<< error_code1.value()
<< ",错误类别名称:" << error_code1.category().name()
<< ",错误码含义:" << error_code1.category().message(error_code1.value())
<<"/"<<error_code1.message() << std::endl;
//value:10,错误类别名称:My Category,错误码含义:has error/has error
//error_code 使用system_category系统错误类别
boost::system::error_code error_code2(10, boost::system::system_category());
std::cout << "value:" << error_code2.value()
<< ",错误类别名称:" << error_code2.category().name()
<< ",错误码含义:" << error_code2.category().message(error_code2.value())
<< "/" << error_code2.message() << std::endl;
//value:10,错误类别名称:system,错误码含义:环境不正确。/环境不正确。
//error_condition 使用generic_category通用错误类别
boost::system::error_condition error_condition(10,boost::system::generic_category());
std::cout << "value:" << error_condition.value()
<< ",错误类别名称:" << error_condition.category().name()
<< ",错误码含义:" << error_condition.category().message(error_condition.value())
<< "/" << error_condition.message() << std::endl;
//value:10, 错误类别名称:generic, 错误码含义:No child processes/No child processes
error_code和error_condition都提供 bool 转换的能力,可以在一个 bool 语境中转换成 bool 值,其含义与我们通常处理的错误代码的含义相同,当错误值非 0 时返回 true。它们也重载了比较操作符,可以执行相等或不等比较。
if (error_code1)//转bool 如果非0,即有错误
{
//产生一个可移植的错误代码
boost::system::error_condition error_condition1 = error_code1.default_error_condition();
std::cout << "value:" << error_condition1.value()
<< ",错误类别名称:" << error_condition1.category().name()
<< ",错误码含义:" << error_condition1.message() << std::endl;
//value:10,错误类别名称:system,错误码含义:环境不正确。
}
boost::system::error_code _code(2, boost::system::system_category());
if (_code)
{
std::cout << "没有移植前:value:" << _code.value()
<< ",错误类别名称:" << _code.category().name()
<< ",错误码含义:" << _code.message() << std::endl;
//没有移植前:value:2,错误类别名称:system,错误码含义:系统找不到指定的文件。
boost::system::error_condition error_condition1 = _code.default_error_condition();
std::cout << "移植后:value:" << error_condition1.value()
<< ",错误类别名称:" << error_condition1.category().name()
<< ",错误码含义:" << error_condition1.message() << std::endl;
//移植后:value:2,错误类别名称:generic,错误码含义:No such file or directory
}
(四)错误异常
system库还提供一个异常类 system_error ,它是std::runtime_error 的子类,是对 error_code 的一个适配,可以把 error_code 应用到 C ++ 的异常处理机制之中。
#include <boost/system/system_error.hpp>
try {
boost::system::error_code _code1(3, boost::system::generic_category());
throw boost::system::system_error(_code1);
}
catch (boost::system::system_error e) {
std::cout << e.what() << std::endl;//No such process
}
二、boost.chrono
chrono完全实现了 C ++ 标准里定义的时间处理库( C++11.20.11 ),并且额外增加了一些有用的功能。chrono库里的很多时间概念与 date_time库类似,但它更侧重于表达“计算机世界”里的时间。
(一)时间长度 duration
chrono库定义的时间长度 duration 在概念上与 date_time 库的time_duration 相同,都表示一定长度的时间,但duration侧重于编译期的时间单位的表示,更接近ratio,所以它与 time_duration 的接口有很大差异。
1.duration模板参数
duration有两个模板参数:
- 第一个参数 Rep 是一个算术类型(或者可以将其当作算术类型运算),如int、double,表示时间单位的数量;
- 第二个参数Period表示时间单位,以秒为基准,它必须是 ratio类型,且 Period::num 必须是正数。
#include <boost/chrono.hpp>
boost::chrono::duration < int, boost::ratio<30>> _30second;//30秒
2.chrono库预定义的时间单位
chrono库预定义了常用的时间单位,如 hours 、minutes、 seconds 、 milliseconds 等,但它与date_time 库 time_duration 使用派生子类的方式不同, chrono 库通过在模板参数里使用不同的 ratio 来定义时间单位,因此每一个时间单位都是不同的类型。
duration内部只有一个成员变量 rep_ ,用来存储时间的计数,支持各种算术运算。它本质上就是一个算术类型。而时间单位则用 ratio 在编译期表示,各种时间单位的转换都是通过 ratio 在编译期模板元计算完成的(具体的实现原理我们暂不必深究)。
duration的构造函数指定它的时间计数,表示一段rep_* ratio 秒的时间长度,成员函数 count( )可以获得内部保存的时间计数 rep_ 。
boost::chrono::hours h(1);//1小时
std::cout << h.count() << std::endl;//1
boost::chrono::minutes m(30);//30分钟
std::cout << m.count() << std::endl;//30
boost::chrono::seconds value = h - m;
std::cout << value << std::endl;//1800 seconds
3.duration的运算
duration可以像 int 、 double 一样执行各种算术运算和比较运算,也能够直接流输出。运算时要注意小的时间单位不能转为大的时间单位。
m += h;
std::cout << m << std::endl;//90 minutes
//h += m;//错误
当duration的模板参数是double时,小的时间单位不能转为大的时间单位。
//double支持小的时间转换为大时间
typedef boost::chrono::duration < double, boost::ratio<3600>> myhour;//小时
myhour _5h(5);//5小时
boost::chrono::seconds s(1);//1秒
_5h += s;
std::cout << _5h << std::endl;//5.00028 hours
4.duration时间类型转换
大的时间单位可以直接转为小的时间单位。
boost::chrono::hours hours(5);
boost::chrono::seconds seconds = hours;
std::cout << seconds << std::endl;//18000 seconds
duration_cast支持小的时间单位转为大的时间单位,但只是执行简单的舍入取整。
boost::chrono::hours hours1 = boost::chrono::duration_cast<boost::chrono::hours>(seconds);
std::cout << hours1 << std::endl;//5 hours
chrono库里还有 3 个 C ++ 标准之外的扩展函数——floor ( ) 、 ceil ( ) 和 round ( )。
- floor ( ) :与 duration_cast ( ) 相同,取下界,它是简单的截断处理。
- ceil ( ) :取上界。
- round ( ):四舍五入操作。
typedef boost::chrono::duration < double, boost::ratio<65>> seconds65;//65秒
seconds65 _seconds65(2);
std::cout << _seconds65 << std::endl;//2*65 seconds
std::cout << "130秒向下取整:" << boost::chrono::floor<boost::chrono::minutes>(_seconds65) << std::endl;//130秒向下取整:2 minutes
std::cout << "130秒向上取整:" << boost::chrono::ceil<boost::chrono::minutes>(_seconds65) << std::endl;//130秒向上取整:3 minutes
std::cout << "130秒四舍五入:" << boost::chrono::round<boost::chrono::minutes>(_seconds65) << std::endl;//130秒四舍五入:2 minutes
(二)时钟 clock
时钟 ( Clock ) 它确定了一个时间的起点 ( since ) 和时间单位( duration ) , 使用时钟我们就可以处理 " 计算机世界 " 里的时间。
chrono库实现了 C ++ 标准里定义的 3 个时钟:
- system_clock:如实反映计算机世界里的实际时间的时钟。
- steady_clock:稳定的时钟 , 不会因系统时间调整而变化,从系统启动开始计时。
- high_resolution_clock:高分辨率的时钟 , 但通常是前两者的 typedef。
此外 chrono 库还定义了 4 个度量程序运行时间的时钟:
- process_real_cpu_clock:进程执行的实际时间。
- process_user_cpu_clock:用户 CPU 时间。
- process_system_cpu_clock:系统 CPU 时间。
- thread_clock:线程执行的实际时间。
各个时钟的接口基本相同。
- 时钟用内部的 4 个 typedef 确定了可以产生的时间类型。
- is_steady 用来区分时钟是否会随系统时间的调整而改变 , 对于 system _ clock , 它的值是 false。
- now ( ) 用来获得当前的时间点—— time_point 对象。
- system_clock 额外提供与 time_t 结构的互换操作,而 steady_clock 、high_resolution_clock 等时钟则不支持此操作。
- 使用辅助类 clock_string 的两个静态成员函数name ( ) 、since ( ) 可以获取时钟的描述信息 。
boost::chrono::system_clock clock1;
std::cout << boost::chrono::clock_string< boost::chrono::system_clock, char>::name() << std::endl;
std::cout << boost::chrono::clock_string< boost::chrono::system_clock, char>::since() << std::endl;
std::cout << "system_clock:" << clock1.is_steady << std::endl;
//system_clock
//since Jan 1, 1970
//system_clock:0
boost::chrono::steady_clock clock2;
std::cout << boost::chrono::clock_string< boost::chrono::steady_clock, char>::name() << std::endl;
std::cout << boost::chrono::clock_string< boost::chrono::steady_clock, char>::since() << std::endl;
std::cout << "steady_clock:" << clock2.is_steady << std::endl;
//steady_clock
//since boot
//steady_clock : 1
boost::chrono::process_real_cpu_clock clock3;
std::cout << boost::chrono::clock_string< boost::chrono::process_real_cpu_clock, char>::name() << std::endl;
std::cout << boost::chrono::clock_string< boost::chrono::process_real_cpu_clock, char>::since() << std::endl;
std::cout << "process_real_cpu_clock:" << clock3.is_steady << std::endl;
//process_real_clock
//since process start - up
//process_real_cpu_clock : 1
boost::chrono::process_user_cpu_clock clock4;
std::cout << boost::chrono::clock_string< boost::chrono::process_user_cpu_clock, char>::name() << std::endl;
std::cout << boost::chrono::clock_string< boost::chrono::process_user_cpu_clock, char>::since() << std::endl;
std::cout << "process_user_cpu_clock:" << clock4.is_steady << std::endl;
//process_user_clock
//since process start - up
//process_user_cpu_clock : 1
boost::chrono::process_system_cpu_clock clock5;
std::cout << boost::chrono::clock_string< boost::chrono::process_system_cpu_clock, char>::name() << std::endl;
std::cout << boost::chrono::clock_string< boost::chrono::process_system_cpu_clock, char>::since() << std::endl;
std::cout << "process_system_cpu_clock:" << clock5.is_steady << std::endl;
//process_system_clock
//since process start - up
//process_system_cpu_clock : 1
(三)时间点 time_point
时间点 time_point 与时钟紧密关联,时间点必须由一个时钟产生,它标记了自时钟起点以来所经过的时间。
time_point 的实例通常由时钟类的静态成员函数now()产生,它内部持有 duration 成员变量,不仅支持 C ++ 标准规定的加、减运算和比较运算,还支持自增、自减运算。
time_since_epoch() :可获取自时间点以来的时间长度。
system_clock 提供静态成员函数to_time_t( ),可以把time_point 对象(system_clock产生的,steady_clock 等其他时钟因为不与确定的现实时间点关联,所以它们产生的 time _ point 对象无法转换为实际时间。)转换为time_t 类型。以 time_t 为基础,我们既可以使用date_time 库的 rom_time_t ()转换到 ptime 对象,也可以用 C 中的 ctime ()系列函数操作。
auto time_point1 = boost::chrono::system_clock::now();
auto d1 = time_point1.time_since_epoch();
boost::chrono::hours _hours1= boost::chrono::duration_cast<boost::chrono::hours>(d1);
std::cout << time_point1<<","<< _hours1 << std::endl;
//16379891731654652[1 / 10000000]seconds since Jan 1, 1970, 454996 hours
auto time_t = boost::chrono::system_clock::to_time_t(time_point1);
std::cout <<std::ctime(&time_t)<< std::endl;//Sat Nov 27 13:09:18 2021
auto time_point2 = boost::chrono::steady_clock::now();
auto d2 = time_point2.time_since_epoch();
boost::chrono::minutes minutes = boost::chrono::duration_cast<boost::chrono::minutes>(d2);
std::cout << time_point2 << "," << minutes << std::endl;//16425847041200 nanoseconds since boot,273 minutes //此时13:03,大概早上八点半开的电脑
三、boost.cpu_timer
cpu_timer使用了 chrono 库的高精度时钟high_resolution_clock,不仅能够度量进程使用的实际时间,还能度量 CPU 时间,它最高支持微秒级别的计时,而且使用起来非常方便,是旧版本的timer的很好的替代品。
1.时间类型
cpu_timer库在 boost::timer名字空间里定义了 库使用的时间类型nanosecond_type 和cpu_times。
typedef boost::int_least64_t nanosecond_type; //计时用的纳秒类型 struct cpu_times //CPU时间类型 { nanosecond_type wall; //挂钟(日历)时间 nanosecond_type user; //用户CPU(进程)时间 nanosecond_type system; //系统CPU(进程)时间 void clear() { wall = user = system = 0; } //简单的清零操作 };
cpu_timer 库以 nanosecond_type 作为它的计时单位,需要注意因为受系统限制它不会达到纳秒级别的精确度。实际情况是挂钟时间 ( wall clock time ) 的精度大约是微秒 (1000 纳秒 ),CPU时间的精度大约是 10~15 毫秒 ( 1000 × 1000纳秒 ) 。
cpu_times 类型整合了 3 个计算机系统里常用的时间度量:
- wall:挂钟 ( 日历 ) 时间,进程运行的实际时间。
- user:用户 CPU 时间,进程执行用户指令使用的CPU时间。
- system:系统 CPU 时间,进程执行系统内核调用使用的 CPU 时间。
cpu _ times 是一个 POD 类型,只有一个简单的clear ( ) 成员函数用于时间清零。
2.cpu_timer
cpu_timer 的构造函数调用 start( )记录当前时间作为计时起点,之后就可以用 elapsed ()来获得启动后流逝的时间。计时过程中我们可以用stop()暂停计时器,可以用 resume ()恢复计时器的运行,如果想重新启动计时器则需要调用start ( ) ——这是与 timer 的不同之处( timer 使用的是 restart ()函数)。
cpu_times 没有流输出功能,但它提供格式化函数format ( ) ,可以把 elapsed ()的结果转换为一个可读的字符串。
#include <boost/timer/timer.hpp>
void TestCPUTimer()
{
timer::cpu_timer cpuTimer;
if (!cpuTimer.is_stopped())
{
std::vector<int> vector{ 1,2,3,4,5,6,7,8,9 };
for (int j = 0; j < 1000; j++)
{
for (size_t i = 0; i < vector.size(); i++)
{
vector[i] += i;
}
}
cpuTimer.stop();
std::cout << cpuTimer.format() << std::endl;//0.000022s wall, 0.000000s user + 0.000000s system = 0.000000s CPU (n/a%)
}
}
3.format格式
字符串的格式是可以定义的,cpu_timer 库的 format 函数使用两个参数来定制计时结果的时间类型 cpu_times 的格式:短整型的places和字符串类型的 format。
- 短整型 places 用于指定输出值的小数点后的精确度 , 默认值是 6 ( 微秒 ) , 最多可以是 9 ( 纳秒 , 但因为计时精度的限制这通常没有意义 ) 。
- 字符串参数 format 用于指定输出的格式,之前我们默认使用的格式由匿名名字空间的字符串常量default_fmt 确定。
//源码位置:boost_1_xx_0\libs\timer\src\cpu_timer.cpp
const std::string default_fmt(" %ws wall, %us user + %ss system = %ts CPU (%p%)\n");
- %w:挂钟时间,即 cpu_times.Wall 值
- %u:用户CPU时间,即 cpu_times.user 值
- %s:系统 CPU时间,即 cpu_times.system 值
- %t:总计CPU时间,即cpu_times.user+cpu_times.system。
- %p:总计CPU 时间占挂钟时间的百分比。
如果 CPU 时间值太小,小于 cpu_timer 库的度量精度,那么百分比就会显示为n/a(not available)。
std::string format("%w秒,%u秒+%s秒=%t秒,%p");
std::cout << cpuTimer.format(9, format) << std::endl;//0.000022300秒,0.000000000秒+0.000000000秒=0.000000000秒,n/a
cpu_timer 库里还有两个格式化函数,用来把cpu_times 类型格式化为字符串,以被 cpu_timer 和auto_cpu_timer 调用:
timer::cpu_times cpu_time{ 100000,100000,100000 };
std::cout << timer::format(cpu_time, 6) << std::endl;// 0.000100s wall, 0.000100s user + 0.000100s system = 0.000200s CPU (n/a%)
4.auto_cpu_timer
auto_cpu_timer是一个类似于progress_timer 的自动计时器 , 它继承自cpu_timer。
auto_cpu_timer具有 cpu_timer 的所有接口 , 同progress_timer —样 , 它会在析构时自动输出流逝的时间 , 非常简单、易用,我们也可以用report ( ) 函数直接向流输出计时结果。
auto_cpu_timer有 6 个构造函数,可以用来定制计时的输出格式。
void TestAutoCpuTimer()
{
timer::auto_cpu_timer autoCpuTimer;
std::vector<int> vector{ 1,2,3,4,5,6,7,8,9 };
for (size_t i = 0; i < vector.size(); i++)
{
vector[i] += i;
}
//0.000097s wall, 0.000000s user + 0.000000s system = 0.000000s CPU (n/a%)
}