Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does the C++ standard require the `Clock::now` function to be `static`?

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

like image 990
Fatih BAKIR Avatar asked May 31 '19 18:05

Fatih BAKIR


1 Answers

There were both theoretical and practical concerns that drove this decision.

The Theoretical

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.

The Practical

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:

  1. condition_variable::wait_until could not get the current time, or
  2. The client would have to pass in the clock the time_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.


Update 1

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 ClockTM.

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_points 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 type local_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 durations 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 shall either satisfy the Cpp17Clock requirements (27.3) or be the type local_t have the nested type duration if the instantiation defaults the template parameter Duration.

Update 2

There is now a paper tracking this issue.

Update 3

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.

like image 197
Howard Hinnant Avatar answered Nov 14 '22 18:11

Howard Hinnant