Boost源码简析系列——timer(2)
对于timer的第二个头文件中包含的两个类,我早就想写出来,但是最近忙着写代码,今天终于有时间写了,并且前段时间解决了一个问题,用的就是timer,所以也给了我动力。
剩下的两个类,grogress_timer和grogress_display都定义在头文件progress.hpp中,由Beman Dawes编写,最新版是2001年11月1日发表。
1. 类progress_timer
#ifndef BOOST_PROGRESS_HPP
#define BOOST_PROGRESS_HPP
…
#endif
同样,这是几乎每个头文件都会有的包含保护符。
#include <boost/timer.hpp> //因为progress_timer继承于类timer,所以包含进来。
#include <boost/utility.hpp> //timer定义中也遇到,其中包含了类noncopyable的定义,当progress_timer继承了这个类后,就没有了拷贝构造函数了。
#include <boost/cstdint.hpp> //boost在这里面定义了很多数据类型。
#include <iostream>
#include <string> //这两个没有什么好说的,但是现在写代码最好还是以标准库为准,比如用string,而不是用char *。
class progress_timer : public timer, private noncopyable
{
public:
explicit progress_timer( std::ostream & os = std::cout )
: m_os(os) {}
~progress_timer()
{
try
{
std::istream::fmtflags old_flags = m_os.setf( std::istream::fixed,
std::istream::floatfield );
std::streamsize old_prec = m_os.precision( 2 );
m_os << elapsed() << " s\n" << std::endl;
m_os.flags( old_flags );
m_os.precision( old_prec );
}
catch (...) {}
}
private:
std::ostream & m_os;
};
从代码上看,progress_timer真是简单的可以了,她继承于timer和noncopyable。主要代码都在析构函数中,意思就是说在其对象析构的时候把,对象生存的时间显示出来。
构造函数用explicit表示不能用于隐藏类型转换。
计算流逝的时间的函数是elapsed(),是从timer继承而来。要读懂这段东西,我想最复杂就是对输出流的操作了,这里涉及到了,对输出流输出格式状态的设置,对输出流输出精度的设置。
fmtflags:是一种在标准库流中定义的一种数据类型,她的对象表示了流的i/o操作的一组标志和整数值控制。这些标志有很多,比如:
boolalpha
用符号形式表示真假。
dec
以10为基数输出
hex
以16为基数输出
fixed
浮点数输出格式
showbase
输出前缀,八进制为0,十六进制为0x
floatfield
与浮点数输出有关的标志组
unitbuf
每次输出操作之后刷新
…
…
在这里只是列出了极少的一部分,比较详细的描述可参考The C++ Programming Language 第21章。
std::istream::fmtflags old_flags = m_os.setf( std::istream::fixed,
std::istream::floatfield );
所以这条语句的意思就是说,给标准输出对象m_os设置输出格式是浮点数格式,并保存原来的格式,便于完成输出后恢复设置。
std::streamsize old_prec = m_os.precision( 2 );
这条设置了,输出精度为2。
剩下的工作就是输出,并恢复了。
在这段代码中用了异常处理技术。
try
{
//…
}
catch(…){//…}
在这里用catch后面的…表示捕捉所有的异常。
2. 类grogress_display
这个类好像与前面两个类没有一点关系,我们通过她可以在控制台程序中实现用一个进度条显示出我们任务的进展情况。
我们建造一个该类的对象时,会显示出一个进度条标尺,在调用operator ++函数时,决定是否增长进度条,直到到达标尺的最后停止。
class progress_display : private noncopyable
{
public:
explicit progress_display( unsigned long expected_count,
std::ostream & os = std::cout,
const std::string & s1 = "\n", //leading strings
const std::string & s2 = "",
const std::string & s3 = "" )
: m_os(os), m_s1(s1), m_s2(s2), m_s3(s3) { restart(expected_count); }
void restart( unsigned long expected_count )
{
_count = _next_tic_count = _tic = 0;
_expected_count = expected_count;
m_os << m_s1 << "0% 10 20 30 40 50 60 70 80 90 100%\n"
<< m_s2 << "|----|----|----|----|----|----|----|----|----|----|"
<< std::endl // endl implies flush, which ensures display
<< m_s3;
if ( !_expected_count ) _expected_count = 1;
}
unsigned long operator+=( unsigned long increment )
{
if ( (_count += increment) >= _next_tic_count ) { display_tic(); }
return _count;
}
unsigned long operator++() { return operator+=( 1 ); }
unsigned long count() const { return _count; }
unsigned long expected_count() const { return _expected_count; }
private:
std::ostream & m_os;
const std::string m_s1;
const std::string m_s2;
const std::string m_s3;
unsigned long _count, _expected_count, _next_tic_count;
unsigned int _tic;
void display_tic()
{
unsigned int tics_needed =
static_cast<unsigned int>(
(static_cast<double>(_count)/_expected_count)*50.0 );
do { m_os << '*' << std::flush; } while ( ++_tic < tics_needed );
_next_tic_count =
static_cast<unsigned long>((_tic/50.0)*_expected_count);
if ( _count == _expected_count ) {
if ( _tic < 51 ) m_os << '*';
m_os << std::endl;
}
}
};
这个类代码也比较简单,有几个data member需要说明一下:
_count
标定现在计数到什么位置。
_expected_count
标定计数可以到达的最大数。
_next_tic_count
下个要打进度标志的计数位置。
…
…
创建对象的时候,调用restart函数,显示出标尺,标尺用m_s1,m_s2,m_s3标定标尺的显示规格。
然后,每次进度条需要增加的时候调用menber function:operator+=(),在这个函数中,_count += increment) >= _next_tic_count,增加_count,看是否到达下一个需要对进度条增加的位置,如果是,调用menber function:display_tic()。
而这个类理解的最困难的地方就在于display_tic(),这个函数做了几件事,首先是显示这次调用函数时,应该显示的进度条,计算出下次进度条增加的位置,当进度条也就是_count的计数到达最大值的时候,判断是否打最后一个进度条上的点。
在我们使用这个类的时候,需要的就是创建个对象,在该增加进度条的时候调用operator++()就行了。用起来也比较方便。
本文地址:http://com.8s8s.com/it/it28933.htm