Rebuilding Boost Date-Time For C++11 - Jeff Garland - CppCon 2014
Rebuilding Boost Date-Time For C++11 - Jeff Garland - CppCon 2014
Jeff Garland
[email protected]
1
Alternate title:
!
2
What Will This Talk Achieve?
• Examination of C++11 & 14 features for building
ValueType classes
• Preview of date-time v2
3
Background
• boost date-time (bdt)
• v1 put into boost in 1.29 in 2002
• used in many, many projects
• Good
• Bad
• I/O 4
- facet based strftime based interfaces is relatively slow
//bdt v1
using namespace boost::gregorian;
date weekstart(2002,Feb,1); //obvious construction
//math
date weekend = weekstart + week(1);
date d2 = d1 + days(5);
//clock
date today = day_clock::local_day();
if (d2 >= today) {} //all the usual comparison operators
!
5
date-time & chrono
• BDT v1 the basis is elements in <chrono>
• Excuses :)
6
•
C++11/14 Features Covered
Design considerations for the ‘date’ class (mostly)
• Specifically c++11/14 feature considerations (language and library)
• final
• noexcept (error handling)
• move construction/assignment (R-values)
• constexpr
• member initialization
• enum types
• user defined literals
• template aliases
• std::to_string
• delegating constructors
• Also, general class considerations
• default construction
• templated construction / conversion 7
Time to Start Over!
!
Reconsider everything…
8
What’s easiest to use type in
C++?
• int!
• as easy as int
• or as fast
9
Properties of an Int
• Hard to break
• overflow on arithmetic
11
C++11/14 Features 1 by 1
12
final
13
date as final class
class date final {…..};
!
!
class mydate : public boost::date_time2::date { }
14
final - the final word
15
//bdt v1
using namespace boost::gregorian;
date weekstart(2002,Feb,1); //obvious construction
//math
date weekend = weekstart + week(1);
date d2 = d1 + days(5);
//clock
date today = day_clock::local_day();
if (d2 >= today) {} //all the usual comparison operators
!
16
//input streaming
std::stringstream ss("2004-Jan-1");
ss >> d3;
17
Do you see the problem?
18
It’s all about construction
• default constructors
• templated constructor
19
!
!
date d3;
d3 += days(2); //value?
20
It’s all about construction
default constructor
• Should there be a default constructor?
21
you’re so special - a
diversion
• v1 had special values for date - nadt, neg_infinity, pos_infinity
• gone in v2
• advantages
• disadvantages
• i/o is harder…
• advantages
• disadvantages
• most don’t…
• complexity…again
23
!
!
date d3;
d3 += days(2); //v2 == epoch() + 2 days
24
It’s all about construction
move construction?
• Should there be a move constructor?
25
It’s all about construction
26
noexcept
date(const year_month_day& ymd) noexcept;
date(const year_month_day& ymd, checking check);
27
It’s all about construction
member initialization
• Allows class/struct data members to be explicitly
initialized
28
//user code…
iso_week_number wn;
//library code…
struct iso_week_number {
/** Construct to invalid state */
iso_week_number() noexcept = default;
!
…
!
uint16_t year = 10001;
uint8_t week_no=54; ///<use iso week numbering
uint8_t day_in_week=8; ///<1==monday...7==sunday
29
Date - How do I represent
thee?
• Strings — a gazillion variations
• localization anyone?
• modified julian day &
• iso julian day
• month, day, year
templated construction
31
std::tm
//v1
//v2
tm d_tm;
tm t;
d_tm.tm_year = 105;
t.tm_year = 113;//2013
d_tm.tm_mon = 0;
t.tm_mon = 11; //Dec
d_tm.tm_mday = 1;
t.tm_mday = 30;
date d = date_from_tm(d_tm);
date d(t);
32
std::tuple & chrono
//v2 only — can’t write in v1
std::tuple<int, int, int> t_ymd(1900, 1, 1);
date d(t_ymd);
33
!
//v2 api
using namespace boost::date_time2;
date d(2012, 1, 1);
year_month_day ymd(2012, 1, 1);
date d2(ymd);
!
date d1(year_month_day(“20140401T000000”), ISO);
!
date d2(year_month_day(“2014-04-01”));
date d1(year_month_day(“2004-10-01"));
date d2(year_month_day("2004/10/01"));
//calculated dates
day_of_week dow(First, Wed, Jan, 2013 );
date d(dow);
!
date d(day_of_year(2014, 1)); //jan 1, 2014
!
closest_day_of_week pdw(Sun, Before, 2013, May, 17);
! date d(pdw);
34
iso_week_number wn1(“2014-W01-2");
date d1(wn1);
!
iso_week_number wn2("2014", "W1", “2");
date d2(wn2);
!
iso_week_number wn3(2014, 1, 5);
date d3(wn3); //2014-Jan-3
35
How is it done?
• Templated constructor
• Alternative
36
Under the Hood
//declaration…
template<typename T>
explicit date(const T& t) noexcept;
38
user defined literal example
#include <chrono>
#include <iostream>
using namespace std::chrono;
using namespace std::literals::chrono_literals;
39
Nice, but…
• Standard limits user defined literals
• _w for week?
• Easy to implement?
40
snippet of chrono ‘h’
operator
constexpr chrono::duration<long double, ratio<3600,1>>
operator""h(long double __hours)
{ return chrono::duration<long double, ratio<3600,1>>{__hours}; }
!
template <char... _Digits>
constexpr typename
__select_type::_Select_type<__select_int::_Select_int<_Digits...>::value,
chrono::hours>::type
operator""h()
{
return __select_type::_Select_type<
__select_int::_Select_int<_Digits...>::value,
chrono::hours>::value;
}
41
what’s wrong with this?
constexpr boost::date_time2::days operator""_d(short d)
{
return boost::date_time2::days(d);
}
42
still no joy…
constexpr days operator”"_d(unsigned long long d)
{
return days(d);
}
error…!
!
boost::date_time2::days’ is not an aggregate, does not have a trivial
default constructor, and has no constexpr constructor that is not a copy
or move constructor
43
Remove the constexpr for
now
constexpr days operator”"_d(unsigned long long d)
{
return days(d);
}
d+= 1_w;
• ???
44
constexpr
• Generalized constant expression
• Local variables
45
obvious constexpr
46
constexpr limits
47
date::max_date
48
well, maybe not…
g++-4.8 -I ~/devtools/boost_1_55_0 -std=c++11 test.cpp
In file included from test.cpp:6:0:
date.hpp: In static member function ‘static constexpr boost::date_time2::date
boost::date_time2::date::max_date()’:
date.hpp:85:29: error: invalid return type ‘boost::date_time2::date’ of constexpr function ‘static constexpr
boost::date_time2::date boost::date_time2::date::max_date()’
static constexpr date max_date()
^
date.hpp:78:11: note: ‘boost::date_time2::date’ is not literal because:
class date : public date_base<gregorian_calendar>
^
date.hpp:78:11: note: ‘boost::date_time2::date’ is not an aggregate, does not have a trivial default
constructor, and has no constexpr constructor that is not a copy or move constructor
49
std::to_string
50
using to_string - generic range check
}
}
51
delegating constructors
52
delegating constructor
class year_month_day {
public:
year_month_day(const char* const ymd_string);
template<typename T>
explicit year_month_day(const T& ymd);
!
template<>
year_month_day::year_month_day(const std::string& ymd_string) :
year_month_day(ymd_string.c_str())!
{}
53
Building Valuetypes in C+
+11/14
!
• It’s a whole new world…
• to_string
• noexcept
• member initialization
• constexpr
• delegating constructor
• questionable value
• final
54
Conclusions: Valuetype
Design Considerations
55
Conclusion
• C++11 and 14 provide nice features for writing value types
• Other resources
56