diff options
author | Billy Donahue <billy.donahue@mongodb.com> | 2021-01-16 12:54:43 -0500 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2021-01-22 09:27:32 +0000 |
commit | 332df3bdd7e6d5f7f9a09c9bd0861f5370bc667c (patch) | |
tree | 8e6186b4036e44ff2b1e1b80bc3ac32dca51d5d6 /src | |
parent | 760f6bc37313888fa755235a61d9962624c688f4 (diff) | |
download | mongo-332df3bdd7e6d5f7f9a09c9bd0861f5370bc667c.tar.gz |
SERVER-47709 add deduceChronoDuration to duration.h
Diffstat (limited to 'src')
-rw-r--r-- | src/mongo/util/duration.h | 50 | ||||
-rw-r--r-- | src/mongo/util/duration_test.cpp | 43 |
2 files changed, 88 insertions, 5 deletions
diff --git a/src/mongo/util/duration.h b/src/mongo/util/duration.h index ed187c0ed71..51b2eae14b6 100644 --- a/src/mongo/util/duration.h +++ b/src/mongo/util/duration.h @@ -30,10 +30,12 @@ #pragma once #include <cstdint> -#include <fmt/format.h> #include <iosfwd> #include <limits> #include <ratio> +#include <type_traits> + +#include <fmt/format.h> #include "mongo/base/static_assert.h" #include "mongo/platform/overflow_arithmetic.h" @@ -60,6 +62,13 @@ using Minutes = Duration<std::ratio<60>>; using Hours = Duration<std::ratio<3600>>; using Days = Duration<std::ratio<86400>>; +namespace duration_detail { +template <typename> +inline constexpr bool isMongoDuration = false; +template <typename... Ts> +inline constexpr bool isMongoDuration<Duration<Ts...>> = true; +} // namespace duration_detail + // // Streaming output operators for common duration types. Writes the numerical value followed by // an abbreviated unit, without a space. @@ -81,7 +90,9 @@ using HigherPrecisionDuration = * the ToDuration. For example, Seconds::max() cannot be represented as a Milliseconds, and so * attempting to cast that value to Milliseconds will throw an exception. */ -template <typename ToDuration, typename FromPeriod> +template <typename ToDuration, + typename FromPeriod, + std::enable_if_t<duration_detail::isMongoDuration<ToDuration>, int> = 0> constexpr ToDuration duration_cast(const Duration<FromPeriod>& from) { using FromOverTo = std::ratio_divide<FromPeriod, typename ToDuration::period>; if (ToDuration::template isHigherPrecisionThan<Duration<FromPeriod>>()) { @@ -94,7 +105,10 @@ constexpr ToDuration duration_cast(const Duration<FromPeriod>& from) { return ToDuration{from.count() / FromOverTo::den}; } -template <typename ToDuration, typename FromRep, typename FromPeriod> +template <typename ToDuration, + typename FromRep, + typename FromPeriod, + std::enable_if_t<duration_detail::isMongoDuration<ToDuration>, int> = 0> constexpr ToDuration duration_cast(const stdx::chrono::duration<FromRep, FromPeriod>& d) { return duration_cast<ToDuration>(Duration<FromPeriod>{d.count()}); } @@ -488,4 +502,34 @@ StringBuilderImpl<Allocator>& operator<<(StringBuilderImpl<Allocator>& os, Durat return streamPut(os, dp); } +/** + * Make a std::chrono::duration from an arithmetic expression and a period ratio. + * This does not do any math or precision changes. It's just a type-deduced wrapper + * that attaches a period to a number for typesafety. The output std::chrono::duration + * will retain the Rep type and value of the input argument. + * + * E.g: + * int waited = 123; // unitless, type-unsafe millisecond count. + * auto dur = deduceChronoDuration<std::milli>(waited); + * static_assert(std::is_same_v<decltype(dur), + * std::chrono::duration<int, std::milli>>); + * invariant(dur.count() == 123); + * + * Note that std::chrono::duration<int, std::milli> is not std::milliseconds, + * which has a different (unspecified) Rep type. + * + * Then mongo::duration_cast can convert the deduced std::chrono::duration to + * mongo::Duration, or `std::chrono::duration_cast` be used to adjust the rep + * to create a more canonical std::chrono::duration: + * + * auto durMongo = duration_cast<Milliseconds>(dur); + * auto durChrono = duration_cast<std::milliseconds>(dur); + * + * Order the cast operations carefully to avoid losing range or precision. + */ +template <typename Per = std::ratio<1>, typename Rep> +constexpr auto deduceChronoDuration(const Rep& count) { + return stdx::chrono::duration<Rep, Per>{count}; +} + } // namespace mongo diff --git a/src/mongo/util/duration_test.cpp b/src/mongo/util/duration_test.cpp index a4b70439179..2db2373bcf9 100644 --- a/src/mongo/util/duration_test.cpp +++ b/src/mongo/util/duration_test.cpp @@ -29,13 +29,18 @@ #include "mongo/platform/basic.h" +#include "mongo/util/duration.h" + +#include <fmt/format.h> + #include "mongo/stdx/chrono.h" #include "mongo/unittest/unittest.h" -#include "mongo/util/duration.h" namespace mongo { namespace { +using namespace fmt::literals; + // The DurationTestSameType Compare* tests server to check the implementation of the comparison // operators as well as the compare() method, and so sometimes must explicitly check ASSERT_FALSE(v1 // OP v2). The DurationTestDifferentTypes Compare* tests rely on the fact that the operators all @@ -155,7 +160,7 @@ TEST(DurationCast, DurationCastConstexpr) { ASSERT_EQ(2, secs.count()); } - // Converting from std::chrono::duration to Duration is constexpr. + // Converting from stdx::chrono::duration to Duration is constexpr. { constexpr auto ms = duration_cast<Milliseconds>(stdx::chrono::seconds(2)); ASSERT_EQ(2000, ms.count()); @@ -247,5 +252,39 @@ TEST(DurationArithmetic, DivideOverflowThrows) { ASSERT_THROWS_CODE(Milliseconds::min() / -1, AssertionException, ErrorCodes::DurationOverflow); } +/** Calls `deduceChronoDuration<Period>(in)`, asserts that it returns `equivalent`. */ +template <typename Period = void, typename In, typename Equivalent> +auto validateDeduce(In in, Equivalent equivalent) { + static constexpr bool useDefaultPeriod = std::is_same_v<Period, void>; + using OutPeriod = std::conditional_t<useDefaultPeriod, std::ratio<1>, Period>; + auto deduced = [&] { + if constexpr (useDefaultPeriod) { + return deduceChronoDuration(in); + } else { + return deduceChronoDuration<Period>(in); + } + }(); + ASSERT((std::is_same_v<decltype(deduced), Equivalent>)) << "expected:{}got:{}"_format( + demangleName(typeid(Equivalent)), demangleName(typeid(deduced))); + ASSERT_EQ(deduced.count(), equivalent.count()) + << "in:{}, equivalent:{}, deduced:{}"_format(in, equivalent.count(), deduced.count()); +} + +TEST(DeduceChronoDuration, DefaultPeriod) { + validateDeduce(3600, stdx::chrono::duration<int>(3600)); + validateDeduce(3600, stdx::chrono::duration<int>(3600)); + validateDeduce((short)3600, stdx::chrono::duration<short>(3600)); + validateDeduce(3600u, stdx::chrono::duration<unsigned>(3600)); + validateDeduce(3600., stdx::chrono::duration<double>(3600)); + validateDeduce(3600.5, stdx::chrono::duration<double>(3600.5)); + validateDeduce(-3600.5, stdx::chrono::duration<double>(-3600.5)); +} + +TEST(DeduceChronoDuration, ExplicitPeriod) { + validateDeduce<std::milli>(123, stdx::chrono::duration<int, std::milli>(123)); + validateDeduce<std::nano>(123, stdx::chrono::duration<int, std::nano>(123)); + validateDeduce<std::ratio<45, 123>>(50, stdx::chrono::duration<int, std::ratio<45, 123>>(50)); +} + } // namespace } // namespace mongo |