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 {
/**
* SI time units to specify timeouts.
* @relates duration
*/
enum class time_unit : uint32_t {
invalid = 0,
seconds = 1,
milliseconds = 1000,
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>
struct ratio_to_time_unit_helper {
static constexpr time_unit value = time_unit::invalid;
};
template <>
struct ratio_to_time_unit_helper<1, 1> {
static constexpr time_unit value = time_unit::seconds;
};
template <>
struct ratio_to_time_unit_helper<1, 1000> {
static constexpr time_unit value = time_unit::milliseconds;
};
template <>
struct ratio_to_time_unit_helper<1, 1000000> {
static constexpr time_unit value = time_unit::microseconds;
};
template <>
struct ratio_to_time_unit_helper<60, 1> {
static constexpr time_unit value = time_unit::seconds;
};
/**
* Converts an STL time period to a `time_unit`.
* @relates duration
*/
template <class Period>
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.
*/
class duration {
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) {}
constexpr duration(time_unit u, uint32_t v) : unit(u), count(v) {
// nop
}
/**
* Creates a new instance from an STL duration.
......@@ -94,54 +97,43 @@ class duration {
*/
template <class Rep, class Period>
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,
"caf::duration supports only minutes, seconds, "
"milliseconds or microseconds");
"only minutes, seconds, milliseconds and "
"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`.
*/
inline bool valid() const { return unit != time_unit::invalid; }
inline bool valid() const {
return unit != time_unit::invalid;
}
/**
* Returns `count == 0`.
*/
inline bool is_zero() const { return count == 0; }
std::string to_string() const;
inline bool is_zero() const {
return count == 0;
}
time_unit unit;
uint64_t count;
private:
// reads d.count and throws invalid_argument if d.count < 0
template <class Rep, class Period>
static inline uint64_t rd(const std::chrono::duration<Rep, Period>& d) {
template <class Rep, intmax_t Num, intmax_t 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) {
throw std::invalid_argument("negative durations are not supported");
}
return static_cast<uint64_t>(d.count());
}
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);
return static_cast<uint64_t>(d.count()) * static_cast<uint64_t>(Num);
}
};
/**
......@@ -156,32 +148,28 @@ inline bool operator!=(const duration& lhs, const duration& rhs) {
return !(lhs == rhs);
}
} // namespace caf
/**
* @relates caf::duration
* @relates duration
*/
template <class Clock, class Duration>
std::chrono::time_point<Clock, Duration>&
operator+=(std::chrono::time_point<Clock, Duration>& lhs,
const caf::duration& rhs) {
operator+=(std::chrono::time_point<Clock, Duration>& lhs, const duration& rhs) {
switch (rhs.unit) {
case caf::time_unit::seconds:
case time_unit::seconds:
lhs += std::chrono::seconds(rhs.count);
break;
case caf::time_unit::milliseconds:
case time_unit::milliseconds:
lhs += std::chrono::milliseconds(rhs.count);
break;
case caf::time_unit::microseconds:
case time_unit::microseconds:
lhs += std::chrono::microseconds(rhs.count);
break;
case caf::time_unit::invalid:
case time_unit::invalid:
break;
}
return lhs;
}
} // namespace caf
#endif // CAF_DURATION_HPP
......@@ -23,40 +23,8 @@
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) {
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();
return lhs.unit == rhs.unit && lhs.count == rhs.count;
}
} // 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