Backport ceil, floor, round from C++17 std::chrono
authorYedidya Feldblum <yfeldblum@fb.com>
Thu, 5 Jan 2017 17:54:44 +0000 (09:54 -0800)
committerFacebook Github Bot <facebook-github-bot@users.noreply.github.com>
Thu, 5 Jan 2017 18:02:59 +0000 (10:02 -0800)
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 [new file with mode: 0644]
folly/Makefile.am
folly/test/ChronoTest.cpp [new file with mode: 0644]
folly/test/Makefile.am

diff --git a/folly/Chrono.h b/folly/Chrono.h
new file mode 100644 (file)
index 0000000..852a74b
--- /dev/null
@@ -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 <chrono>
+#include <type_traits>
+
+/***
+ *  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 <typename T>
+struct is_duration : std::false_type {};
+template <typename Rep, typename Period>
+struct is_duration<std::chrono::duration<Rep, Period>> : std::true_type {};
+
+template <typename To, typename Duration>
+constexpr To ceil_impl(Duration const& d, To const& t) {
+  return t < d ? t + To{1} : t;
+}
+
+template <typename To, typename Duration>
+constexpr To floor_impl(Duration const& d, To const& t) {
+  return t > d ? t - To{1} : t;
+}
+
+template <typename To, typename Diff>
+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 <typename To, typename Duration>
+constexpr To round_impl(Duration const& d, To const& t0, To const& t1) {
+  return round_impl(t0, t1, d - t0, t1 - d);
+}
+
+template <typename To, typename Duration>
+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<detail::is_duration<To>::value>::type>
+constexpr To ceil(std::chrono::duration<Rep, Period> const& d) {
+  return detail::ceil_impl(d, std::chrono::duration_cast<To>(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<detail::is_duration<To>::value>::type>
+constexpr std::chrono::time_point<Clock, To> ceil(
+    std::chrono::time_point<Clock, Duration> const& tp) {
+  return std::chrono::time_point<Clock, To>{ceil<To>(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<detail::is_duration<To>::value>::type>
+constexpr To floor(std::chrono::duration<Rep, Period> const& d) {
+  return detail::floor_impl(d, std::chrono::duration_cast<To>(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<detail::is_duration<To>::value>::type>
+constexpr std::chrono::time_point<Clock, To> floor(
+    std::chrono::time_point<Clock, Duration> const& tp) {
+  return std::chrono::time_point<Clock, To>{floor<To>(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<To>::value &&
+        !std::chrono::treat_as_floating_point<typename To::rep>::value>::type>
+constexpr To round(std::chrono::duration<Rep, Period> const& d) {
+  return detail::round_impl(d, floor<To>(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<To>::value &&
+        !std::chrono::treat_as_floating_point<typename To::rep>::value>::type>
+constexpr std::chrono::time_point<Clock, To> round(
+    std::chrono::time_point<Clock, Duration> const& tp) {
+  return std::chrono::time_point<Clock, To>{round<To>(tp.time_since_epoch())};
+}
+}
+}
+
+#endif
index 52cb69fa339c9169dbcfe53e446412fe704fe5c3..dd6e6bc0dc98f8749858900e7408d156241d0962 100644 (file)
@@ -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 (file)
index 0000000..9142ab6
--- /dev/null
@@ -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 <folly/Chrono.h>
+
+#include <gtest/gtest.h>
+
+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>(seconds(7)));
+  EXPECT_EQ(seconds(7), ceil<seconds>(milliseconds(7000)));
+  EXPECT_EQ(seconds(7), ceil<seconds>(milliseconds(6200)));
+}
+
+TEST_F(ChronoTest, ceil_time_point) {
+  auto const point = steady_clock::time_point{};
+  EXPECT_EQ(point + seconds(7), ceil<seconds>(point + seconds(7)));
+  EXPECT_EQ(point + seconds(7), ceil<seconds>(point + milliseconds(7000)));
+  EXPECT_EQ(point + seconds(7), ceil<seconds>(point + milliseconds(6200)));
+}
+
+TEST_F(ChronoTest, floor_duration) {
+  EXPECT_EQ(seconds(7), floor<seconds>(seconds(7)));
+  EXPECT_EQ(seconds(7), floor<seconds>(milliseconds(7000)));
+  EXPECT_EQ(seconds(7), floor<seconds>(milliseconds(7800)));
+}
+
+TEST_F(ChronoTest, floor_time_point) {
+  auto const point = steady_clock::time_point{};
+  EXPECT_EQ(point + seconds(7), floor<seconds>(point + seconds(7)));
+  EXPECT_EQ(point + seconds(7), floor<seconds>(point + milliseconds(7000)));
+  EXPECT_EQ(point + seconds(7), floor<seconds>(point + milliseconds(7800)));
+}
+
+TEST_F(ChronoTest, round_duration) {
+  EXPECT_EQ(seconds(7), round<seconds>(seconds(7)));
+  EXPECT_EQ(seconds(6), round<seconds>(milliseconds(6200)));
+  EXPECT_EQ(seconds(6), round<seconds>(milliseconds(6500)));
+  EXPECT_EQ(seconds(7), round<seconds>(milliseconds(6800)));
+  EXPECT_EQ(seconds(7), round<seconds>(milliseconds(7000)));
+  EXPECT_EQ(seconds(7), round<seconds>(milliseconds(7200)));
+  EXPECT_EQ(seconds(8), round<seconds>(milliseconds(7500)));
+  EXPECT_EQ(seconds(8), round<seconds>(milliseconds(7800)));
+}
+
+TEST_F(ChronoTest, round_time_point) {
+  auto const point = steady_clock::time_point{};
+  EXPECT_EQ(point + seconds(7), round<seconds>(point + seconds(7)));
+  EXPECT_EQ(point + seconds(6), round<seconds>(point + milliseconds(6200)));
+  EXPECT_EQ(point + seconds(6), round<seconds>(point + milliseconds(6500)));
+  EXPECT_EQ(point + seconds(7), round<seconds>(point + milliseconds(6800)));
+  EXPECT_EQ(point + seconds(7), round<seconds>(point + milliseconds(7000)));
+  EXPECT_EQ(point + seconds(7), round<seconds>(point + milliseconds(7200)));
+  EXPECT_EQ(point + seconds(8), round<seconds>(point + milliseconds(7500)));
+  EXPECT_EQ(point + seconds(8), round<seconds>(point + milliseconds(7800)));
+}
index 77a3ae8414c65a0e14133d9e9e7e96a9cf276621..afc119d49fd9cfc841bc3919499a266fcc2ed057 100644 (file)
@@ -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