Subject: Re: [boost] [gsoc 2013] draft proposal for chrono::date
From: Rob Stewart (robertstewart_at_[hidden])
Date: 2013-05-04 07:57:29
On May 3, 2013, at 10:58 AM, Howard Hinnant <howard.hinnant_at_[hidden]> wrote:
> Unchecked interface:
> date(year y, month m, day d);
> This is the only order, there is no checking, the year, month and date objects do no validation themselves. They exist only to disambiguate the order of this low level constructor. This single order is chosen above any other because of the ISO standards. The use of the year, month and day objects add some type safety with no run time overhead, and so are acceptable. Each of these objects will *explicitly* convert from int:
> date(year(2013), month(5), day(3)); // ok
> date(2013, 5, 3); // compile time error
I see we agree on the use of day, month, year, but I don't agree with a single order. More on that below.
> I dislike the use of no_check_t at this level because I see it as unnecessarily verbose and ugly, and I'll have to look up how to spell it every time I use it (see the checked interface for why).
The use of an extra tag argument, creating a separate overload set for use when checking isn't needed, doesn't seem a bad approach.
> Checked interface:
> I like the use of factory functions here. I also like an easy-to-remember, convenience, forgiving, type-safety.
We agree thus far.
> My still-preferred spelling for the factory functions (yes, more than 1) is:
> date d = year(2013) / month(5) / day(3);
> date d = year(2013) / month(5) / 3;
> date d = month(5) / day(3) / year(2013);
> date d = month(5) / day(3) / 2013;
> date d = day(3) / month(5) / year(2013);
> date d = day(3) / month(5) / 2013;
To implement those, you must either introduce intermediate types, like year_and_month, with and overloaded operator /, or you need expression templates to shuffle the argument order before forwarding to a single, checked, date constructor.
The same is possible with overloaded date constructors:
date(year, month, day);
date(month, day, year);
date(day, month, year);
plus overloads permitting 1 of 3 to be and integer for each supported order.
> These 3 orders are chosen among the possible 6 because these are the 3 that people actually use:
That's a good rationale.
> The first two units have to explicit. The last unit can be implicit.
Overloaded constructors permit the first or second unit to be implicit.
> Other rules are possible, but this one is a nice compromise between convenience and simplicity. It is an easy rule to remember, and the rule is easily checked by the compiler at compile time.
My version is even easier, while still safe.
> I like this spelling of the checked factory function over:
> make_unchecked_date(2013, may, 3);
> make_valid_date(2013, may, 3);
> because I know I will remember how to spell the former 3 years from now, but I will have to look the latter up in a reference, unless I'm dealing with dates often enough that I memorize it.
Surely you'll remember the constructor spelling as easily. Given that, my suggestion is one overloaded factory function:
make_date(year, month, day);
This would have the same set of overloads as the date constructors, including the no_check_t (or unchecked_t parameter).
The result is easy to use and recall, efficient, and safe.
> In both interfaces, the year, month and day objects do no validity checking. The validity checks in the checked interface happen only at the time the full date is constructed.
> Two date types:
> 1. Serial:
> typedef duration<int, ratio_multiply<hours::period, ratio<24>>> days;
> typedef time_point<system_clock, days> day_point;
> day_point is the serial date type. This is nothing but integral arithmetic with no validity checking. It has some convenience conversions to/from the serial date_time type: system_clock::time_point, and the field-date type...
> 2. Field date-type:
> Something that stores year, month and day, maybe named date, maybe not. Provides conversions to and from day_point.
> These two date types are analogous to the two C date types: time_t and tm. I have come to the conclusion that two types are necessary because each has its strengths and weaknesses in terms of performance. Each type should offer only the operations which it can do fast. E.g. there is no month() accessor on the serial date type, only on the field date type. If you have serial and you need the month, convert to the field type, and then you have it.
While I understand the motivation, I do hope you're implying more encapsulation than for the C types.
(Sent from my portable computation engine)
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk