Subject: Re: [boost] [gsoc 2013] draft proposal for chrono::date
From: Vicente J. Botet Escriba (vicente.botet_at_[hidden])
Date: 2013-05-03 12:06:29
Le 03/05/13 16:58, Howard Hinnant a écrit :
> I have been reading with interest this thread, but have been sitting on my hands until now. After all this is a gsoc project for Anurag, not for me or anyone else.
> But as long as everyone seems free to throw in their design preferences, I will add mine, several of which I've already seen repeated here, but some not.
> 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 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).
my guess with no_check was to make it more difficult to build an
unvalidated date than a validated one. Wouldn't an beginner user start
using the unchecked constructors before the factory functions?
> Checked interface:
> I like the use of factory functions here. I also like an easy-to-remember, convenience, forgiving, type-safety. 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;
> These 3 orders are chosen among the possible 6 because these are the 3 that people actually use:
> The first two units have to explicit. The last unit can 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.
> 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.
Again I proposed these functions for the no_check date construction.
Nothing to be with the checked one. Anyway I don't like them more that
the no_check constructor.
I like the factories for the check interface.
> 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.
I would expect that month and year could be check its validity so no
check is needed when using constant objects. BTW, Howard, could you
comment on my post about date literals for day and year?
> 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.
> This design follows the philosophy of the containers, and whether or not any given container has an operator(size_t), or a size() member. If you need an operation, and it isn't available on the container you have, you just have to move your data to a container that supports it. This aids in exposing performance bugs. It makes expensive operations explicit.
This is a reasonable argument. I have taken the option to provide on all
the date classes the same interface. Maybe both can be conciliated.
* First level: provides only the operations that are efficient with the
used representation. The user must do the needed conversions to obtain
whatever is needed.
* Second level: build an adaptor that models a Date concept
independently of the underlying type. The adaptor need to make
internally the conversions to provide the Date functions.
* If a Date concept is proposed, we (the user/boost/or the standard) can
provide other alternative dates that could be more efficient than the
> Both checked and unchecked interfaces should be made constexpr. day_point is already constexpr (in C++14). The field date-type should be constexpr.
> Anurag, please feel free to adopt or ignore any or all of this. I can also offer services in the creation of constexpr field<->serial conversions if that would be helpful. Best of luck in your gsoc!
So the cached serial functionality would be hidden on the field-serial
conversions. As the and so on the Field date-type, isn't it?
BTW, what about the relative date functionalities? Where should them be
implemented? On the field based date? Or on a specific relative_date
class that is convertible to ymd_date? I 'm all for a relative_date
class, so that the additional functionality doesn't incur on
performances penalties of the ymd date.
Thanks Howard for your comments,
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk