Boost logo

Boost :

Subject: Re: [boost] [chrono/date] Two axes for a date design: validity check and representation and an essence date class/factory
From: Vicente J. Botet Escriba (vicente.botet_at_[hidden])
Date: 2013-05-09 06:11:26

Le 08/05/13 14:52, Stewart, Robert a écrit :

Thanks Rob for your comments and suggestions.
Note that I'm not YET suggesting yet a single class date with
representation and validity parameters and yes the essence concept is
represented by the unchecked dates as I said in a reply to my own post.
> Vicente J. Botet Escriba wrote:
>> Le 07/05/13 12:46, Vicente J. Botet Escriba a écrit :
>>> I would like to discuss about some alternatives to take in
>>> account the following use case "Reset a date if it resulting
>>> date is valid and report if this succeeds" when we have two
>>> axes of variation on the date design.
>>> Let me add two parameters to the possible date class.
>>> * validity: unchecked/checked
>>> * representation: serial, ymd(calendar), iso_ordinal,
>>> iso_week...
>>> date<serial, unchecked> is an unchecked serial date. It
>>> corresponds +/- to the N3344 date suggestion.
>>> date<ymd, checked> is a checked ymd date. It corresponds
>>> +/- to the H.H 2011 date proposal.
>>> WARNING !!! I'm not saying that a template class is a good
>>> thing, it is just to state clearly the features of the date.
>>> N3344 suggest a date::set_if_valid_date function.
>>> date<serial, unchecked> ud=...;
>>> if (ud.set_if_valid_date(y,m,d)) {
>>> // xx
>>> } else {
>>> // yy
>>> }
>>> But if we want to do as for the date constructors we will
>>> need multiple overloads again for this function :(.
> That possibly modifies ud. Spelling it "try_set" will be clearer
> (and shorter!). The benefit of this approach is that the
> checking is not wasted; the result is stored in ud. The downside
> is that ud is still unchecked, so the checked characteristic is
> lost except from context.
I wanted to get rid of this set_if_valid_date, but I think it is the
clearer and more concise of all the other alternatives. I agree with
renaming it.
The parameter could be (any other?) an unchecked date.

bool date::try_set(date<R, uncheked>);

When used together with factories the result is quite readable

if (ud.try_set(m/d/y)) {

> Thus, creating a date<serial, checked> from a date<serial, unchecked> would be ideal, but hardly fits as a member function of the latter.
Both conversion must be supported. Whether this conversions are implicit
or explicit would depend on the representation.
>>> As show on this ML serial+unchecked dates validation could
>>> have some surprises.
>>> We can get the same service building a ymd date that can
>>> provide a way to be validated.
>>> //-------------
>>> // Validity check done when is_valid() is called on
>>> ymd_date.
>>> date<serial, unchecked> dd=...;
>>> date<ymd, unchecked> yd(y,m,d);
>>> if (yd.is_valid()) { // CHECK
>>> dd = yd;
>>> // xx
>>> } else {
>>> // yy
>>> }
> That should probably be in addition to try_set(). That is, both
> have their uses, so choosing between them is problematic.
>>> Instead of using explicitly date<ymd,unchecked> we can use
>>> a make_essence() factory that returns some kind of tuple
>>> essence_date<year,month,day> that is not a date, but its
>>> essence. It contains everything needed to build a date.
> That sounds like Howard's ymd struct idea. The latter name is
> certainly more convenient and specific than "essence".
> Nevertheless, I understand that you're trying to be abstract
> here.
>>> This essence should be implicitly convertible to any date.
>>> The make_essence_date() factory supports all the different
>>> orders of parameters.
> Presumably, that also would include day-of-year, week-of-year,
> and other, similar constructs.
Of course.
>>> The operator/() expression would also create an essence
>>> date.
>>> // --------------
>>> //No validity check is done.
>>> date<serial, unchecked> ud = make_essence(y,m,d);
>>> date<serial, unchecked> ud1 = m/d/y;
> If s/make_essense/make_date/, that looks very workable.
>>> This essence dates support date validity, that is provides
>>> a is_valid() function.
>>> // --------------
>>> // Validity check done when is_valid() is called on
>>> essence.
>>> date<serial, unchecked> ud;
>>> auto d = y/m/d;
>>> if (d.is_valid()) { // CHECK
>>> ud = d;
>>> // xx
>>> } else {
>>> // yy
>>> }
>>> //--------------
>>> // Validity check done at construction of cd.
>>> serial_date<checked> cd = make_essence(m,d,y);
>>> //serial_date<checked> cd = m/d/y;
> That makes sense. serial_date<checked>(essence) can call
> essence::is_valid(), so the validity checking logic is only one
> place.
Yes, and can be checked at construction.
>>> Summary: IMO
>>> Having checked and unchecked dates with several
>>> representations could help to make dates interface more
>>> precise and concise.
>>> Checked dates are always valid.
>>> Dates with a specific representations would provide only,
>>> as suggested by Howard, the functions that are efficient.
>>> The use of essence dates factories simplify the interface
>>> of all the date classes construction. Only 1 constructor by
>>> representation needed independent of the parameters order
>>> date(essence<ymd> &&);
>>> date(essence<serial> &&);
>>> date(essence<iso_ordinal> &&);
>>> date(essence<iso_week> &&);
>> After doing some trials, it seems that the essence essence<T>
>> class is equivalent to date<T, unchecked> :).
> Here you've taken an unexpected turn, though looking back at the
> options you mentioned for the representation axis, it shouldn't
> have been unexpected. I was thinking your make_essence() would
> take various arguments, including day-of-year and similar special
> cases, convert them to ymd values, and the various date classes
> would construct from that information.
No I didn't wanted to reserve it to ymd representation . The essence is
whatever information defining a date but not checked, so yes it could be
any unchecked date.
> ymd is likely not the best intermediate format of the information
> for every representation. A date<iso_week,*> date would convert
> trivially from an essence<iso_week>, so if the make_essence()
> overload, that builds an iso_week value, returns an
> essence<iso_week>, then creating a date<iso_week,*> can be
> optimally efficient.
This is the idea.
> The approach seems good. Each date class can focus on its best
> features and leave others to other classes.
I hope we all agree on this point.
> There's one piece
> missing, however. You haven't discussed date type conversions.
> [From this point, I'll s/essence<Rep>/date<Rep,unchecked>/ to
> correspond to your findings and avoid the awkward name
> "essence".]
Consider it already done ;-)
> Should date<Rep> be the intermediary for conversions, too? That
> is, does each date type provide a get_rep() accessor that returns
> a date<Rep,unchecked> from which another date type can be
> constructed?
I don't see the need. A date can be converted from any other date,
without needing to get the representation.
> Can we make such conversions implicit?
Not all the conversions should be implicit. Howard has suggested that we
can start with explicit conversions except the calendar->serial. If we
provide ordinal_date or other date representation, the conversion from
calendar_date to all the other should be implicit.
> With
> delegating constructors, I think so:
> struct some_date_type
> {
> some_date_type(date<ymd,unchecked> const &);
> ...
> template <class Date>
> some_date_type(Date const & _other)
> : some_date_type(_other.get_rep())
> {
> }
> date<my_ideal_rep,unchecked>
> get_rep() const;
> };
I don't see yet the need for this get_rep() function.

> That means that no date type need know about the rest, yet they
> all can interconvert. Even user-defined date types can
> participate in this interoperably. date<Rep,unchecked> could be
> extended for a user-defined representation, but the standard date
> types need some help to convert from it. You'd start with a
> customization point:
> template <class Rep, class Checking, class Rep2>
> date<Rep,Checking>
> make_date(date<Rep2,unchecked> const &);
Your make_date factory is now a converter. We can go this way but this
remember my Boost.Conversion library review. I guess that it would be
better to avoid all the troubles this kind of customization points can
raise. Instead we can have a hierarchy of dates. The new added dates
should provide the conversion to all the other.
> Then, the standard date types need an appropriate constructor:
> template <class Rep>
> some_date_type(date<Rep,unchecked> const & _date)
> : some_date_type(make_date<MyRep,MyChecking>(_date))
> {
> }
As I said, I'm not suggesting yet a date template class. I have not
taken the time to see if there is something that could be obtained with it.
> So long as make_date() is specialized for a particular Rep/Checking/Rep2 tuple, then a standard date type can be constructed from a date<Rep2,unchecked>.
> Interestingly, given that customization point, the date classes now only need a move constructor and that templated constructor to support all representations. That is, all of the conversion work moves to the make_date() specializations.
> Indeed, a standard date class can look like the following yet support all of the argument combinations and interconversions:
> template <class Rep, checking_type Checking>
> struct date
> {
> date(date const &);
> date(date &&);
> template <class R, class C>
> date(date<R,C> const & _date)
> : date(static_cast<date<R,unchecked>>(_date))
> {
> }
> template <class R>
> date(date<R,unchecked> const & _date)
> : date(make_date<Rep,Checking>(_date))
> {
> }
> date &
> operator =(date const &);
> date &
> operator =(date &&);
> template <class R, class C>
> date &
> operator =(date<R,C> const & _date)
> {
> date(_date).swap(*this);
> }
> explicit operator date<Rep,unchecked>() const;
> };
> Of course, that means the only way to create an arbitrary date
> type from a particular argument list is via make_date(), so other
> constructors may be desirable.
You know that I would like we can do this kind customization without
having ODR issues, but I suspect we cannot.


Boost list run by bdawes at, gregod at, cpdaniel at, john at