With C++11, C++ has some timing facilities in the standard. One of these facilities is a standard interface for clocks, that basically allows getting the time at the call of the now
function of the clock.
All is well up until this point but I fail to see the reason for requiring now
to be a static function. On a hosted system, the standard clocks might be implementable purely with system calls or by reading the processor counters etc. However, this limits the implementation of custom clocks that needs to maintain some state. With this interface, one either cannot implement some clocks or has to use global state.
One issue I ran into was basically synchronizing a local clock to the time I got from an NTP server. The code simply looks like this:
class sntp_clock
{
public:
sntp_clock()
: local_time_at_ctor(read_some_cpu_counter())
, sntp_time_at_ctor(read_sntp_time()) {}
sntp_time_t now() const {
return sntp_time_at_ctor + (read_some_cpu_counter() - local_time_at_ctor);
}
/* required types etc */
private:
local_time_t local_time_at_ctor;
sntp_time_t sntp_time_at_ctor;
};
Since I can't make now
static without making the state static as well, this clock does not satisfy the requirements of a Clock in C++ standard. But each NTP server will have separate state.
Additionally, for efficiency reasons, I may not want to start the cpu counter a clock instance exists, but again, since now
is static, I can't know exactly when to start to clock, or when to stop it.
My question is why do clocks have static now
requirement?
Note: The current standard draft requires now
to be static: http://eel.is/c++draft/time.clock.req#tab:time.clock
Boost.Chrono documentation has the same requirement: https://www.boost.org/doc/libs/1_63_0/doc/html/chrono/reference.html#chrono.reference.cpp0x.clock
There were both theoretical and practical concerns that drove this decision.
There's a joke that a person with a watch always knows what time it is, but a person with two watches never does. There's just a little bit of truth in that joke, and it did influence the decision. If an application needs to know the current time in two or more places, and if clocks are stateful, then there is an issue in ensuring that the same instance of the clock be used in all places to ensure that all parts of the code are dealing with the same definition of "current time."
By making clocks stateless, but allowing multiple clocks with different types, the type system can help the programmer ensure that a program uses the same definition of current time in different places of the program. And yet in those cases where one needs multiple definitions of time, then that too is available, just as distinct types.
As more of a practical consideration, the very first clients of the chrono::clock
code was chrono
itself. We had to eat our own dog food. Take for example an implementation of condition_variable::wait_until
:
https://github.com/llvm-mirror/libcxx/blob/master/include/__mutex_base#L377-L385
template <class _Clock, class _Duration>
cv_status
condition_variable::wait_until(unique_lock<mutex>& __lk,
const chrono::time_point<_Clock, _Duration>& __t)
{
using namespace chrono;
wait_for(__lk, __t - _Clock::now());
return _Clock::now() < __t ? cv_status::no_timeout : cv_status::timeout;
}
Here a function is taking a single generic time_point
, and the algorithm needs to find the current time associated with that time_point
. By packing the type of the Clock
into the type of the time_point
and by having a static now()
, the code is very clean to write, and has a very clean interface. Yet it is generic enough that this code will work with any user-written custom clock: not just the std-specified clocks.
Had clocks been stateful, then either:
condition_variable::wait_until
could not get the current time, ortime_point
is measured against as well.Neither of the above two choices seemed palatable to me.
Note that condition_variable::wait_until
is not a special case, but just one example out of many such algorithms. Indeed, I assumed that not only standards implementors would write such algorithms, but the general public would too. Here's an example of the latter:
https://stackoverflow.com/a/35293183/576911
Yes, I have run into cases where people want stateful clocks. The OP of this question offers such an example. But since there is the option that "stateful clocks" can still be given static state, and if you need other state, use a different type; And because of the advantages of stateless clocks stated above; The choice was made that the advantage lay with the stateless clock design.
I've been thinking more about the client that says:
So is my stateful clock not good code?
I think a stateful clock is fine as long as one realizes the limitations that are on it. And unfortunately I believe this is an issue that is more complicated than needs be because of a minor bug in the standard.
From a practical standpoint, there's only one thing you can't do with a stateful clock that you can do with a stateless clock, and it goes back to the section above titled The Practical.
You can not instantiate an algorithm (std or otherwise) that requires a template parameter to be a
Clock
TM.
For example if you have a time_point
based on a stateful clock, you can not use that time_point
to call condition_variable::wait_until
. If you don't want to do that anyway, that doesn't mean your stateful clock is bad. If your stateful clock serves your purposes, then that is fine.
In C++20, there's even an example of a clock that doesn't meet all of the C++11-17 clock requirements:
struct local_t {};
Yes, that is (sort of) a clock. But it doesn't do anything, almost. It is used to create a family of time_point
s that have no associated now()
:
template<class Duration>
using local_time = time_point<local_t, Duration>;
And this turns out to be really useful when distinguishing UTC time points from time points that are associated with an as-yet-unspecified time zone (think type safety).
So if creating a clock without a static now()
is ok for the standard, why isn't it ok for you?! And the only reason I can think of is that "minor bug" in the standard I reference above.
27.6 [time.point] in the C++20 spec says this about template<class Clock, class Duration> class time_point
:
1
Clock
shall either satisfy the Cpp17Clock requirements (27.3) or be the typelocal_t
.
I now believe this is overly restrictive. Programmers ought to be able to instantiate time_point
with a stateful clock. They just can't call condition_variable::wait_until
(et al.) with that time_point
. But they can still get all of the algebraic benefits of using that time_point
and the duration
s resulting from its differences.
There's no good reason for this restriction except for the standard saying so. And the very existence of local_t
which also doesn't meet the Cpp17Clock requirements pretty much proves the point.
1
Clock
shalleither satisfy the Cpp17Clock requirements (27.3) or be the typehave the nested typelocal_t
duration
if the instantiation defaults the template parameterDuration
.
There is now a paper tracking this issue.
The proposed changes are now in the working draft for the standard after C++20:
http://eel.is/c++draft/time.point.general
http://eel.is/c++draft/thread.req.paramname
Much thanks to Alexey Dmitriev for driving this work.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With