Boost source code analysis notes

Boost源码分析笔记

Recently, I’ve been reading the Boost code and writing some analyses on the usage and implementation of useful modules in the Boost libraries, with updates to come periodically.

Note: When using Boost, don’t be lazy and directly use using namespace std;, as there are many name collisions with the standard library, and the introduction of namespace is precisely to solve this problem. Reasonable and correct use of namespaces is what a qualified cpp programmer should do.

Timer

The current version of Boost (1.62) includes two versions of timer: one is timer (v1), which is implemented using C/C++ library functions and provides low precision (relying on the operating system or compiler); the timer does not require linking libraries, just include <boost/timer.hpp>; the second is cpu_timer (v2), which uses the API of the operating system based on the chrono library, offering higher precision timing.

timer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>
#include <boost/timer.hpp>

using namespace std;
using namespace boost;

int main(int argc,char* argv[])
{
timer t;
cout<<t.elapsed_max()<<endl;
cout<<t.elapsed_min()<<endl;
cout<<t.elapsed()<<endl;
return 0;
}

The timer in Boost is implemented by calling the C/C++ library function clock():

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class timer
{
public:
timer() { _start_time = std::clock(); } // postcondition: elapsed()==0
// timer( const timer& src ); // post: elapsed()==src.elapsed()
// ~timer(){}
// timer& operator=( const timer& src ); // post: elapsed()==src.elapsed()
void restart() { _start_time = std::clock(); } // post: elapsed()==0
double elapsed() const // return elapsed time in seconds
{ return double(std::clock() - _start_time) / CLOCKS_PER_SEC; }

double elapsed_max() const // return estimated maximum value for elapsed()
// Portability warning: elapsed_max() may return too high a value on systems
// where std::clock_t overflows or resets at surprising values.
{
return (double((std::numeric_limits<std::clock_t>::max)())
- double(_start_time)) / double(CLOCKS_PER_SEC);
}

double elapsed_min() const // return minimum value for elapsed()
{ return double(1)/double(CLOCKS_PER_SEC); }

private:
std::clock_t _start_time;
}; // timer

And std::clock() is defined in ctime.h in C++:

1
std::clock_t clock(void);

Where std::clock_t is also defined in ctime.h.

Defined in header
typedef /* unspecified */ clock_t;
Arithmetic type capable of representing the process running time of implementation-defined range and precision.

In fact, the above code calling timer is equivalent to:

1
2
3
4
5
6
7
8
9
10
11
#include <iosteram>
#include <ctime.h>
using namespace std;

int main(void){
clock_t start=clock();
// elapsed_max and elapsed_min are calculated similarly
// elapsed
clock_t end=clock()-start/CLOCKS_PER_SEC;
return 0;
}

Note: The number of clock ticks per second is defined by the macro CLOCKS_PER_SEC, which varies across different operating systems. On Win32, it’s 1,000 (with a timing precision of 1s/1,000=1ms), while on Linux, it’s 1,000,000 (with a timing precision of 1s/1,000,000=1μs).

progress_timer

progress_timer inherits from the timer class, thus having all member functions of the timer class (the interface is the same as timer). We can perform any operation on a progress_timer object that can be done on a timer object.

The constructor of progress_timer consists of the constructor of timer + the constructor defined in progress_timer. When constructing progress_timer, we need to pass an IO stream object to output the time to that stream upon destruction. The default is std::cout, but other standard output streams (ofstream/ostringstream) can be used alternatively, or the output of cout can be redirected using cout.rdbuf().

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public:
explicit progress_timer( std::ostream & os = std::cout )
// os is hint; implementation may ignore, particularly in embedded systems
: timer(), noncopyable(), m_os(os) {}
~progress_timer()
{
// A) Throwing an exception from a destructor is a Bad Thing.
// B) The progress_timer destructor does output which may throw.
// C) A progress_timer is usually not critical to the application.
// Therefore, wrap the I/O in a try block, catch and ignore all exceptions.
try
{
// use istream instead of ios_base to workaround GNU problem (Greg Chicares)
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" // "s" is System International d'Unites std
<< std::endl;
m_os.flags( old_flags );
m_os.precision( old_prec );
}
catch (...) {} // eat any exceptions
} // ~progress_timer
private:
std::ostream & m_os;

As can be seen, when we construct a progress_timer, the timing starts automatically (timer::timer()), and when the progress_timer is destructed, it outputs the time elapsed from construction to destruction.

progress_display

progress_display can show the execution progress of the program in the console, but I think this class is pretty useless, so I won’t elaborate…

date_time

Boost has a date_time library to handle time, and date_time needs to be compiled to use, requiring linking to the boost_date_time library during compilation. The date_time consists of two parts: gregorian for handling dates and posix_time for handling time.

More about the interfaces of date_time in Boost can be found here: Chapter 10. Boost.Date_Time

date_time basically covers our needs for calculating between dates, but the date in the date_time library is based on the Gregorian calendar, supporting only dates from 1400-01-01 to 9999-12-31.

Without further ado, let’s look at some simple and common usages… More usages can be found in the Boost documentation or “The Complete Development of the Boost Library”.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <iostream>
#include <boost/date_time/gregorian/gregorian.hpp>

int main(int argc,char* argv[])
{
boost::gregorian::date testDate(2016,10,19);

// Check if this is a valid date
if(!testDate.is_not_a_date())
{
std::cout<<"yes"<<std::endl;
}else{
std::cout<<"error"<<std::endl;
}
// Accessing the date
std::cout<<testDate.year()<<"-"<<testDate.month()<<"-"<<testDate.day()<<std::endl;
std::cout<<testDate<<std::endl;
// Calculate the interval between two dates
boost::gregorian::date init(1994,11,11);
boost::gregorian::date now(2016,10,19);
std::cout<<now-init<<std::endl;

// Date calculations
// boost::gregorian::date test(2016,10,19);
// years()/months()/days() are defined in boost::gregorian namespace
// tomorrow
boost::gregorian::date tomorrow=now+boost::gregorian::days(1);
std::cout<<tomorrow<<std::endl;
// next month
boost::gregorian::date nextMonth=now+boost::gregorian::months(1);
std::cout<<nextMonth<<std::endl;
// next year
boost::gregorian::date nextYear=now+boost::gregorian::years(1);
std::cout<<nextYear<<std::endl;

return 0;
}

Some section code of the date class in date_time:

For more details, see boostcode/date_time/date.hpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
template<class T, class calendar, class duration_type_>
class date : private
boost::less_than_comparable<T
, boost::equality_comparable<T
> >
{
private:
typedef T date_type;
typedef calendar calendar_type;
typedef typename calendar::year_type year_type;
typedef typename calendar::month_type month_type;
typedef typename calendar::day_type day_type;
typedef typename calendar::ymd_type ymd_type;
public:
// Constructor
date(year_type y, month_type m, day_type d)
: days_(calendar::day_number(ymd_type(y, m, d)))
{}
date(const ymd_type& ymd)
: days_(calendar::day_number(ymd))
{}

// Member functions
year_type year() const
{
ymd_type ymd = calendar::from_day_number(days_);
return ymd.year;
}
month_type month() const
{
ymd_type ymd = calendar::from_day_number(days_);
return ymd.month;
}
day_type day() const
{
ymd_type ymd = calendar::from_day_number(days_);
return ymd.day;
}
bool is_not_a_date() const
{
return traits_type::is_not_a_number(days_);
}

// Operator overloading
date_type operator-=(const duration_type& dd)
{
*this = *this - dd;
return date_type(days_);
}
date_rep_type day_count() const
{
return days_;
}
// allow internal access from operators
date_type operator+(const duration_type& dd) const
{
if(dd.is_special())
{
return date_type(date_rep_type(days_) + dd.get_rep());
}
return date_type(date_rep_type(days_) + static_cast<date_int_type>(dd.days()));
}
date_type operator+=(const duration_type& dd)
{
*this = *this + dd;
return date_type(days_);
}

};

Smart Pointer

The article is finished. If you have any questions, please comment and communicate.

Scan the QR code on WeChat and follow me.

Title:Boost source code analysis notes
Author:LIPENGZHA
Publish Date:2016/10/19 21:39
Word Count:2.9k Words
Link:https://en.imzlp.com/posts/18194/
License: CC BY-NC-SA 4.0
Reprinting of the full article is prohibited.
Your donation will encourage me to keep creating!