Commit cd578dbf authored by Yedidya Feldblum's avatar Yedidya Feldblum Committed by Facebook Github Bot

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
parent eaa278a5
/*
* 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
...@@ -43,6 +43,7 @@ nobase_follyinclude_HEADERS = \ ...@@ -43,6 +43,7 @@ nobase_follyinclude_HEADERS = \
CachelinePadded.h \ CachelinePadded.h \
CallOnce.h \ CallOnce.h \
Checksum.h \ Checksum.h \
Chrono.h \
ClockGettimeWrappers.h \ ClockGettimeWrappers.h \
ConcurrentSkipList.h \ ConcurrentSkipList.h \
ConcurrentSkipList-inl.h \ ConcurrentSkipList-inl.h \
......
/*
* 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)));
}
...@@ -201,6 +201,10 @@ atomic_hash_map_test_SOURCES = AtomicHashMapTest.cpp ...@@ -201,6 +201,10 @@ atomic_hash_map_test_SOURCES = AtomicHashMapTest.cpp
atomic_hash_map_test_LDADD = libfollytestmain.la $(top_builddir)/libfollybenchmark.la atomic_hash_map_test_LDADD = libfollytestmain.la $(top_builddir)/libfollybenchmark.la
TESTS += atomic_hash_map_test 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_SOURCES = FormatTest.cpp
format_test_LDADD = libfollytestmain.la $(top_builddir)/libfollybenchmark.la format_test_LDADD = libfollytestmain.la $(top_builddir)/libfollybenchmark.la
TESTS += format_test TESTS += format_test
......
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