Commit 8f30f19c authored by Dominik Charousset's avatar Dominik Charousset

Restrict `duration` to accept only integral values

Rather than implicitly casting floating points to integrals, `duration` now
verifies its input to be integral via `static_assert`. This patch also
streamlines the implementation of `duration`, reducing its code size.
parent 697f25e9
...@@ -29,48 +29,49 @@ namespace caf { ...@@ -29,48 +29,49 @@ namespace caf {
/** /**
* SI time units to specify timeouts. * SI time units to specify timeouts.
* @relates duration
*/ */
enum class time_unit : uint32_t { enum class time_unit : uint32_t {
invalid = 0, invalid = 0,
seconds = 1, seconds = 1,
milliseconds = 1000, milliseconds = 1000,
microseconds = 1000000 microseconds = 1000000
}; };
// minutes are implicitly converted to seconds /**
* Converts the ratio Num/Denom to a `time_unit` if the ratio describes
* seconds, milliseconds, microseconds, or minutes. Minutes are mapped
* to `time_unit::seconds`, any unrecognized ratio to `time_unit::invalid`.
* @relates duration
*/
template <intmax_t Num, intmax_t Denom> template <intmax_t Num, intmax_t Denom>
struct ratio_to_time_unit_helper { struct ratio_to_time_unit_helper {
static constexpr time_unit value = time_unit::invalid; static constexpr time_unit value = time_unit::invalid;
}; };
template <> template <>
struct ratio_to_time_unit_helper<1, 1> { struct ratio_to_time_unit_helper<1, 1> {
static constexpr time_unit value = time_unit::seconds; static constexpr time_unit value = time_unit::seconds;
}; };
template <> template <>
struct ratio_to_time_unit_helper<1, 1000> { struct ratio_to_time_unit_helper<1, 1000> {
static constexpr time_unit value = time_unit::milliseconds; static constexpr time_unit value = time_unit::milliseconds;
}; };
template <> template <>
struct ratio_to_time_unit_helper<1, 1000000> { struct ratio_to_time_unit_helper<1, 1000000> {
static constexpr time_unit value = time_unit::microseconds; static constexpr time_unit value = time_unit::microseconds;
}; };
template <> template <>
struct ratio_to_time_unit_helper<60, 1> { struct ratio_to_time_unit_helper<60, 1> {
static constexpr time_unit value = time_unit::seconds; static constexpr time_unit value = time_unit::seconds;
}; };
/** /**
* Converts an STL time period to a `time_unit`. * Converts an STL time period to a `time_unit`.
* @relates duration
*/ */
template <class Period> template <class Period>
constexpr time_unit get_time_unit_from_period() { constexpr time_unit get_time_unit_from_period() {
...@@ -81,12 +82,14 @@ constexpr time_unit get_time_unit_from_period() { ...@@ -81,12 +82,14 @@ constexpr time_unit get_time_unit_from_period() {
* Time duration consisting of a `time_unit` and a 64 bit unsigned integer. * Time duration consisting of a `time_unit` and a 64 bit unsigned integer.
*/ */
class duration { class duration {
public: public:
constexpr duration() : unit(time_unit::invalid), count(0) {
// nop
}
constexpr duration() : unit(time_unit::invalid), count(0) {} constexpr duration(time_unit u, uint32_t v) : unit(u), count(v) {
// nop
constexpr duration(time_unit u, uint32_t v) : unit(u), count(v) {} }
/** /**
* Creates a new instance from an STL duration. * Creates a new instance from an STL duration.
...@@ -94,54 +97,43 @@ class duration { ...@@ -94,54 +97,43 @@ class duration {
*/ */
template <class Rep, class Period> template <class Rep, class Period>
duration(std::chrono::duration<Rep, Period> d) duration(std::chrono::duration<Rep, Period> d)
: unit(get_time_unit_from_period<Period>()), count(rd(d)) { : unit(get_time_unit_from_period<Period>()),
count(rd(d)) {
static_assert(get_time_unit_from_period<Period>() != time_unit::invalid, static_assert(get_time_unit_from_period<Period>() != time_unit::invalid,
"caf::duration supports only minutes, seconds, " "only minutes, seconds, milliseconds and "
"milliseconds or microseconds"); "microseconds are supported");
static_assert(std::is_integral<Rep>::value,
"only integral durations are supported");
} }
/**
* Creates a new instance from an STL duration given in minutes.
* @throws std::invalid_argument Thrown if `d.count() is negative.
*/
template <class Rep>
duration(std::chrono::duration<Rep, std::ratio<60, 1>> d)
: unit(time_unit::seconds), count(rd(d) * 60) {}
/** /**
* Returns `unit != time_unit::invalid`. * Returns `unit != time_unit::invalid`.
*/ */
inline bool valid() const { return unit != time_unit::invalid; } inline bool valid() const {
return unit != time_unit::invalid;
}
/** /**
* Returns `count == 0`. * Returns `count == 0`.
*/ */
inline bool is_zero() const { return count == 0; } inline bool is_zero() const {
return count == 0;
std::string to_string() const; }
time_unit unit; time_unit unit;
uint64_t count; uint64_t count;
private: private:
// reads d.count and throws invalid_argument if d.count < 0 // reads d.count and throws invalid_argument if d.count < 0
template <class Rep, class Period> template <class Rep, intmax_t Num, intmax_t D>
static inline uint64_t rd(const std::chrono::duration<Rep, Period>& d) { static uint64_t rd(const std::chrono::duration<Rep, std::ratio<Num, D>>& d) {
// assertion (via ctors): Num == 1 || (Num == 60 && D == 1)
if (d.count() < 0) { if (d.count() < 0) {
throw std::invalid_argument("negative durations are not supported"); throw std::invalid_argument("negative durations are not supported");
} }
return static_cast<uint64_t>(d.count()); return static_cast<uint64_t>(d.count()) * static_cast<uint64_t>(Num);
}
template <class Rep>
static inline uint64_t
rd(const std::chrono::duration<Rep, std::ratio<60, 1>>& d) {
// convert minutes to seconds on-the-fly
return rd(std::chrono::duration<Rep, std::ratio<1, 1>>(d.count()) * 60);
} }
}; };
/** /**
...@@ -156,32 +148,28 @@ inline bool operator!=(const duration& lhs, const duration& rhs) { ...@@ -156,32 +148,28 @@ inline bool operator!=(const duration& lhs, const duration& rhs) {
return !(lhs == rhs); return !(lhs == rhs);
} }
} // namespace caf
/** /**
* @relates caf::duration * @relates duration
*/ */
template <class Clock, class Duration> template <class Clock, class Duration>
std::chrono::time_point<Clock, Duration>& std::chrono::time_point<Clock, Duration>&
operator+=(std::chrono::time_point<Clock, Duration>& lhs, operator+=(std::chrono::time_point<Clock, Duration>& lhs, const duration& rhs) {
const caf::duration& rhs) {
switch (rhs.unit) { switch (rhs.unit) {
case caf::time_unit::seconds: case time_unit::seconds:
lhs += std::chrono::seconds(rhs.count); lhs += std::chrono::seconds(rhs.count);
break; break;
case time_unit::milliseconds:
case caf::time_unit::milliseconds:
lhs += std::chrono::milliseconds(rhs.count); lhs += std::chrono::milliseconds(rhs.count);
break; break;
case time_unit::microseconds:
case caf::time_unit::microseconds:
lhs += std::chrono::microseconds(rhs.count); lhs += std::chrono::microseconds(rhs.count);
break; break;
case time_unit::invalid:
case caf::time_unit::invalid:
break; break;
} }
return lhs; return lhs;
} }
} // namespace caf
#endif // CAF_DURATION_HPP #endif // CAF_DURATION_HPP
...@@ -23,40 +23,8 @@ ...@@ -23,40 +23,8 @@
namespace caf { namespace caf {
namespace {
inline uint64_t ui64_val(const duration& d) {
return static_cast<uint64_t>(d.unit) * d.count;
}
} // namespace <anonmyous>
bool operator==(const duration& lhs, const duration& rhs) { bool operator==(const duration& lhs, const duration& rhs) {
return (lhs.unit == rhs.unit ? lhs.count == rhs.count return lhs.unit == rhs.unit && lhs.count == rhs.count;
: ui64_val(lhs) == ui64_val(rhs));
}
std::string duration::to_string() const {
if (unit == time_unit::invalid) {
return "-invalid-";
}
std::ostringstream oss;
oss << count;
switch (unit) {
case time_unit::invalid:
oss << "?";
break;
case time_unit::seconds:
oss << "s";
break;
case time_unit::milliseconds:
oss << "ms";
break;
case time_unit::microseconds:
oss << "us";
break;
}
return oss.str();
} }
} // namespace caf } // namespace caf
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment