That would seem most error prone. Here is a non-contrived custom chrono clock I have in my tool box:
namespace x
{
struct clock
{
typedef unsigned long long rep;
typedef std::ratio<1, 2800000000> period; // My machine is 2.8 GHz
typedef std::chrono::duration<rep, period> duration;
typedef std::chrono::time_point<clock> time_point;
static const bool is_steady = true;
static time_point now() noexcept
{
unsigned lo, hi;
asm volatile("rdtsc" : "=a" (lo), "=d" (hi));
return time_point(duration(static_cast<rep>(hi) << 32 | lo));
}
private:
static
unsigned
get_clock_speed()
{
int mib[] = {CTL_HW, HW_CPU_FREQ};
const std::size_t namelen = sizeof(mib)/sizeof(mib[0]);
unsigned freq;
size_t freq_len = sizeof(freq);
if (sysctl(mib, namelen, &freq, &freq_len, nullptr, 0) != 0)
return 0;
return freq;
}
static
bool
check_invariants()
{
static_assert(1 == period::num, "period must be 1/freq");
assert(get_clock_speed() == period::den);
static_assert(std::is_same<rep, duration::rep>::value,
"rep and duration::rep must be the same type");
static_assert(std::is_same<period, duration::period>::value,
"period and duration::period must be the same type");
static_assert(std::is_same<duration, time_point::duration>::value,
"duration and time_point::duration must be the same type");
return true;
}
static const bool invariants;
};
const bool clock::invariants = clock::check_invariants();
} // x
It isn't portable. But when I need a bare-to-the-metal timer, this is what I reach for.
Now consider a world where std::abs(0ull) compiles. I'll simulate such a world with the exp namespace:
namespace exp
{
template <class Int>
Int
abs(Int x)
{
return x >= 0 ? x : -x;
}
}
And finally consider some analysis code that somewhere deep down in the details, is going to manipulate time_points from my x::clock, and call abs on x::clock::rep (really low level, hidden details here):
template <class Clock, class Duration>
typename Duration::rep
analyze(std::chrono::time_point<Clock, Duration> x,
std::chrono::time_point<Clock, Duration> y)
{
return exp::abs((x-y).count());
}
int
main()
{
auto x = x::clock::now();
auto y = x::clock::now();
std::cout << analyze(x, y) << '\n';
}
This all looks quite reasonable, right?
It is a run-time error. For me it prints out:
18446744073709551559
It should be printing out a relatively small number of clock ticks. If I change x::clock::rep from unsigned long long to long long, I get:
18
(the expected result).
But a signed rep for my clock really doesn't make sense. Really the bug is a combination of things:
1. I'm using a clock with an analysis that was not designed to handle unsigned reps.
2. The analysis should have known that it was not designed to handle unsigned reps, detected it, and given a compile-time diagnostic.
One thing the C++ committee could do to help the coder of analyze(), and the client who is using x::clock(), is to not proceed with LWG 2192. I.e. this result is actually a good result:
test.cpp:80:12: error: call to 'abs' is ambiguous
return std::abs((x-y).count());
^~~~~~~~
test.cpp:90:18: note: in instantiation of function template specialization 'analyze<x::clock, std::__1::chrono::duration<unsigned long long, std::__1::ratio<1,
2800000000> > >' requested here
std::cout << analyze(x, y) << '\n';
^
/usr/include/stdlib.h:129:6: note: candidate function
int abs(int) __pure2;
^
../libcxx/include/cstdlib:159:44: note: candidate function
inline _LIBCPP_INLINE_VISIBILITY long abs( long __x) _NOEXCEPT {return labs(__x);}
^
../libcxx/include/cstdlib:161:44: note: candidate function
inline _LIBCPP_INLINE_VISIBILITY long long abs(long long __x) _NOEXCEPT {return llabs(__x);}
^
1 error generated.
Now I'm alerted to the bug at compile time, and I can decide what to do about it.
Howard