Hello, I am an a process of adapting our codebase to boost date_time library, and one quite annoying problem I encountered is that date iterators conform to InputIterator model, rather then Random Access Iterator. By the nature of application I have to calculate quite often something like current date plus N months. The only way I see so far is doing something like date d(2002,Dec,26); month_iterator mi(d); for( size_t i=0; i < 5; ++i) ++mi; d = *mi; This is rather awkward, not to mention inefficient. I was trying to search both User and Developers mailing lists for "date InputIterator" hoping to find out what was the reason to use InputIterators, but no luck. BTW, how am I supposed to roll few months back? It is a forward iterator not even a bidirectional. Even more critique: the iterator is not exactly InputIterator, since it has only prefix increment, and no postfix one. Best regards, Kirill Lapshin.
I am an a process of adapting our codebase to boost date_time library, and one quite annoying problem I encountered is that date iterators conform to InputIterator model, rather then Random Access Iterator.
The reason for this is that it is unclear in all cases that an operation is easily reversed. In particular, the problem comes about with handling the end of the month. Say for example you start an iterator on Jan 30. When you iterate to February what happens? Well the rule the supplied iterator is to back up to the end of the February -- either the 27 or 28th for a leap year. Given that these rules are complex and at least possibly non-reversible I tried to limit the exposure of the iterator: hence input iterator. In retrospect, however, it could probably be RandomAccess because I can't give you an actual case where making the iterator reverse is an actual issue.
By the nature of application I have to calculate quite often something like current date plus N months.
The only way I see so far is doing something like
date d(2002,Dec,26); month_iterator mi(d); for( size_t i=0; i < 5; ++i) ++mi; d = *mi;
This is rather awkward, not to mention inefficient.
If you want to use the iterator you can simply do the following: month_iterator mi(d, 3); // configure iterator to add 3 months at a time. mi++; //adds 3 months. If you don't want to use the iterator there are a couple of possible solutions. One is to do the following: int N = 3; //add 3 months date d(...);// constructed however date d1(d.year(), d.month()+N, d.day()); Well, except that this doesn't handle year wrap arounds and other the issues associated with the end of the month as described above. So you have to write some supporting code to handle these issues. It's not that hard -- you might take a look at the boost/date_time/adjust_functors.hpp for ideas. If you are happy with the rules associated with the iterator for the end of month handling then you can just use the functor that is used by the iterator directly. There is an example in libs/date_time/examples/gregorian/add_month.cpp. It looks like: using namespace boost::gregorian; typedef boost::date_time::month_functor<date> add_month; date d = day_clock::local_day(); add_month mf(3); //add by 3 months at a time date d2 = d + mf.get_offset(d); But don't try date d2 = d - mf.get_offset(d); because you will get the wrong answer. Basically what you need is a month functor that calculates the reverse offset for going backward. The code that's there in the functor could probably be made bi-directional, but it would take some thought and work.
BTW, how am I supposed to roll few months back? It is a forward iterator not even a bidirectional.
See above. I'm adding the month subtraction function to my todo list -- unless you want to take a crack at it ;-)
Even more critique: the iterator is not exactly InputIterator, since it has only prefix increment, and no postfix one.
Right -- will correct, thanks. Jeff
annoying problem I encountered is that date iterators conform to InputIterator model, rather then Random Access Iterator.
The reason for this is that it is unclear in all cases that an operation is easily reversed. In particular, the problem comes about with handling the end of the month. Say for example you start an iterator on Jan 30. When you iterate to February what happens? Well the rule the supplied iterator is to back up to the end of the February -- either the 27 or 28th for a leap year. Yes, I understand these complications. I was quite surprised, btw,
--- In Boost-Users@yahoogroups.com, "Jeff Garland"
Given that these rules are complex and at least possibly non-reversible I tried to limit the exposure of the iterator: hence input iterator. In retrospect, however, it could probably be RandomAccess because I can't give you an actual case where making the iterator reverse is an actual issue. I can live with InputIterator, but having RandomAccess (or, at least, Bidirectional) will simplify life significantly. Is there any chance you will consider this feature request for future development?
If you want to use the iterator you can simply do the following:
month_iterator mi(d, 3); // configure iterator to add 3 months at a time. mi++; //adds 3 months. This is nice, thanks for pointing out this feature.
If you don't want to use the iterator there are a couple of possible solutions. One is to do the following: int N = 3; //add 3 months date d(...);// constructed however date d1(d.year(), d.month()+N, d.day()); Well, except that this doesn't handle year wrap arounds and other the issues associated with the end of the month as described above. So you have to write some supporting code to handle That is exactly what I did as a temporary solution. I hope though, that someday you will create more powerful iterator or some other means to accomplish this task in simpler and cleaner way.
So bottom line for this problem is: I was wrong talking about inefficiency -- there is a way to move few month at a time. The real problem though that I can't move iterator backward. One more thing: it is quite inconvinient that it is prohibited to create a vector of dates. Do you have plans to make it vector compliant? Default constructor could initialize it to not_a_date for instance. Another little issue: there are few typos on the boost web site. Example "Print a month" looks garbled (probably transformation from source code to html failed). For instance: int std::cin >> year; and int eom_day = gregorian_calendar::end_of_month_day( date endOfMonth(year,month,eom_day); look odd. Don't know about other examples; did not have chance to study them carefully. Also under "Build & Compiler information" there is a typo: /boost/date_time/gregoran -- gregorian calendar system header files should be /boost/date_time/gregorIan Best regards, Kirill Lapshin.
I can live with InputIterator, but having RandomAccess (or, at least, Bidirectional) will simplify life significantly. Is there any chance you will consider this feature request for future development?
Sure, I'll put it on the list, but I expect it will be awhile. Of course, I'm willing to consult if you want to take a shot at doing this :-).
So bottom line for this problem is: I was wrong talking about inefficiency -- there is a way to move few month at a time. The real problem though that I can't move iterator backward.
Ok, thanks for clarifying.
One more thing: it is quite inconvinient that it is prohibited to create a vector of dates. Do you have plans to make it vector compliant? Default constructor could initialize it to not_a_date for instance.
How about this: class yourdate : public boost::gregorian::date { //your default constructor here //copy the constructors you need from the base... }; The base classes don't have virtual destructors, but you will be safe as long as you just create constructors. I've been loath to create a default constructor since it is unclear what would be the correct policy for this: not_a_date, negative_infinity, etc. Interestingly, you are the first person to ask about this....
Another little issue: there are few typos on the boost web site. Example "Print a month" looks garbled (probably transformation from source code to html failed). For instance:
int std::cin >> year;
and
int eom_day = gregorian_calendar::end_of_month_day( date endOfMonth(year,month,eom_day);
look odd. Don't know about other examples; did not have chance to study them carefully.
Thanks, these have been reported and are already fixed in CVS.
Also under "Build & Compiler information" there is a typo: /boost/date_time/gregoran -- gregorian calendar system header files
should be /boost/date_time/gregorIan
Thanks, will fix. Jeff
--- In Boost-Users@yahoogroups.com, "Jeff Garland"
Sure, I'll put it on the list, but I expect it will be awhile. Of course, I'm willing to consult if you want to take a shot at doing this :-). I don't really have much free time at the moment. However I will try to give it a shot. What is the right place to take latest source code from? Regular Boost CVS repository on SourceForge?
One more thing: it is quite inconvinient that it is prohibited to create a vector of dates. Do you have plans to make it vector compliant? Default constructor could initialize it to not_a_date for instance.
How about this:
class yourdate : public boost::gregorian::date { //your default constructor here //copy the constructors you need from the base... }; Right, this will do it. I think I shall pursue this way. However, date is quite a low-level class, and it sounds perfectly reasonable to create a vector of dates. My opinion is that ability to create vectors is a strong argument for providing default constructor; it is not really that important what exact value it sets date to, as long as it is not a regular date. I personally have a slight preference for not_a_date.
After all if default value is a real issue we can always parameterize date with "DefaultValuePolicy" giving users way to control it. I doubt though that many people will change it. Kirill
Right, this will do it. I think I shall pursue this way. However, date is quite a low-level class, and it sounds perfectly reasonable to create a vector of dates.
My opinion is that ability to create vectors is a strong argument for providing default constructor; it is not really that important what exact value it sets date to, as long as it is not a regular date. I personally have a slight preference for not_a_date.
Um, you can create a std::vector of a non-default-constructible object. It needs to be CopyConstructible, not DefaultConstructible. The only issue is that occasionally you may need to pass in a default initializer (e.g., when resizing (not reserving) or push_back-ing with no argument). Now, whether a date should nevertheless be default-constructible is another issue, but simply being able to put them in a vector is not an argument for it. George Heintzelman georgeh@aya.yale.edu
Um, you can create a std::vector of a non-default-constructible object. It needs to be CopyConstructible, not DefaultConstructible. The only issue is that occasionally you may need to pass in a default initializer (e.g., when resizing (not reserving) or push_back-ing with no argument).
Now, whether a date should nevertheless be default-constructible is another issue, but simply being able to put them in a vector is not an argument for it.
Well at first I was really suprised when the date class wasn't default constructible (I expected it to be default constructible and set to not_a_date, for me that would be the design with the least suprise), and I usually expect most simple classes with value semantics to be default constructible. But when I've used (and get used ;)) to the date class for a while I really don't miss it and usually it isn't much pain to add the explicit instantiation of the class either when using it in containers or as variables. So my first reaction was to write to the list or to Jeff and ask why but now I don't really care and can live with the design in either way. But I guess for new users of the library would be least suprised if a default constructor was supplied (instantiating date to not_a_date). Regards /Michel
In article
"Jeff Garland"
Even more critique: the iterator is not exactly InputIterator, since it has only prefix increment, and no postfix one.
Right -- will correct, thanks.
Jeff, I remember discussing this with you at Oopsla last year -- why not use the iterator adaptors library to help you build correct iterators? -- David Abrahams dave@boost-consulting.com * http://www.boost-consulting.com Boost support, enhancements, training, and commercial distribution
participants (6)
-
David Abrahams
-
George A. Heintzelman
-
Howard Hinnant
-
Jeff Garland
-
klapshin <klapshin@yahoo.com>
-
Michel Andr�