From c1c8b8e83632b31e821fb2c17be42956844c4d6c Mon Sep 17 00:00:00 2001 From: Sebastian Jansson Date: Tue, 7 Aug 2018 15:29:04 +0200 Subject: [PATCH] Adds constexpr create functions for units. This adds new constexpr create function for DataSize, DataRate, TimeDelta and Timestamp. The names are capitalized to mirror the naming scheme of the previously constexpr methods (Zero and Infinity create functions). They are also kept longer since they are not expected to be used in complex expressions. Bug: webrtc:9574 Change-Id: I5950548718675050fc5d66699de295455c310861 Reviewed-on: https://webrtc-review.googlesource.com/91161 Reviewed-by: Karl Wiberg Commit-Queue: Sebastian Jansson Cr-Commit-Position: refs/heads/master@{#24218} --- api/units/data_rate.h | 53 +++++++++++++------- api/units/data_rate_unittest.cc | 9 ++++ api/units/data_size.h | 38 +++++++++------ api/units/data_size_unittest.cc | 8 +++ api/units/time_delta.h | 83 ++++++++++++++++++++++---------- api/units/time_delta_unittest.cc | 12 +++++ api/units/timestamp.h | 70 +++++++++++++++++++-------- api/units/timestamp_unittest.cc | 16 ++++++ 8 files changed, 213 insertions(+), 76 deletions(-) diff --git a/api/units/data_rate.h b/api/units/data_rate.h index 1122d5e891..47a143c889 100644 --- a/api/units/data_rate.h +++ b/api/units/data_rate.h @@ -44,6 +44,18 @@ class DataRate { static constexpr DataRate Infinity() { return DataRate(data_rate_impl::kPlusInfinityVal); } + template + static constexpr DataRate BitsPerSec() { + static_assert(bps >= 0, ""); + static_assert(bps < data_rate_impl::kPlusInfinityVal, ""); + return DataRate(bps); + } + template + static constexpr DataRate KilobitsPerSec() { + static_assert(kbps >= 0, ""); + static_assert(kbps < data_rate_impl::kPlusInfinityVal / 1000, ""); + return DataRate(kbps * 1000); + } template < typename T, @@ -89,49 +101,53 @@ class DataRate { } template typename std::enable_if::value, T>::type kbps() const { - return rtc::dchecked_cast((bps() + 500) / 1000); + RTC_DCHECK(IsFinite()); + return rtc::dchecked_cast(UnsafeKilobitsPerSec()); } template - typename std::enable_if::value, T>::type bps() - const { - if (IsInfinite()) { - return std::numeric_limits::infinity(); - } else { - return bits_per_sec_; - } + typename std::enable_if::value, + T>::type constexpr bps() const { + return IsInfinite() ? std::numeric_limits::infinity() : bits_per_sec_; } template - typename std::enable_if::value, T>::type kbps() - const { + typename std::enable_if::value, + T>::type constexpr kbps() const { return bps() * 1e-3; } + constexpr int64_t bps_or(int64_t fallback_value) const { + return IsFinite() ? bits_per_sec_ : fallback_value; + } + constexpr int64_t kbps_or(int64_t fallback_value) const { + return IsFinite() ? UnsafeKilobitsPerSec() : fallback_value; + } + constexpr bool IsZero() const { return bits_per_sec_ == 0; } constexpr bool IsInfinite() const { return bits_per_sec_ == data_rate_impl::kPlusInfinityVal; } constexpr bool IsFinite() const { return !IsInfinite(); } - double operator/(const DataRate& other) const { + constexpr double operator/(const DataRate& other) const { return bps() / other.bps(); } - bool operator==(const DataRate& other) const { + constexpr bool operator==(const DataRate& other) const { return bits_per_sec_ == other.bits_per_sec_; } - bool operator!=(const DataRate& other) const { + constexpr bool operator!=(const DataRate& other) const { return bits_per_sec_ != other.bits_per_sec_; } - bool operator<=(const DataRate& other) const { + constexpr bool operator<=(const DataRate& other) const { return bits_per_sec_ <= other.bits_per_sec_; } - bool operator>=(const DataRate& other) const { + constexpr bool operator>=(const DataRate& other) const { return bits_per_sec_ >= other.bits_per_sec_; } - bool operator>(const DataRate& other) const { + constexpr bool operator>(const DataRate& other) const { return bits_per_sec_ > other.bits_per_sec_; } - bool operator<(const DataRate& other) const { + constexpr bool operator<(const DataRate& other) const { return bits_per_sec_ < other.bits_per_sec_; } @@ -140,6 +156,9 @@ class DataRate { // more recognizable. explicit constexpr DataRate(int64_t bits_per_second) : bits_per_sec_(bits_per_second) {} + constexpr int64_t UnsafeKilobitsPerSec() const { + return (bits_per_sec_ + 500) / 1000; + } int64_t bits_per_sec_; }; diff --git a/api/units/data_rate_unittest.cc b/api/units/data_rate_unittest.cc index b0cc0138dd..9c91fd6247 100644 --- a/api/units/data_rate_unittest.cc +++ b/api/units/data_rate_unittest.cc @@ -15,10 +15,19 @@ namespace webrtc { namespace test { TEST(DataRateTest, ConstExpr) { + constexpr int64_t kValue = 12345; constexpr DataRate kDataRateZero = DataRate::Zero(); constexpr DataRate kDataRateInf = DataRate::Infinity(); static_assert(kDataRateZero.IsZero(), ""); static_assert(kDataRateInf.IsInfinite(), ""); + static_assert(kDataRateInf.bps_or(-1) == -1, ""); + static_assert(kDataRateInf > kDataRateZero, ""); + + constexpr DataRate kDataRateBps = DataRate::BitsPerSec(); + constexpr DataRate kDataRateKbps = DataRate::KilobitsPerSec(); + static_assert(kDataRateBps.bps() == kValue, ""); + static_assert(kDataRateBps.bps_or(0) == kValue, ""); + static_assert(kDataRateKbps.kbps_or(0) == kValue, ""); } TEST(DataRateTest, GetBackSameValues) { diff --git a/api/units/data_size.h b/api/units/data_size.h index 6fe1259e68..00ab2eccf7 100644 --- a/api/units/data_size.h +++ b/api/units/data_size.h @@ -33,6 +33,12 @@ class DataSize { static constexpr DataSize Infinity() { return DataSize(data_size_impl::kPlusInfinityVal); } + template + static constexpr DataSize Bytes() { + static_assert(bytes >= 0, ""); + static_assert(bytes < data_size_impl::kPlusInfinityVal, ""); + return DataSize(bytes); + } template < typename T, @@ -64,13 +70,13 @@ class DataSize { } template - typename std::enable_if::value, T>::type bytes() - const { - if (IsInfinite()) { - return std::numeric_limits::infinity(); - } else { - return bytes_; - } + constexpr typename std::enable_if::value, T>::type + bytes() const { + return IsInfinite() ? std::numeric_limits::infinity() : bytes_; + } + + constexpr int64_t bytes_or(int64_t fallback_value) const { + return IsFinite() ? bytes_ : fallback_value; } constexpr bool IsZero() const { return bytes_ == 0; } @@ -92,23 +98,27 @@ class DataSize { bytes_ += other.bytes(); return *this; } - double operator/(const DataSize& other) const { + constexpr double operator/(const DataSize& other) const { return bytes() / other.bytes(); } - bool operator==(const DataSize& other) const { + constexpr bool operator==(const DataSize& other) const { return bytes_ == other.bytes_; } - bool operator!=(const DataSize& other) const { + constexpr bool operator!=(const DataSize& other) const { return bytes_ != other.bytes_; } - bool operator<=(const DataSize& other) const { + constexpr bool operator<=(const DataSize& other) const { return bytes_ <= other.bytes_; } - bool operator>=(const DataSize& other) const { + constexpr bool operator>=(const DataSize& other) const { return bytes_ >= other.bytes_; } - bool operator>(const DataSize& other) const { return bytes_ > other.bytes_; } - bool operator<(const DataSize& other) const { return bytes_ < other.bytes_; } + constexpr bool operator>(const DataSize& other) const { + return bytes_ > other.bytes_; + } + constexpr bool operator<(const DataSize& other) const { + return bytes_ < other.bytes_; + } private: explicit constexpr DataSize(int64_t bytes) : bytes_(bytes) {} diff --git a/api/units/data_size_unittest.cc b/api/units/data_size_unittest.cc index e8e16362c5..fe7f591dc0 100644 --- a/api/units/data_size_unittest.cc +++ b/api/units/data_size_unittest.cc @@ -15,10 +15,18 @@ namespace webrtc { namespace test { TEST(DataSizeTest, ConstExpr) { + constexpr int64_t kValue = 12345; constexpr DataSize kDataSizeZero = DataSize::Zero(); constexpr DataSize kDataSizeInf = DataSize::Infinity(); static_assert(kDataSizeZero.IsZero(), ""); static_assert(kDataSizeInf.IsInfinite(), ""); + static_assert(kDataSizeInf.bytes_or(-1) == -1, ""); + static_assert(kDataSizeInf > kDataSizeZero, ""); + + constexpr DataSize kDataSize = DataSize::Bytes(); + static_assert(kDataSize.bytes_or(-1) == kValue, ""); + + EXPECT_EQ(kDataSize.bytes(), kValue); } TEST(DataSizeTest, GetBackSameValues) { diff --git a/api/units/time_delta.h b/api/units/time_delta.h index 1a636bd09d..1155eb526b 100644 --- a/api/units/time_delta.h +++ b/api/units/time_delta.h @@ -42,6 +42,24 @@ class TimeDelta { static constexpr TimeDelta MinusInfinity() { return TimeDelta(timedelta_impl::kMinusInfinityVal); } + template + static constexpr TimeDelta Seconds() { + static_assert(seconds > timedelta_impl::kMinusInfinityVal / 1000000, ""); + static_assert(seconds < timedelta_impl::kPlusInfinityVal / 1000000, ""); + return TimeDelta(seconds * 1000000); + } + template + static constexpr TimeDelta Millis() { + static_assert(ms > timedelta_impl::kMinusInfinityVal / 1000, ""); + static_assert(ms < timedelta_impl::kPlusInfinityVal / 1000, ""); + return TimeDelta(ms * 1000); + } + template + static constexpr TimeDelta Micros() { + static_assert(us > timedelta_impl::kMinusInfinityVal, ""); + static_assert(us < timedelta_impl::kPlusInfinityVal, ""); + return TimeDelta(us); + } template < typename T, @@ -98,12 +116,13 @@ class TimeDelta { template typename std::enable_if::value, T>::type seconds() const { - return rtc::dchecked_cast((us() + (us() >= 0 ? 500000 : -500000)) / - 1000000); + RTC_DCHECK(IsFinite()); + return rtc::dchecked_cast(UnsafeSeconds()); } template typename std::enable_if::value, T>::type ms() const { - return rtc::dchecked_cast((us() + (us() >= 0 ? 500 : -500)) / 1000); + RTC_DCHECK(IsFinite()); + return rtc::dchecked_cast(UnsafeMillis()); } template typename std::enable_if::value, T>::type us() const { @@ -118,32 +137,39 @@ class TimeDelta { } template - typename std::enable_if::value, T>::type seconds() - const { + constexpr typename std::enable_if::value, T>::type + seconds() const { return us() * 1e-6; } template - typename std::enable_if::value, T>::type ms() - const { + constexpr typename std::enable_if::value, T>::type + ms() const { return us() * 1e-3; } template - typename std::enable_if::value, T>::type us() - const { - if (IsPlusInfinity()) { - return std::numeric_limits::infinity(); - } else if (IsMinusInfinity()) { - return -std::numeric_limits::infinity(); - } else { - return microseconds_; - } + constexpr typename std::enable_if::value, T>::type + us() const { + return IsPlusInfinity() + ? std::numeric_limits::infinity() + : IsMinusInfinity() ? -std::numeric_limits::infinity() + : microseconds_; } template - typename std::enable_if::value, T>::type ns() - const { + constexpr typename std::enable_if::value, T>::type + ns() const { return us() * 1e3; } + constexpr int64_t seconds_or(int64_t fallback_value) const { + return IsFinite() ? UnsafeSeconds() : fallback_value; + } + constexpr int64_t ms_or(int64_t fallback_value) const { + return IsFinite() ? UnsafeMillis() : fallback_value; + } + constexpr int64_t us_or(int64_t fallback_value) const { + return IsFinite() ? microseconds_ : fallback_value; + } + TimeDelta Abs() const { return TimeDelta::us(std::abs(us())); } constexpr bool IsZero() const { return microseconds_ == 0; } constexpr bool IsFinite() const { return !IsInfinite(); } @@ -171,30 +197,36 @@ class TimeDelta { microseconds_ += other.us(); return *this; } - double operator/(const TimeDelta& other) const { + constexpr double operator/(const TimeDelta& other) const { return us() / other.us(); } - bool operator==(const TimeDelta& other) const { + constexpr bool operator==(const TimeDelta& other) const { return microseconds_ == other.microseconds_; } - bool operator!=(const TimeDelta& other) const { + constexpr bool operator!=(const TimeDelta& other) const { return microseconds_ != other.microseconds_; } - bool operator<=(const TimeDelta& other) const { + constexpr bool operator<=(const TimeDelta& other) const { return microseconds_ <= other.microseconds_; } - bool operator>=(const TimeDelta& other) const { + constexpr bool operator>=(const TimeDelta& other) const { return microseconds_ >= other.microseconds_; } - bool operator>(const TimeDelta& other) const { + constexpr bool operator>(const TimeDelta& other) const { return microseconds_ > other.microseconds_; } - bool operator<(const TimeDelta& other) const { + constexpr bool operator<(const TimeDelta& other) const { return microseconds_ < other.microseconds_; } private: explicit constexpr TimeDelta(int64_t us) : microseconds_(us) {} + constexpr int64_t UnsafeSeconds() const { + return (microseconds_ + (microseconds_ >= 0 ? 500000 : -500000)) / 1000000; + } + constexpr int64_t UnsafeMillis() const { + return (microseconds_ + (microseconds_ >= 0 ? 500 : -500)) / 1000; + } int64_t microseconds_; }; @@ -220,7 +252,6 @@ inline TimeDelta operator*(const int32_t& scalar, const TimeDelta& delta) { inline TimeDelta operator/(const TimeDelta& delta, const int64_t& scalar) { return TimeDelta::us(delta.us() / scalar); } - std::string ToString(const TimeDelta& value); } // namespace webrtc diff --git a/api/units/time_delta_unittest.cc b/api/units/time_delta_unittest.cc index a858f78482..9eddee73ad 100644 --- a/api/units/time_delta_unittest.cc +++ b/api/units/time_delta_unittest.cc @@ -15,12 +15,24 @@ namespace webrtc { namespace test { TEST(TimeDeltaTest, ConstExpr) { + constexpr int64_t kValue = -12345; constexpr TimeDelta kTimeDeltaZero = TimeDelta::Zero(); constexpr TimeDelta kTimeDeltaPlusInf = TimeDelta::PlusInfinity(); constexpr TimeDelta kTimeDeltaMinusInf = TimeDelta::MinusInfinity(); static_assert(kTimeDeltaZero.IsZero(), ""); static_assert(kTimeDeltaPlusInf.IsPlusInfinity(), ""); static_assert(kTimeDeltaMinusInf.IsMinusInfinity(), ""); + static_assert(kTimeDeltaPlusInf.ms_or(-1) == -1, ""); + + static_assert(kTimeDeltaPlusInf > kTimeDeltaZero, ""); + + constexpr TimeDelta kTimeDeltaSeconds = TimeDelta::Seconds(); + constexpr TimeDelta kTimeDeltaMs = TimeDelta::Millis(); + constexpr TimeDelta kTimeDeltaUs = TimeDelta::Micros(); + + static_assert(kTimeDeltaSeconds.seconds_or(0) == kValue, ""); + static_assert(kTimeDeltaMs.ms_or(0) == kValue, ""); + static_assert(kTimeDeltaUs.us_or(0) == kValue, ""); } TEST(TimeDeltaTest, GetBackSameValues) { diff --git a/api/units/timestamp.h b/api/units/timestamp.h index 6f4dff88bc..1b5e84ff60 100644 --- a/api/units/timestamp.h +++ b/api/units/timestamp.h @@ -35,6 +35,24 @@ class Timestamp { static constexpr Timestamp Infinity() { return Timestamp(timestamp_impl::kPlusInfinityVal); } + template + static constexpr Timestamp Seconds() { + static_assert(seconds >= 0, ""); + static_assert(seconds < timestamp_impl::kPlusInfinityVal / 1000000, ""); + return Timestamp(seconds * 1000000); + } + template + static constexpr Timestamp Millis() { + static_assert(ms >= 0, ""); + static_assert(ms < timestamp_impl::kPlusInfinityVal / 1000, ""); + return Timestamp(ms * 1000); + } + template + static constexpr Timestamp Micros() { + static_assert(us >= 0, ""); + static_assert(us < timestamp_impl::kPlusInfinityVal, ""); + return Timestamp(us); + } template < typename T, @@ -92,11 +110,13 @@ class Timestamp { template typename std::enable_if::value, T>::type seconds() const { - return rtc::dchecked_cast((us() + 500000) / 1000000); + RTC_DCHECK(IsFinite()); + return rtc::dchecked_cast(UnsafeSeconds()); } template typename std::enable_if::value, T>::type ms() const { - return rtc::dchecked_cast((us() + 500) / 1000); + RTC_DCHECK(IsFinite()); + return rtc::dchecked_cast(UnsafeMillis()); } template typename std::enable_if::value, T>::type us() const { @@ -105,23 +125,29 @@ class Timestamp { } template - typename std::enable_if::value, T>::type seconds() - const { + constexpr typename std::enable_if::value, T>::type + seconds() const { return us() * 1e-6; } template - typename std::enable_if::value, T>::type ms() - const { + constexpr typename std::enable_if::value, T>::type + ms() const { return us() * 1e-3; } template - typename std::enable_if::value, T>::type us() - const { - if (IsInfinite()) { - return std::numeric_limits::infinity(); - } else { - return microseconds_; - } + constexpr typename std::enable_if::value, T>::type + us() const { + return IsInfinite() ? std::numeric_limits::infinity() : microseconds_; + } + + constexpr int64_t seconds_or(int64_t fallback_value) const { + return IsFinite() ? UnsafeSeconds() : fallback_value; + } + constexpr int64_t ms_or(int64_t fallback_value) const { + return IsFinite() ? UnsafeMillis() : fallback_value; + } + constexpr int64_t us_or(int64_t fallback_value) const { + return IsFinite() ? microseconds_ : fallback_value; } constexpr bool IsInfinite() const { @@ -145,27 +171,33 @@ class Timestamp { microseconds_ += other.us(); return *this; } - bool operator==(const Timestamp& other) const { + constexpr bool operator==(const Timestamp& other) const { return microseconds_ == other.microseconds_; } - bool operator!=(const Timestamp& other) const { + constexpr bool operator!=(const Timestamp& other) const { return microseconds_ != other.microseconds_; } - bool operator<=(const Timestamp& other) const { + constexpr bool operator<=(const Timestamp& other) const { return microseconds_ <= other.microseconds_; } - bool operator>=(const Timestamp& other) const { + constexpr bool operator>=(const Timestamp& other) const { return microseconds_ >= other.microseconds_; } - bool operator>(const Timestamp& other) const { + constexpr bool operator>(const Timestamp& other) const { return microseconds_ > other.microseconds_; } - bool operator<(const Timestamp& other) const { + constexpr bool operator<(const Timestamp& other) const { return microseconds_ < other.microseconds_; } private: explicit constexpr Timestamp(int64_t us) : microseconds_(us) {} + constexpr int64_t UnsafeSeconds() const { + return (microseconds_ + 500000) / 1000000; + } + constexpr int64_t UnsafeMillis() const { + return (microseconds_ + 500) / 1000; + } int64_t microseconds_; }; diff --git a/api/units/timestamp_unittest.cc b/api/units/timestamp_unittest.cc index 7dfe6695d2..db894cca44 100644 --- a/api/units/timestamp_unittest.cc +++ b/api/units/timestamp_unittest.cc @@ -14,8 +14,24 @@ namespace webrtc { namespace test { TEST(TimestampTest, ConstExpr) { + constexpr int64_t kValue = 12345; constexpr Timestamp kTimestampInf = Timestamp::Infinity(); static_assert(kTimestampInf.IsInfinite(), ""); + static_assert(kTimestampInf.ms_or(-1) == -1, ""); + + constexpr Timestamp kTimestampSeconds = Timestamp::Seconds(); + constexpr Timestamp kTimestampMs = Timestamp::Millis(); + constexpr Timestamp kTimestampUs = Timestamp::Micros(); + + static_assert(kTimestampSeconds.seconds_or(0) == kValue, ""); + static_assert(kTimestampMs.ms_or(0) == kValue, ""); + static_assert(kTimestampUs.us_or(0) == kValue, ""); + + static_assert(kTimestampMs > kTimestampUs, ""); + + EXPECT_EQ(kTimestampSeconds.seconds(), kValue); + EXPECT_EQ(kTimestampMs.ms(), kValue); + EXPECT_EQ(kTimestampUs.us(), kValue); } TEST(TimestampTest, GetBackSameValues) {