From cd578dbf5e59b02579435b0f75c251abe672ae1f Mon Sep 17 00:00:00 2001 From: Yedidya Feldblum Date: Thu, 5 Jan 2017 09:54:44 -0800 Subject: [PATCH] Backport ceil, floor, round from C++17 std::chrono Summary: [Folly] Backport `ceil`, `floor`, `round` from C++17 `std::chrono`. These functions for operating on `std::chrono::duration` and `std::chrono::time_point` values are useful Note: The implementations are derived from cppreference.com, which has a notice listing all content there as licensed under CC-BY-SA. Reviewed By: ericniebler Differential Revision: D4375603 fbshipit-source-id: 36a098bf5f75db071c1670518fc42bbc6df2817d --- folly/Chrono.h | 154 ++++++++++++++++++++++++++++++++++++++ folly/Makefile.am | 1 + folly/test/ChronoTest.cpp | 76 +++++++++++++++++++ folly/test/Makefile.am | 4 + 4 files changed, 235 insertions(+) create mode 100644 folly/Chrono.h create mode 100644 folly/test/ChronoTest.cpp diff --git a/folly/Chrono.h b/folly/Chrono.h new file mode 100644 index 00000000..852a74b7 --- /dev/null +++ b/folly/Chrono.h @@ -0,0 +1,154 @@ +/* + * Copyright 2017 Facebook, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include + +/*** + * include or backport: + * * std::chrono::ceil + * * std::chrono::floor + * * std::chrono::round + */ + +#if __cpp_lib_chrono >= 201510 || _MSC_VER + +namespace folly { +namespace chrono { + +/* using override */ using std::chrono::ceil; +/* using override */ using std::chrono::floor; +/* using override */ using std::chrono::round; +} +} + +#else + +namespace folly { +namespace chrono { + +namespace detail { + +// from: http://en.cppreference.com/w/cpp/chrono/duration/ceil, CC-BY-SA +template +struct is_duration : std::false_type {}; +template +struct is_duration> : std::true_type {}; + +template +constexpr To ceil_impl(Duration const& d, To const& t) { + return t < d ? t + To{1} : t; +} + +template +constexpr To floor_impl(Duration const& d, To const& t) { + return t > d ? t - To{1} : t; +} + +template +constexpr To round_impl(To const& t0, To const& t1, Diff diff0, Diff diff1) { + return diff0 < diff1 ? t0 : diff1 < diff0 ? t1 : t0.count() & 1 ? t1 : t0; +} + +template +constexpr To round_impl(Duration const& d, To const& t0, To const& t1) { + return round_impl(t0, t1, d - t0, t1 - d); +} + +template +constexpr To round_impl(Duration const& d, To const& t0) { + return round_impl(d, t0, t0 + To{1}); +} +} + +// mimic: std::chrono::ceil, C++17 +// from: http://en.cppreference.com/w/cpp/chrono/duration/ceil, CC-BY-SA +template < + typename To, + typename Rep, + typename Period, + typename = typename std::enable_if::value>::type> +constexpr To ceil(std::chrono::duration const& d) { + return detail::ceil_impl(d, std::chrono::duration_cast(d)); +} + +// mimic: std::chrono::ceil, C++17 +// from: http://en.cppreference.com/w/cpp/chrono/time_point/ceil, CC-BY-SA +template < + typename To, + typename Clock, + typename Duration, + typename = typename std::enable_if::value>::type> +constexpr std::chrono::time_point ceil( + std::chrono::time_point const& tp) { + return std::chrono::time_point{ceil(tp.time_since_epoch())}; +} + +// mimic: std::chrono::floor, C++17 +// from: http://en.cppreference.com/w/cpp/chrono/duration/floor, CC-BY-SA +template < + typename To, + typename Rep, + typename Period, + typename = typename std::enable_if::value>::type> +constexpr To floor(std::chrono::duration const& d) { + return detail::floor_impl(d, std::chrono::duration_cast(d)); +} + +// mimic: std::chrono::floor, C++17 +// from: http://en.cppreference.com/w/cpp/chrono/time_point/floor, CC-BY-SA +template < + typename To, + typename Clock, + typename Duration, + typename = typename std::enable_if::value>::type> +constexpr std::chrono::time_point floor( + std::chrono::time_point const& tp) { + return std::chrono::time_point{floor(tp.time_since_epoch())}; +} + +// mimic: std::chrono::round, C++17 +// from: http://en.cppreference.com/w/cpp/chrono/duration/round, CC-BY-SA +template < + typename To, + typename Rep, + typename Period, + typename = typename std::enable_if< + detail::is_duration::value && + !std::chrono::treat_as_floating_point::value>::type> +constexpr To round(std::chrono::duration const& d) { + return detail::round_impl(d, floor(d)); +} + +// mimic: std::chrono::round, C++17 +// from: http://en.cppreference.com/w/cpp/chrono/time_point/round, CC-BY-SA +template < + typename To, + typename Clock, + typename Duration, + typename = typename std::enable_if< + detail::is_duration::value && + !std::chrono::treat_as_floating_point::value>::type> +constexpr std::chrono::time_point round( + std::chrono::time_point const& tp) { + return std::chrono::time_point{round(tp.time_since_epoch())}; +} +} +} + +#endif diff --git a/folly/Makefile.am b/folly/Makefile.am index 52cb69fa..dd6e6bc0 100644 --- a/folly/Makefile.am +++ b/folly/Makefile.am @@ -43,6 +43,7 @@ nobase_follyinclude_HEADERS = \ CachelinePadded.h \ CallOnce.h \ Checksum.h \ + Chrono.h \ ClockGettimeWrappers.h \ ConcurrentSkipList.h \ ConcurrentSkipList-inl.h \ diff --git a/folly/test/ChronoTest.cpp b/folly/test/ChronoTest.cpp new file mode 100644 index 00000000..9142ab6a --- /dev/null +++ b/folly/test/ChronoTest.cpp @@ -0,0 +1,76 @@ +/* + * Copyright 2017 Facebook, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include + +using namespace std::chrono; +using namespace folly::chrono; + +namespace { + +class ChronoTest : public testing::Test {}; +} + +TEST_F(ChronoTest, ceil_duration) { + EXPECT_EQ(seconds(7), ceil(seconds(7))); + EXPECT_EQ(seconds(7), ceil(milliseconds(7000))); + EXPECT_EQ(seconds(7), ceil(milliseconds(6200))); +} + +TEST_F(ChronoTest, ceil_time_point) { + auto const point = steady_clock::time_point{}; + EXPECT_EQ(point + seconds(7), ceil(point + seconds(7))); + EXPECT_EQ(point + seconds(7), ceil(point + milliseconds(7000))); + EXPECT_EQ(point + seconds(7), ceil(point + milliseconds(6200))); +} + +TEST_F(ChronoTest, floor_duration) { + EXPECT_EQ(seconds(7), floor(seconds(7))); + EXPECT_EQ(seconds(7), floor(milliseconds(7000))); + EXPECT_EQ(seconds(7), floor(milliseconds(7800))); +} + +TEST_F(ChronoTest, floor_time_point) { + auto const point = steady_clock::time_point{}; + EXPECT_EQ(point + seconds(7), floor(point + seconds(7))); + EXPECT_EQ(point + seconds(7), floor(point + milliseconds(7000))); + EXPECT_EQ(point + seconds(7), floor(point + milliseconds(7800))); +} + +TEST_F(ChronoTest, round_duration) { + EXPECT_EQ(seconds(7), round(seconds(7))); + EXPECT_EQ(seconds(6), round(milliseconds(6200))); + EXPECT_EQ(seconds(6), round(milliseconds(6500))); + EXPECT_EQ(seconds(7), round(milliseconds(6800))); + EXPECT_EQ(seconds(7), round(milliseconds(7000))); + EXPECT_EQ(seconds(7), round(milliseconds(7200))); + EXPECT_EQ(seconds(8), round(milliseconds(7500))); + EXPECT_EQ(seconds(8), round(milliseconds(7800))); +} + +TEST_F(ChronoTest, round_time_point) { + auto const point = steady_clock::time_point{}; + EXPECT_EQ(point + seconds(7), round(point + seconds(7))); + EXPECT_EQ(point + seconds(6), round(point + milliseconds(6200))); + EXPECT_EQ(point + seconds(6), round(point + milliseconds(6500))); + EXPECT_EQ(point + seconds(7), round(point + milliseconds(6800))); + EXPECT_EQ(point + seconds(7), round(point + milliseconds(7000))); + EXPECT_EQ(point + seconds(7), round(point + milliseconds(7200))); + EXPECT_EQ(point + seconds(8), round(point + milliseconds(7500))); + EXPECT_EQ(point + seconds(8), round(point + milliseconds(7800))); +} diff --git a/folly/test/Makefile.am b/folly/test/Makefile.am index 77a3ae84..afc119d4 100644 --- a/folly/test/Makefile.am +++ b/folly/test/Makefile.am @@ -201,6 +201,10 @@ atomic_hash_map_test_SOURCES = AtomicHashMapTest.cpp atomic_hash_map_test_LDADD = libfollytestmain.la $(top_builddir)/libfollybenchmark.la TESTS += atomic_hash_map_test +chrono_test_SOURCES = ChronoTest.cpp +chrono_test_LDADD = libfollytestmain.la +TESTS += chrono_test + format_test_SOURCES = FormatTest.cpp format_test_LDADD = libfollytestmain.la $(top_builddir)/libfollybenchmark.la TESTS += format_test -- 2.34.1