[chrono/date] conversion between concrete dates
Hi, as the conversion between concrete dates could be expensive I guess that these conversion must be explicit. But this has some consequences when used the implicit conversion was hidden a not efficient implementation, e.g. date ISO_week_start = mon <= jan/day(4)/y; jan/day(4)/y should be ymd_date as is the efficient representation. The date generator was declared as date operator<=(weekday wd, date x); but it works efficiently only for days_date. If we provide only the functions that are efficient we should declare it as days_date operator<=(weekday wd, days_date x); So the preceding expression would need an explicit conversion days_date ISO_week_start = mon <= days_date(jan/day(4)/y); Do we want to go on this direction? A radical alternative to the explicit construction, if we want to make evident that the conversion operation could be expensive, is to use a compute_ factory days_date ISO_week_start = mon <= compute_days_date(jan/day(4)/y); Best, Vicente P.S. the generator function is just an example needing explicit conversion.
On May 8, 2013, at 12:08 PM, Vicente J. Botet Escriba
Hi,
as the conversion between concrete dates could be expensive I guess that these conversion must be explicit.
But this has some consequences when used the implicit conversion was hidden a not efficient implementation, e.g.
date ISO_week_start = mon <= jan/day(4)/y;
jan/day(4)/y should be ymd_date as is the efficient representation.
The date generator was declared as
date operator<=(weekday wd, date x);
but it works efficiently only for days_date. If we provide only the functions that are efficient we should declare it as
days_date operator<=(weekday wd, days_date x);
So the preceding expression would need an explicit conversion
days_date ISO_week_start = mon <= days_date(jan/day(4)/y);
Do we want to go on this direction?
A radical alternative to the explicit construction, if we want to make evident that the conversion operation could be expensive, is to use a compute_ factory
days_date ISO_week_start = mon <= compute_days_date(jan/day(4)/y);
Best, Vicente
P.S. the generator function is just an example needing explicit conversion.
I think we should consider the route of an explicit conversion between the serial date and the ymd date, and see where field experience takes us. In some timings I did last weekend I was getting about 1.2ns for a field->serial conversion averaged over 200 years, and 18ns for a serial->field conversion averaged over the same range. I was just timing the conversion function, and not any validation checks. If experience holds that serial->field is 15X the cost of field->serial, a possibility is for a hybrid approach: implicit in one direction and explicit in the other. However I wouldn't take these numbers as fact. This is just one report from one machine and one implementation. Howard
On May 8, 2013, at 12:08 PM, Vicente J. Botet Escriba
wrote: Hi,
as the conversion between concrete dates could be expensive I guess that these conversion must be explicit.
But this has some consequences when used the implicit conversion was hidden a not efficient implementation, e.g.
date ISO_week_start = mon <= jan/day(4)/y;
jan/day(4)/y should be ymd_date as is the efficient representation.
The date generator was declared as
date operator<=(weekday wd, date x);
but it works efficiently only for days_date. If we provide only the functions that are efficient we should declare it as
days_date operator<=(weekday wd, days_date x);
So the preceding expression would need an explicit conversion
days_date ISO_week_start = mon <= days_date(jan/day(4)/y);
Do we want to go on this direction?
A radical alternative to the explicit construction, if we want to make evident that the conversion operation could be expensive, is to use a compute_ factory
days_date ISO_week_start = mon <= compute_days_date(jan/day(4)/y);
Best, Vicente
P.S. the generator function is just an example needing explicit conversion.
I think we should consider the route of an explicit conversion between the serial date and the ymd date, and see where field experience takes us. I agree. The implicit conversion from ymd dates is needed to maintain
Le 08/05/13 19:29, Howard Hinnant a écrit : the code readable.
In some timings I did last weekend I was getting about 1.2ns for a field->serial conversion averaged over 200 years, and 18ns for a serial->field conversion averaged over the same range. I was just timing the conversion function, and not any validation checks.
If experience holds that serial->field is 15X the cost of field->serial, a possibility is for a hybrid approach: implicit in one direction and explicit in the other. However I wouldn't take these numbers as fact. This is just one report from one machine and one implementation.
Could you share your test program so that I can run it with my implementation and on other platforms? Best, Vicente
On May 9, 2013, at 6:16 AM, "Vicente J. Botet Escriba"
Could you share your test program so that I can run it with my implementation and on other platforms?
Don't mind at all, though what I have at this point hardly counts as a good benchmark. It is more like back-of-the-envenlope testing:
// time encoding // about 3-5 clock cycles, 1.2 ns
const int Ymin = 1900;
const int Ymax = 2100;
volatile int k;
int count = 0;
auto t0 = std::chrono::high_resolution_clock::now();
for (int y = Ymin; y <= Ymax; ++y)
{
bool is_l = is_leap(y);
for (int m = 1; m <= 12; ++m)
{
int last = db[is_l][m] - db[is_l][m-1];
for (int d = 1; d <= last; ++d)
{
k = days_from(y, m, d);
++count;
}
}
}
auto t1 = std::chrono::high_resolution_clock::now();
typedef std::chrono::duration
Le 08/05/13 19:29, Howard Hinnant a écrit :
On May 8, 2013, at 12:08 PM, Vicente J. Botet Escriba
wrote: Hi,
as the conversion between concrete dates could be expensive I guess that these conversion must be explicit.
But this has some consequences when used the implicit conversion was hidden a not efficient implementation, e.g.
date ISO_week_start = mon <= jan/day(4)/y;
jan/day(4)/y should be ymd_date as is the efficient representation.
The date generator was declared as
date operator<=(weekday wd, date x);
but it works efficiently only for days_date. If we provide only the functions that are efficient we should declare it as
days_date operator<=(weekday wd, days_date x);
So the preceding expression would need an explicit conversion
days_date ISO_week_start = mon <= days_date(jan/day(4)/y);
Do we want to go on this direction?
A radical alternative to the explicit construction, if we want to make evident that the conversion operation could be expensive, is to use a compute_ factory
days_date ISO_week_start = mon <= compute_days_date(jan/day(4)/y);
Best, Vicente
P.S. the generator function is just an example needing explicit conversion.
I think we should consider the route of an explicit conversion between the serial date and the ymd date, and see where field experience takes us.
In some timings I did last weekend I was getting about 1.2ns for a field->serial conversion averaged over 200 years, and 18ns for a serial->field conversion averaged over the same range. I was just timing the conversion function, and not any validation checks.
If experience holds that serial->field is 15X the cost of field->serial, a possibility is for a hybrid approach: implicit in one direction and explicit in the other. However I wouldn't take these numbers as fact. This is just one report from one machine and one implementation.
Hi,
I have updated the test to use my current public interface.
I'm getting the following:
clang 3.2
* empty field->serial ~0.15ns.
* field->serial ~7.5ns.
* empty serial->field ~0.65ns.
* serial->field ~17.4ns.
gcc-4.8.0
* empty field->serial ~0.2ns.
* field->serial ~10.2ns.
* empty serial->field ~0ns.
* serial->field ~20.6ns.
I will check the field -> serial implementation to see why is much less
efficient than yours.
When I add validation on the source date format I get
clang 3.2
* empty field->serial ~6.3ns.
* field->serial ~13.4ns.
* empty serial->field ~1ns.
* serial->field ~17.9ns.
gcc-4.8.0
* empty field->serial ~7.5ns.
* field->serial ~15.7ns.
* empty serial->field ~1ns.
* serial->field ~21.7ns.
Best,
Vicente
==============
#include
On May 10, 2013, at 1:45 PM, "Vicente J. Botet Escriba"
I will check the field -> serial implementation to see why is much less efficient than yours.
I'll look into this too. I wouldn't have expected such a dramatic difference. It is possible my optimizer removed enough to invalidate the test. Howard
On May 10, 2013, at 1:45 PM, "Vicente J. Botet Escriba"
I have updated the test to use my current public interface.
I'm getting the following:
clang 3.2 * empty field->serial ~0.15ns. * field->serial ~7.5ns. * empty serial->field ~0.65ns. * serial->field ~17.4ns.
gcc-4.8.0 * empty field->serial ~0.2ns. * field->serial ~10.2ns. * empty serial->field ~0ns. * serial->field ~20.6ns.
I've redone my timings in a more careful manner and am getting results much closer to yours now. I'm still only timing the conversion functions, but this time I put the functions in a separate translation unit. Previously I had the conversion functions and the test loop, all visible to the compiler within the same translation unit. I also put in a "fake" conversion (out-of-line in the second translation unit) to measure the "empty" time. Here are my results: * empty field->serial ~2.4ns. * field->serial ~8.0ns. * field->serial - empty field->serial ~5.6ns * empty serial->field ~2.4ns. * serial->field ~16.9ns. * serial->field - empty serial->field ~14.5ns And now my ratio of serial->field / field->serial is down to 2.6. This is using clang++ -03 on a Core i5 2.8GHz. Howard
On May 10, 2013, at 1:45 PM, "Vicente J. Botet Escriba"
When I add validation on the source date format I get
clang 3.2 * empty field->serial ~6.3ns. * field->serial ~13.4ns. * empty serial->field ~1ns. * serial->field ~17.9ns.
gcc-4.8.0 * empty field->serial ~7.5ns. * field->serial ~15.7ns. * empty serial->field ~1ns. * serial->field ~21.7ns.
I've been experimenting with adding validation today. I'm guessing that all of your validation is in a translation unit hidden from the testing loop. Is that correct?
I've been putting my validation in a header because I want to make it constexpr, and constexpr stuff has weak linkage. The motivation for making it constexpr is that for any part of the validation that involves compile-time information, the validation happens at compile time.
And my first experiments today involve putting some of the validation back into the unit specifiers, in contrast to the direction I was heading earlier.
Specifically:
// invariants:
// 1 <= d_
class day
{
int d_;
static
constexpr
int
__attribute__((__always_inline__))
check_invariants(int d)
{
return 1 <= d ? d : throw bad_date{};
}
public:
constexpr
explicit
__attribute__((__always_inline__))
day(int d)
: d_(check_invariants(d))
{}
constexpr
__attribute__((__always_inline__))
operator int() const
{return d_;}
};
// invariants:
// 1 <= m_ && m_ <= 12
class month
{
int m_;
static
constexpr
int
__attribute__((__always_inline__))
check_invariants(int m)
{
return 1 <= m && m <= 12 ? m : throw bad_date{};
}
public:
constexpr
explicit
__attribute__((__always_inline__))
month(int m)
: m_(check_invariants(m))
{}
constexpr
__attribute__((__always_inline__))
operator int() const
{return m_;}
};
// invariants:
// none
class year
{
int y_;
public:
constexpr
explicit
__attribute__((__always_inline__))
year(int y)
: y_(y)
{}
constexpr
__attribute__((__always_inline__))
operator int() const
{return y_;}
constexpr
bool
__attribute__((__always_inline__))
is_leap() const
{return y_ % 4 == 0 && (y_ % 100 != 0 || y_ % 400 == 0);}
};
Because of a bug in clang (http://llvm.org/bugs/show_bug.cgi?id=12848) I've had to mark everything with always_inline to get the compiler to optimize it properly. But once done, it does the optimizations nicely.
Now the ymd_date (or whatever name) constructors can be carefully crafted to not re-validate information that is already known. For example if the ymd_date constructor takes a month (not an int), then there is no need for it to re-validate in the month at that point. month is known to be valid.
I've removed what I call "range checking", which means there is no validation on year.
Here is a partial implementation of what I'm testing for ymd_date:
class ymd_date
{
year y_;
month m_;
day d_;
static
constexpr
day
__attribute__((__always_inline__))
check_invariants(year y, month m, day d)
{
return m != 2 ?
(
d <= limit[m-1] ? d : throw bad_date{}
) :
(
y.is_leap() ? (d <= 29 ? d : throw bad_date{}) :
(d <= 28 ? d : throw bad_date{})
);
}
static
constexpr
day
__attribute__((__always_inline__))
check_invariants(year y, month_day md)
{
return md.month() != 2 || md.day() <= 28 || y.is_leap() ?
md.day() : throw bad_date{};
}
public:
constexpr
__attribute__((__always_inline__))
ymd_date(year y, month m, day d)
: y_(y),
m_(m),
d_(check_invariants(y_, m_, d))
{}
The class is holding objects of type year, month and date instead of 3 ints (or whatever) so that the invariants of the individual components are not compromised when storing into, or returning from the ymd_date (i.e. they don't have to unnecessarily undergo re-validation).
The ymd_date validator taking year, month and day doesn't have to validate the month, it is known to be valid. It doesn't have to validate the year, there is nothing to validate. It only has to validate the day. And it doesn't need to check that the day >= 1, the day constructor already took care of that.
My experiments with looking at assembly generated at -O3 is that if either the month or day is a compile-time object, the validation code is reduced. For example it is common for day to be the first of the month, or perhaps the 5th, or any other fixed number <= 28. When this happens, and I construct a:
ymd_date ymd(year(y), month(m), day(1));
I can see in the generated assembly that everything disappears except ensuring that 1 <= m <= 12. Similarly when only the month is compile-time information I'm seeing the constraint checking on d is simplified, especially for the case that the month is not feb.
But even when all three unit specifiers are run time information, when I run this through a field->serial conversion:
const int Ymin = 1900;
const int Ymax = 2100;
volatile int k;
int count = 0;
auto t0 = std::chrono::high_resolution_clock::now();
for (int y = Ymin; y <= Ymax; ++y)
{
for (int m = 1; m <= 12; ++m)
{
int last = days_in_month(y, m);
for (int d = 1; d <= last; ++d)
{
ymd_date ymd{year(y), month(m), day(d)};
k = days_from(ymd.year(), ymd.month(), ymd.day());
++count;
}
}
}
auto t1 = std::chrono::high_resolution_clock::now();
typedef std::chrono::duration
Le 12/05/13 00:22, Howard Hinnant a écrit :
On May 10, 2013, at 1:45 PM, "Vicente J. Botet Escriba"
wrote: When I add validation on the source date format I get
clang 3.2 * empty field->serial ~6.3ns. * field->serial ~13.4ns. * empty serial->field ~1ns. * serial->field ~17.9ns.
gcc-4.8.0 * empty field->serial ~7.5ns. * field->serial ~15.7ns. * empty serial->field ~1ns. * serial->field ~21.7ns. I've been experimenting with adding validation today. I'm guessing that all of your validation is in a translation unit hidden from the testing loop. Is that correct? Right. I've been putting my validation in a header because I want to make it constexpr, and constexpr stuff has weak linkage. The motivation for making it constexpr is that for any part of the validation that involves compile-time information, the validation happens at compile time. Agreed. I would like to make as many consexpr as possible, but the current limitations force to write too many auxiliary functions. Anyway, I have started to move toward this direction. And my first experiments today involve putting some of the validation back into the unit specifiers, in contrast to the direction I was heading earlier. Glad to see you come back. I suspect that we would be forced to implement both to get real performance tests. Specifically:
// invariants: // 1 <= d_ class day { int d_;
static constexpr int __attribute__((__always_inline__)) check_invariants(int d) { return 1 <= d ? d : throw bad_date{}; } Yeah. This is the kind of auxiliary functions that are needed. public: constexpr explicit __attribute__((__always_inline__)) day(int d) : d_(check_invariants(d)) {}
constexpr __attribute__((__always_inline__)) operator int() const {return d_;} };
Because of a bug in clang (http://llvm.org/bugs/show_bug.cgi?id=12848) I've had to mark everything with always_inline to get the compiler to optimize it properly. But once done, it does the optimizations nicely. OK. I would add this attribute to my code.
Now the ymd_date (or whatever name) constructors can be carefully crafted to not re-validate information that is already known. For example if the ymd_date constructor takes a month (not an int), then there is no need for it to re-validate in the month at that point. month is known to be valid.
I've removed what I call "range checking", which means there is no validation on year.
Here is a partial implementation of what I'm testing for ymd_date:
<snip>
The class is holding objects of type year, month and date instead of 3 ints (or whatever) so that the invariants of the individual components are not compromised when storing into, or returning from the ymd_date (i.e. they don't have to unnecessarily undergo re-validation). As the sizeof such a ymd_class is 3 times the minimum needed (32bits) , shouldn't this mean that a ymd_date type should be passed as const reference instead of by value?
The ymd_date validator taking year, month and day doesn't have to validate the month, it is known to be valid. It doesn't have to validate the year, there is nothing to validate. It only has to validate the day. And it doesn't need to check that the day >= 1, the day constructor already took care of that. The problem with this approach, as we discussed previously, is that the day/month constructors will check for a valid range. constexpr objects help to reduce the check cost, but don't avoid it in general.
My experiments with looking at assembly generated at -O3 is that if either the month or day is a compile-time object, the validation code is reduced. For example it is common for day to be the first of the month, or perhaps the 5th, or any other fixed number <= 28. When this happens, and I construct a:
ymd_date ymd(year(y), month(m), day(1));
I can see in the generated assembly that everything disappears except ensuring that 1 <= m <= 12. Similarly when only the month is compile-time information I'm seeing the constraint checking on d is simplified, especially for the case that the month is not feb.
But even when all three unit specifiers are run time information, when I run this through a field->serial conversion:
const int Ymin = 1900; const int Ymax = 2100; volatile int k; int count = 0; auto t0 = std::chrono::high_resolution_clock::now(); for (int y = Ymin; y <= Ymax; ++y) { for (int m = 1; m <= 12; ++m) { int last = days_in_month(y, m); for (int d = 1; d <= last; ++d) { ymd_date ymd{year(y), month(m), day(d)}; k = days_from(ymd.year(), ymd.month(), ymd.day()); ++count; } } } auto t1 = std::chrono::high_resolution_clock::now(); typedef std::chrono::duration
sec; auto encode = t1 - t0; std::cout << encode.count() / count << '\n'; std::cout << sec(encode).count() / count << '\n'; I'm seeing times that are only 0.1ns to 0.2ns slower. This information is preliminary. My optimizer may again be getting the best of me. But in this case, I do not believe I have the option of moving the validation out of the translation unit with the test loop since I believe that this really must be constexpr to take advantage of common cases like:
ymd_date ymd{year(y), month(m), day(1)}; I would comeback with my own results once I move my validation check constexpr. If the validation really is this cheap, this pulls the motivation for the unchecked field types. And I currently don't see a motivation for a checked serial type. Only an unchecked serial type make sense to me since the only thing that can go wrong with it is for it to move out of range. And that range can easily be made ridiculously large (+/- tens of thousands of years, if not millions of years). See my other post. This style of validation checking has renewed my interest in the month_day type. A month_day type can be created and validated once, and then the ymd_date object can be constructed multiple times with a run-time year and the fixed month_day type with a faster validation check than with separate year, month and day components:
static constexpr day __attribute__((__always_inline__)) check_invariants(year y, month_day md) { return md.month() != 2 || md.day() <= 28 || y.is_leap() ? md.day() : throw bad_date{}; }
constexpr __attribute__((__always_inline__)) ymd_date(year y, month_day md) : y_(y), m_(md.month()), d_(check_invariants(y_, md)) {}
And if month_day happens to be constexpr, and the day happens to be <=28, or the month happens to not be feb, this validation completely disappears at compile time. This is made possible because the month_day constructor has already non-reduntantly performed other parts of the validation (and at compile-time if the month_day is constexpr).
Yes, validated month_days could reduce a lot the checked ymd_date validation cost. If the number of days in a year_month can be know at constant time (a constexpr) the constructor from year_month + day could also be improved. I suspect that we need to implement checked and unchecked dates, the users are asking for date construction without any check. I would add a test field construction with a without validation to see the cost. Best, Vicente
Le 12/05/13 00:22, Howard Hinnant a écrit :
On May 10, 2013, at 1:45 PM, "Vicente J. Botet Escriba"
wrote: When I add validation on the source date format I get
clang 3.2 * empty field->serial ~6.3ns. * field->serial ~13.4ns. * empty serial->field ~1ns. * serial->field ~17.9ns.
gcc-4.8.0 * empty field->serial ~7.5ns. * field->serial ~15.7ns. * empty serial->field ~1ns. * serial->field ~21.7ns. I've been experimenting with adding validation today. I'm guessing that all of your validation is in a translation unit hidden from the testing loop. Is that correct?
I've been putting my validation in a header because I want to make it constexpr, and constexpr stuff has weak linkage. The motivation for making it constexpr is that for any part of the validation that involves compile-time information, the validation happens at compile time.
And my first experiments today involve putting some of the validation back into the unit specifiers, in contrast to the direction I was heading earlier.
Hi,
after making validation *constexpr* I'm getting better results, but
there is yet a difference.
checked: volatile ymd_date dt = ymd_date(year(y), month(m,
check), day(d,check), check);
unchecked: volatile ymd_date dt = ymd_date(year(y), month(m),
day(d));
clang 3.2
checked ymd 1.75081
unchecked ymd 0.0923393
ENCODE empty 0.0895197
gcc-4.8.0
checked ymd 1.42277
unchecked ymd 0.116654
ENCODE empty 0.113943
See the test bellow.
Best,
Vicente
============
const int Ymin = 1900;
const int Ymax = 2100;
void unchecked_ymd_dcl()
{
int count = 0;
auto t0 = boost::chrono::high_resolution_clock::now();
for (int y = Ymin; y <= Ymax; ++y)
{
bool is_l = year(y).is_leap();
for (int m = 1; m <= 12; ++m)
{
int last = month(m).days_in(is_l).count();
for (int d = 1; d <= last; ++d)
{
volatile ymd_date dt = ymd_date(year(y), month(m), day(d));
++count;
}
}
}
auto t1 = boost::chrono::high_resolution_clock::now();
typedef boost::chrono::duration
On May 12, 2013, at 5:44 AM, "Vicente J. Botet Escriba"
after making validation *constexpr* I'm getting better results, but there is yet a difference.
checked: volatile ymd_date dt = ymd_date(year(y), month(m, check), day(d,check), check); unchecked: volatile ymd_date dt = ymd_date(year(y), month(m), day(d));
clang 3.2 checked ymd 1.75081 unchecked ymd 0.0923393 ENCODE empty 0.0895197
gcc-4.8.0 checked ymd 1.42277 unchecked ymd 0.116654 ENCODE empty 0.113943
I haven't tracked this down yet. But here is what I have done:
I changed my test to look like this:
volatile int k;
constexpr int count = 100000000;
std::mt19937 eng;
std::uniform_int_distribution<> get_year(-30000, 30000);
std::uniform_int_distribution<> get_month(1, 12);
auto t0 = std::chrono::high_resolution_clock::now();
for (int c = 0; c < count; ++c)
{
#ifdef cy
constexpr year y(2013);
#else
const year y(get_year(eng));
#endif
#ifdef cm
constexpr month m(5);
#else
const month m(get_month(eng));
#endif
#ifdef cd
constexpr day d(12);
#else
# if defined(cy) && defined(cm)
constexpr year_month ym(y, m);
# else
const year_month ym(y, m);
# endif
std::uniform_int_distribution<> get_day(1, ym.days_in_month());
const day d(get_day(eng));
#endif
ymd_date ymd{y, m, d};
k = days_from(ymd.year(), ymd.month(), ymd.day());
}
auto t1 = std::chrono::high_resolution_clock::now();
typedef std::chrono::duration
On 2013-05-08 18:08, Vicente J. Botet Escriba wrote:
Hi,
as the conversion between concrete dates could be expensive I guess that these conversion must be explicit.
But this has some consequences when used the implicit conversion was hidden a not efficient implementation, e.g.
date ISO_week_start = mon <= jan/day(4)/y;
jan/day(4)/y should be ymd_date as is the efficient representation.
The date generator was declared as
date operator<=(weekday wd, date x);
Hi, Do you really want to allow/document/support/advocate the American date format in C++? Why not restrict the format to something close to ISO date format for ISO C++? That way you could introduce a new type, say ym_type and ym_type operator/(year y, month m); date operator/(ym_type ym, day d); Neither explicit nor implicit conversion necessary :-) Another question I have about this is: Is is really a good idea to use operator/()? It does prevent you from calculating ratios between periods, e.g. int ratio = year(3)/month(4); // ratio = 9 I'd therefore prefer another operator, e.g. operator << But to be completely honest: I don't see the benefit of constructing dates this way. Why not simply use a constructor? date(year, month, day); With C++11 you could then write: date ISO_week_start = mon <= { y, jan, day(4) }; That's the way I'd prefer :-) Regards, Roland
Le 11/05/13 11:54, Roland Bock a écrit :
On 2013-05-08 18:08, Vicente J. Botet Escriba wrote:
Hi,
as the conversion between concrete dates could be expensive I guess that these conversion must be explicit.
But this has some consequences when used the implicit conversion was hidden a not efficient implementation, e.g.
date ISO_week_start = mon <= jan/day(4)/y;
jan/day(4)/y should be ymd_date as is the efficient representation.
The date generator was declared as
date operator<=(weekday wd, date x); Hi,
Do you really want to allow/document/support/advocate the American date format in C++? Why not restrict the format to something close to ISO date format for ISO C++? That way you could introduce a new type, say ym_type and
ym_type operator/(year y, month m); date operator/(ym_type ym, day d);
Neither explicit nor implicit conversion necessary :-) We have suggested a more neutral factory
auto dt = make_date(year(2013), may, 11);
Another question I have about this is: Is is really a good idea to use operator/()? It does prevent you from calculating ratios between periods, e.g.
int ratio = year(3)/month(4); // ratio = 9
I'd therefore prefer another operator, e.g. operator <<
year,month and day are unit specifiers not periods (years, months, days). The ratio is obtained using int ratio = years(3)/months(4); // ratio = 9
But to be completely honest: I don't see the benefit of constructing dates this way. Why not simply use a constructor?
date(year, month, day);
With C++11 you could then write:
date ISO_week_start = mon <= { y, jan, day(4) }; I guess this depends on the type of {y, jan, day(4)} and the expected type of the rhs of the operator<= ? ??? operator <=(month, ???)
Note that we are talking of several dates types. { y, jan, day(4) } would be accepted where a ymd_date is expected. The operator <= is defined on serial_date as the algorithm is efficient only with this representation.
That's the way I'd prefer :-)
In order to support ymd_date ISO_week_start = mon <= { y, jan, day(4) }; we need to define operator <= for ymd_date or as a template, template <typename Date> Date operator <=(month,Date); but the implementation would do a conversion from Date to serial_date and return the conversion of the calculated serial_date to a Date. Best, Vicente
Le 11/05/13 15:13, Vicente J. Botet Escriba a écrit :
Le 11/05/13 11:54, Roland Bock a écrit :
On 2013-05-08 18:08, Vicente J. Botet Escriba wrote:
Hi,
as the conversion between concrete dates could be expensive I guess that these conversion must be explicit.
But this has some consequences when used the implicit conversion was hidden a not efficient implementation, e.g.
date ISO_week_start = mon <= jan/day(4)/y;
jan/day(4)/y should be ymd_date as is the efficient representation.
The date generator was declared as
date operator<=(weekday wd, date x); Hi,
Do you really want to allow/document/support/advocate the American date format in C++? Why not restrict the format to something close to ISO date format for ISO C++? That way you could introduce a new type, say ym_type and
ym_type operator/(year y, month m); date operator/(ym_type ym, day d);
Neither explicit nor implicit conversion necessary :-) We have suggested a more neutral factory
auto dt = make_date(year(2013), may, 11);
Another question I have about this is: Is is really a good idea to use operator/()? It does prevent you from calculating ratios between periods, e.g.
int ratio = year(3)/month(4); // ratio = 9
I'd therefore prefer another operator, e.g. operator <<
year,month and day are unit specifiers not periods (years, months, days). The ratio is obtained using
int ratio = years(3)/months(4); // ratio = 9
But to be completely honest: I don't see the benefit of constructing dates this way. Why not simply use a constructor?
date(year, month, day);
With C++11 you could then write:
date ISO_week_start = mon <= { y, jan, day(4) }; I guess this depends on the type of {y, jan, day(4)} and the expected type of the rhs of the operator<= ? ??? operator <=(month, ???)
Note that we are talking of several dates types.
{ y, jan, day(4) } would be accepted where a ymd_date is expected.
The operator <= is defined on serial_date as the algorithm is efficient only with this representation.
That's the way I'd prefer :-)
In order to support
ymd_date ISO_week_start = mon <= { y, jan, day(4) };
An alternative would be to define the constructor serail_date(year, month, day); and define the operator serial_date operator <=(month, serial_date); As for the time been ymd_date is implicit convertible, the preceding constructor goes in the same direction. So yes, you should be able to auto ISO_week_start = mon <= { y, jan, day(4) }; but not ymd_date ISO_week_start = mon <= { y, jan, day(4) }; Best, Vicente
we need to define operator <= for ymd_date or as a template,
template <typename Date> Date operator <=(month,Date);
but the implementation would do a conversion from Date to serial_date and return the conversion of the calculated serial_date to a Date.
On 2013-05-11 15:13, Vicente J. Botet Escriba wrote:
Le 11/05/13 11:54, Roland Bock a écrit :
On 2013-05-08 18:08, Vicente J. Botet Escriba wrote:
Hi,
as the conversion between concrete dates could be expensive I guess that these conversion must be explicit.
But this has some consequences when used the implicit conversion was hidden a not efficient implementation, e.g.
date ISO_week_start = mon <= jan/day(4)/y;
jan/day(4)/y should be ymd_date as is the efficient representation.
The date generator was declared as
date operator<=(weekday wd, date x); Hi,
Do you really want to allow/document/support/advocate the American date format in C++? Why not restrict the format to something close to ISO date format for ISO C++? That way you could introduce a new type, say ym_type and
ym_type operator/(year y, month m); date operator/(ym_type ym, day d);
Neither explicit nor implicit conversion necessary :-) We have suggested a more neutral factory
auto dt = make_date(year(2013), may, 11);
Yes, I like that much better. The main point I was trying to make is that using operators to mimic the way some people are used to writing a date is not necessarily the best idea. May/11/2013 11.Mai 2013 11.5.13 2013-05-11 ... There are just so many ways. Allowing such formats in code just adds to confusion, IMHO.
Another question I have about this is: Is is really a good idea to use operator/()? It does prevent you from calculating ratios between periods, e.g.
int ratio = year(3)/month(4); // ratio = 9
I'd therefore prefer another operator, e.g. operator <<
year,month and day are unit specifiers not periods (years, months, days). The ratio is obtained using
int ratio = years(3)/months(4); // ratio = 9
Hmm, seems like I missed a lot of the discussion. I'll need to read up on that, but I admit it seems weird to me to have to distinguish between years as a period and year as a "unit". I mean, for instance 'm' is a unit, 10m is a distance (not 10ms), ft is another unit for distances, and there is no harm in dividing 10m by 5ft. Calendar units are a bit weirder than the MKS system, but still... Anyway, I'll read more :-) Regards, Roland
But to be completely honest: I don't see the benefit of constructing dates this way. Why not simply use a constructor?
date(year, month, day);
With C++11 you could then write:
date ISO_week_start = mon <= { y, jan, day(4) }; I guess this depends on the type of {y, jan, day(4)} and the expected type of the rhs of the operator<= ? ??? operator <=(month, ???)
Note that we are talking of several dates types.
{ y, jan, day(4) } would be accepted where a ymd_date is expected.
The operator <= is defined on serial_date as the algorithm is efficient only with this representation.
That's the way I'd prefer :-)
In order to support
ymd_date ISO_week_start = mon <= { y, jan, day(4) };
we need to define operator <= for ymd_date or as a template,
template <typename Date> Date operator <=(month,Date);
but the implementation would do a conversion from Date to serial_date and return the conversion of the calculated serial_date to a Date.
Best, Vicente
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Le 11/05/13 16:29, Roland Bock a écrit :
Le 11/05/13 11:54, Roland Bock a écrit :
On 2013-05-08 18:08, Vicente J. Botet Escriba wrote:
Hi,
as the conversion between concrete dates could be expensive I guess that these conversion must be explicit.
But this has some consequences when used the implicit conversion was hidden a not efficient implementation, e.g.
date ISO_week_start = mon <= jan/day(4)/y;
jan/day(4)/y should be ymd_date as is the efficient representation.
The date generator was declared as
date operator<=(weekday wd, date x); Hi,
Do you really want to allow/document/support/advocate the American date format in C++? Why not restrict the format to something close to ISO date format for ISO C++? That way you could introduce a new type, say ym_type and
ym_type operator/(year y, month m); date operator/(ym_type ym, day d);
Neither explicit nor implicit conversion necessary :-) We have suggested a more neutral factory
auto dt = make_date(year(2013), may, 11); Yes, I like that much better. The main point I was trying to make is
On 2013-05-11 15:13, Vicente J. Botet Escriba wrote: that using operators to mimic the way some people are used to writing a date is not necessarily the best idea.
May/11/2013 11.Mai 2013 11.5.13 2013-05-11 ...
There are just so many ways.
We need to choose one of the overloaded operators.
Allowing such formats in code just adds to confusion, IMHO. Why are you confused. Even if I don't use to use May/11/2013 but 11/May/2013 I don't see how
auto dt = may/11/year(2013); could confuse someone. What could this mean other than the declaration of a date.
Another question I have about this is: Is is really a good idea to use operator/()? It does prevent you from calculating ratios between periods, e.g.
int ratio = year(3)/month(4); // ratio = 9
I'd therefore prefer another operator, e.g. operator <<
year,month and day are unit specifiers not periods (years, months, days). The ratio is obtained using
int ratio = years(3)/months(4); // ratio = 9 Hmm, seems like I missed a lot of the discussion. I'll need to read up on that, but I admit it seems weird to me to have to distinguish between years as a period and year as a "unit".
unit specifiers, such a year, are not units but a way to name an instance of a given type. Periods are units
I mean, for instance 'm' is a unit, 10m is a distance (not 10ms), I guess ms stands for meters ;-) The unit is metres not meter and the suffix is m. All end with n -s, as seconds, hours, years, ... ft is another unit for distances, and there is no harm in dividing 10m by 5ft. The unit specifiers for meters could be used to mean the e.g. the Km 13 of a specific route.
Calendar units are a bit weirder than the MKS system, but still...
Best, Vicente
On 2013-05-11 17:32, Vicente J. Botet Escriba wrote:
May/11/2013 11.Mai 2013 11.5.13 2013-05-11 ...
There are just so many ways.
We need to choose one of the overloaded operators.
Again, maybe I missed some earlier requirement, but I don't see the need. Don't get me wrong. Overloading is wonderful. But in this case, I don't see the point.
Allowing such formats in code just adds to confusion, IMHO. Why are you confused. Even if I don't use to use May/11/2013 but 11/May/2013 I don't see how
auto dt = may/11/year(2013);
could confuse someone. What could this mean other than the declaration of a date. Maybe "confuse" is too strong. But it does not help readability, if dates can be written in different styles (except for the author at that moment).
Regards, Roland
On May 11, 2013, at 5:54 AM, Roland Bock
On 2013-05-08 18:08, Vicente J. Botet Escriba wrote:
as the conversion between concrete dates could be expensive I guess that these conversion must be explicit.
But this has some consequences when used the implicit conversion was hidden a not efficient implementation, e.g.
date ISO_week_start = mon <= jan/day(4)/y;
jan/day(4)/y should be ymd_date as is the efficient representation.
The date generator was declared as
date operator<=(weekday wd, date x);
Do you really want to allow/document/support/advocate the American date format in C++? Why not restrict the format to something close to ISO date format for ISO C++?
If the types are unambiguous, such as with day, month, and year types, then any order can be supported. Those comfortable with a non-ISO order can do what they prefer with type safety.
That way you could introduce a new type, say ym_type and
ym_type operator/(year y, month m); date operator/(ym_type ym, day d);
Neither explicit nor implicit conversion necessary :-)
You misunderstood the conversion context. There are several formats in contemplation: serial, days since an epoch; ymd; day of year; week of year; etc. conversions among them are sometimes costly, so the notion is to make them explicit.
Another question I have about this is: Is is really a good idea to use operator/()? It does prevent you from calculating ratios between periods, e.g.
int ratio = year(3)/month(4); // ratio = 9
I've thought that using the operator was a little too cute, but this is an interesting argument against it.
I'd therefore prefer another operator, e.g. operator <<
-1 That operator is for I/O, even if unsupported. (Someone might want to add I/O, even if not standardized.)
But to be completely honest: I don't see the benefit of constructing dates this way. Why not simply use a constructor?
date(year, month, day);
The issue is that there are other argument lists to support, so the combinations get complicated. We're exploring lots of options.
With C++11 you could then write:
date ISO_week_start = mon <= { y, jan, day(4) };
Using an initializer list is yet another option to consider. ___ Rob (Sent from my portable computation engine)
On 2013-05-11 15:22, Rob Stewart wrote:
On May 11, 2013, at 5:54 AM, Roland Bock
wrote: On 2013-05-08 18:08, Vicente J. Botet Escriba wrote:
as the conversion between concrete dates could be expensive I guess that these conversion must be explicit.
But this has some consequences when used the implicit conversion was hidden a not efficient implementation, e.g.
date ISO_week_start = mon <= jan/day(4)/y;
jan/day(4)/y should be ymd_date as is the efficient representation.
The date generator was declared as
date operator<=(weekday wd, date x);
Do you really want to allow/document/support/advocate the American date format in C++? Why not restrict the format to something close to ISO date format for ISO C++? If the types are unambiguous, such as with day, month, and year types, then any order can be supported. Those comfortable with a non-ISO order can do what they prefer with type safety. Sure, you could, and if the library were a direct user interface, I'd say: "Nice baby steps, but not nearly there yet" ;-)
But I don't see a benefit for a library to support different date formats in code. And in terms of code readability, I think it is not a good idea to allow too many options, even though they are typesafe. The next programmer to read your code might be more comfortable with a different order and might not think like a compiler (and be confused).
That way you could introduce a new type, say ym_type and
ym_type operator/(year y, month m); date operator/(ym_type ym, day d);
Neither explicit nor implicit conversion necessary :-) You misunderstood the conversion context. There are several formats in contemplation: serial, days since an epoch; ymd; day of year; week of year; etc. conversions among them are sometimes costly, so the notion is to make them explicit.
True. As I wrote in reply to Vicente, I obviously missed major parts of the discussion so far, so I am not going to argue about the conversion before I read more about that :-)
I'd therefore prefer another operator, e.g. operator << -1
That operator is for I/O, even if unsupported. (Someone might want to add I/O, even if not standardized.) Agreed.
participants (4)
-
Howard Hinnant
-
Rob Stewart
-
Roland Bock
-
Vicente J. Botet Escriba