Recently I was writing a class that had overloaded read
methods for various data types. One of the sets of types I wanted to handle were integral std::chrono::time_point
s. For this I wrote the following code:
// Return a timestamp from a chunk at a given offset as if from an
// integer in network byte order
template <IsTimePoint T>
requires std::integral<typename T::rep>
[[nodiscard]] T read(length_t offset = 0) const {
return T{typename T::duration{read<typename T::rep>(offset)}};
}
Elsewhere I included a concept for matching time_point
types:
template <typename T>
concept IsTimePoint =
requires {
typename T::clock;
typename T::duration;
typename T::rep;
typename T::period;
requires std::chrono::is_clock_v<typename T::clock>;
requires std::same_as<T,
std::chrono::time_point<typename T::clock,
std::chrono::duration<typename T::rep,
typename T::period>>>;
};
The code works. My question is whether this was a good way to solve this problem or whether there is a simpler solution that I have overlooked. Part of the reason for this project is for me to learn C++20, but I often worry that I might be using new techniques because they are new rather than because they are the right tool for the job.
1 Answer 1
It seems there is a simpler solution. Shamelessly stolen from this post:
template<class, template<class...> class>
static constexpr bool is_specialization = false;
template<template<class...> class T, class... Args>
static constexpr bool is_specialization<T<Args...>, T> = true;
template<class T>
concept IsTimePoint = is_specialization<T, std::chrono::time_point>;
On the other hand, consider whether you really want to have the low-level function read()
construct values of these high-level types. The calling code has to know what the type is of the integer that it reads, then it can construct an appropriate std::chrono::time_point
from it itself.
Explore related questions
See similar questions with these tags.