Extract non-portability constexpr math functions to new header
authorYedidya Feldblum <yfeldblum@fb.com>
Thu, 31 Aug 2017 02:26:32 +0000 (19:26 -0700)
committerFacebook Github Bot <facebook-github-bot@users.noreply.github.com>
Thu, 31 Aug 2017 02:43:36 +0000 (19:43 -0700)
Summary:
[Folly] Extract non-portability `constexpr` math functions to new header.

This new header, `folly/ConstexprMath.h`, is specifically for compile-time-computable math functions. We start with `min`, `max`, `abs`, and `log2`.

Included substantive changes:
* Add new tests for `constexpr_strlen`, which remains in the portability header.
* Make `constexpr_min` and `constexpr_max` variadic.
* Make `constexpr_log2` tail-recursive, remove `const_log2` in `FixedString.h`, and move the related comment.

Reviewed By: Orvid

Differential Revision: D5739715

fbshipit-source-id: 29d3cc846ce98bb4bdddcc8b0fa80e4d32075fe0

folly/ConstexprMath.h [new file with mode: 0644]
folly/FixedString.h
folly/Format.cpp
folly/Makefile.am
folly/fibers/FiberManager.cpp
folly/portability/Constexpr.h
folly/portability/test/ConstexprTest.cpp
folly/small_vector.h
folly/test/ConstexprMathTest.cpp [new file with mode: 0644]
folly/test/Makefile.am

diff --git a/folly/ConstexprMath.h b/folly/ConstexprMath.h
new file mode 100644 (file)
index 0000000..1650c02
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * 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 <type_traits>
+
+namespace folly {
+
+// TLDR: Prefer using operator< for ordering. And when
+// a and b are equivalent objects, we return b to make
+// sorting stable.
+// See http://stepanovpapers.com/notes.pdf for details.
+template <typename T>
+constexpr T constexpr_max(T a) {
+  return a;
+}
+template <typename T, typename... Ts>
+constexpr T constexpr_max(T a, T b, Ts... ts) {
+  return b < a ? constexpr_max(a, ts...) : constexpr_max(b, ts...);
+}
+
+// When a and b are equivalent objects, we return a to
+// make sorting stable.
+template <typename T>
+constexpr T constexpr_min(T a) {
+  return a;
+}
+template <typename T, typename... Ts>
+constexpr T constexpr_min(T a, T b, Ts... ts) {
+  return b < a ? constexpr_max(b, ts...) : constexpr_max(a, ts...);
+}
+
+namespace detail {
+
+template <typename T, typename = void>
+struct constexpr_abs_helper {};
+
+template <typename T>
+struct constexpr_abs_helper<
+    T,
+    typename std::enable_if<std::is_floating_point<T>::value>::type> {
+  static constexpr T go(T t) {
+    return t < static_cast<T>(0) ? -t : t;
+  }
+};
+
+template <typename T>
+struct constexpr_abs_helper<
+    T,
+    typename std::enable_if<
+        std::is_integral<T>::value && !std::is_same<T, bool>::value &&
+        std::is_unsigned<T>::value>::type> {
+  static constexpr T go(T t) {
+    return t;
+  }
+};
+
+template <typename T>
+struct constexpr_abs_helper<
+    T,
+    typename std::enable_if<
+        std::is_integral<T>::value && !std::is_same<T, bool>::value &&
+        std::is_signed<T>::value>::type> {
+  static constexpr typename std::make_unsigned<T>::type go(T t) {
+    return typename std::make_unsigned<T>::type(t < static_cast<T>(0) ? -t : t);
+  }
+};
+} // namespace detail
+
+template <typename T>
+constexpr auto constexpr_abs(T t)
+    -> decltype(detail::constexpr_abs_helper<T>::go(t)) {
+  return detail::constexpr_abs_helper<T>::go(t);
+}
+
+namespace detail {
+template <typename T>
+constexpr T constexpr_log2(T a, T e) {
+  return e == T(1) ? a : constexpr_log2(a + T(1), e / T(2));
+}
+} // namespace detail
+
+template <typename T>
+constexpr T constexpr_log2(T t) {
+  return detail::constexpr_log2(T(0), t);
+}
+
+} // namespace folly
index 8964a4b16ed0f7b55ad2e5ab6abe6e1c10c65fd2..1465070cbb77881ece63c6248f5a85f42c5b245e 100644 (file)
@@ -28,6 +28,7 @@
 #include <type_traits>
 #include <utility>
 
+#include <folly/ConstexprMath.h>
 #include <folly/Portability.h>
 #include <folly/Range.h>
 #include <folly/Utility.h>
@@ -314,18 +315,6 @@ FOLLY_CPP14_CONSTEXPR void constexpr_swap(T& a, T& b) noexcept(
   b = std::move(tmp);
 }
 
-// FUTURE: use const_log2 to fold instantiations of BasicFixedString together.
-// All BasicFixedString<C, N> instantiations could share the implementation
-// of BasicFixedString<C, M>, where M is the next highest power of 2 after N.
-//
-// Also, because of alignment of the data_ and size_ members, N should never be
-// smaller than `(alignof(std::size_t)/sizeof(C))-1` (-1 because of the null
-// terminator). OR, create a specialization for BasicFixedString<C, 0u> that
-// does not have a size_ member, since it is unnecessary.
-constexpr std::size_t const_log2(std::size_t N, std::size_t log2 = 0u) {
-  return N / 2u == 0u ? log2 : const_log2(N / 2u, log2 + 1u);
-}
-
 // For constexpr reverse iteration over a BasicFixedString
 template <class T>
 struct ReverseIterator {
@@ -528,6 +517,15 @@ class BasicFixedString : private detail::fixedstring::FixedStringBase {
   friend class BasicFixedString;
   friend struct detail::fixedstring::Helper;
 
+  // FUTURE: use constexpr_log2 to fold instantiations of BasicFixedString
+  // together. All BasicFixedString<C, N> instantiations could share the
+  // implementation of BasicFixedString<C, M>, where M is the next highest power
+  // of 2 after N.
+  //
+  // Also, because of alignment of the data_ and size_ members, N should never
+  // be smaller than `(alignof(std::size_t)/sizeof(C))-1` (-1 because of the
+  // null terminator). OR, create a specialization for BasicFixedString<C, 0u>
+  // that does not have a size_ member, since it is unnecessary.
   Char data_[N + 1u]; // +1 for the null terminator
   std::size_t size_; // Nbr of chars, not incl. null terminator. size_ <= N.
 
index 9a711e4601e8a47c6872580f7733f0f1100d9c8d..bbcd2d004dec69f155331f26652c8ab3d963e616 100644 (file)
@@ -16,8 +16,8 @@
 
 #include <folly/Format.h>
 
+#include <folly/ConstexprMath.h>
 #include <folly/CppAttributes.h>
-#include <folly/portability/Constexpr.h>
 
 #include <double-conversion/double-conversion.h>
 
index e42b0eafb074c1e3df39734d4d99c394df5a1ba8..442c1e1c30a7f8cb117feb0645752f5a2a13fe5c 100644 (file)
@@ -60,6 +60,7 @@ nobase_follyinclude_HEADERS = \
        concurrency/ConcurrentHashMap.h \
        concurrency/CoreCachedSharedPtr.h \
        concurrency/detail/ConcurrentHashMap-detail.h \
+       ConstexprMath.h \
        detail/AtomicHashUtils.h \
        detail/AtomicUnorderedMapUtils.h \
        detail/AtomicUtils.h \
index 9a3d2446b897ae810af925573865d751463ed0c5..9155078c42364869d21cf93d5fc58f672f0b70b5 100644 (file)
@@ -25,6 +25,7 @@
 #include <folly/fibers/Fiber.h>
 #include <folly/fibers/LoopController.h>
 
+#include <folly/ConstexprMath.h>
 #include <folly/SingletonThreadLocal.h>
 #include <folly/portability/SysSyscall.h>
 #include <folly/portability/Unistd.h>
index a7620918bf5519eed75eeb0c56407f3c46516018..64005e3c45c26d2a5e2f846e99145ba9d5735adf 100644 (file)
 
 namespace folly {
 
-// TLDR: Prefer using operator< for ordering. And when
-// a and b are equivalent objects, we return b to make
-// sorting stable.
-// See http://stepanovpapers.com/notes.pdf for details.
-template <typename T>
-constexpr T constexpr_max(T a, T b) {
-  return b < a ? a : b;
-}
-
-// When a and b are equivalent objects, we return a to
-// make sorting stable.
-template <typename T>
-constexpr T constexpr_min(T a, T b) {
-  return b < a ? b : a;
-}
-
-namespace detail {
-
-template <typename T, typename = void>
-struct constexpr_abs_helper {};
-
-template <typename T>
-struct constexpr_abs_helper<
-    T,
-    typename std::enable_if<std::is_floating_point<T>::value>::type> {
-  static constexpr T go(T t) {
-    return t < static_cast<T>(0) ? -t : t;
-  }
-};
-
-template <typename T>
-struct constexpr_abs_helper<
-    T,
-    typename std::enable_if<
-        std::is_integral<T>::value && !std::is_same<T, bool>::value &&
-        std::is_unsigned<T>::value>::type> {
-  static constexpr T go(T t) {
-    return t;
-  }
-};
-
-template <typename T>
-struct constexpr_abs_helper<
-    T,
-    typename std::enable_if<
-        std::is_integral<T>::value && !std::is_same<T, bool>::value &&
-        std::is_signed<T>::value>::type> {
-  static constexpr typename std::make_unsigned<T>::type go(T t) {
-    return typename std::make_unsigned<T>::type(t < static_cast<T>(0) ? -t : t);
-  }
-};
-} // namespace detail
-
-template <typename T>
-constexpr auto constexpr_abs(T t)
-    -> decltype(detail::constexpr_abs_helper<T>::go(t)) {
-  return detail::constexpr_abs_helper<T>::go(t);
-}
-
-template <typename T>
-constexpr T constexpr_log2(T t) {
-  return t == T(1) ? T(0) : T(1) + constexpr_log2(t / T(2));
-}
-
 namespace detail {
 
 template <typename Char>
index d2f6dbe3a17390865d773b2b7535fd330a02dcb3..2891d797a9000d3dc6c635bdb00cd1921a57bfa2 100644 (file)
@@ -23,72 +23,16 @@ namespace {
 class ConstexprTest : public testing::Test {};
 }
 
-TEST_F(ConstexprTest, constexpr_abs_unsigned) {
-  constexpr auto v = uint32_t(17);
-  constexpr auto a = folly::constexpr_abs(v);
-  EXPECT_EQ(17, a);
-  EXPECT_TRUE((std::is_same<const uint32_t, decltype(a)>::value));
-}
-
-TEST_F(ConstexprTest, constexpr_abs_signed_positive) {
-  constexpr auto v = int32_t(17);
-  constexpr auto a = folly::constexpr_abs(v);
-  EXPECT_EQ(17, a);
-  EXPECT_TRUE((std::is_same<const uint32_t, decltype(a)>::value));
-}
-
-TEST_F(ConstexprTest, constexpr_abs_signed_negative) {
-  constexpr auto v = int32_t(-17);
-  constexpr auto a = folly::constexpr_abs(v);
-  EXPECT_EQ(17, a);
-  EXPECT_TRUE((std::is_same<const uint32_t, decltype(a)>::value));
-}
-
-TEST_F(ConstexprTest, constexpr_abs_float_positive) {
-  constexpr auto v = 17.5f;
-  constexpr auto a = folly::constexpr_abs(v);
-  EXPECT_EQ(17.5, a);
-  EXPECT_TRUE((std::is_same<const float, decltype(a)>::value));
-}
-
-TEST_F(ConstexprTest, constexpr_abs_float_negative) {
-  constexpr auto v = -17.5f;
-  constexpr auto a = folly::constexpr_abs(v);
-  EXPECT_EQ(17.5, a);
-  EXPECT_TRUE((std::is_same<const float, decltype(a)>::value));
-}
-
-TEST_F(ConstexprTest, constexpr_abs_double_positive) {
-  constexpr auto v = 17.5;
-  constexpr auto a = folly::constexpr_abs(v);
-  EXPECT_EQ(17.5, a);
-  EXPECT_TRUE((std::is_same<const double, decltype(a)>::value));
-}
-
-TEST_F(ConstexprTest, constexpr_abs_double_negative) {
-  constexpr auto v = -17.5;
-  constexpr auto a = folly::constexpr_abs(v);
-  EXPECT_EQ(17.5, a);
-  EXPECT_TRUE((std::is_same<const double, decltype(a)>::value));
-}
-
-TEST_F(ConstexprTest, constexpr_log2_1) {
-  constexpr auto v = 1ull;
-  constexpr auto a = folly::constexpr_log2(v);
-  EXPECT_EQ(0ull, a);
-  EXPECT_TRUE((std::is_same<decltype(v), decltype(a)>::value));
-}
-
-TEST_F(ConstexprTest, constexpr_log2_2) {
-  constexpr auto v = 2ull;
-  constexpr auto a = folly::constexpr_log2(v);
-  EXPECT_EQ(1ull, a);
-  EXPECT_TRUE((std::is_same<decltype(v), decltype(a)>::value));
-}
-
-TEST_F(ConstexprTest, constexpr_log2_64) {
-  constexpr auto v = 64ull;
-  constexpr auto a = folly::constexpr_log2(v);
-  EXPECT_EQ(6ull, a);
-  EXPECT_TRUE((std::is_same<decltype(v), decltype(a)>::value));
+TEST_F(ConstexprTest, constexpr_strlen_cstr) {
+  constexpr auto v = "hello";
+  constexpr auto a = folly::constexpr_strlen(v);
+  EXPECT_EQ(5, a);
+  EXPECT_TRUE((std::is_same<const size_t, decltype(a)>::value));
+}
+
+TEST_F(ConstexprTest, constexpr_strlen_ints) {
+  constexpr int v[] = {5, 3, 4, 0, 7};
+  constexpr auto a = folly::constexpr_strlen(v);
+  EXPECT_EQ(3, a);
+  EXPECT_TRUE((std::is_same<const size_t, decltype(a)>::value));
 }
index 64add098f65daddc0c16b81099028dee66f104ce..f70b2a8f009e0feb165ab36fb6e76337463e8c85 100644 (file)
 #include <boost/type_traits.hpp>
 
 #include <folly/Assume.h>
+#include <folly/ConstexprMath.h>
 #include <folly/FormatTraits.h>
 #include <folly/Malloc.h>
 #include <folly/Portability.h>
 #include <folly/SmallLocks.h>
 #include <folly/Traits.h>
 #include <folly/portability/BitsFunctexcept.h>
-#include <folly/portability/Constexpr.h>
 #include <folly/portability/Malloc.h>
 #include <folly/portability/TypeTraits.h>
 
diff --git a/folly/test/ConstexprMathTest.cpp b/folly/test/ConstexprMathTest.cpp
new file mode 100644 (file)
index 0000000..166175a
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * 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/ConstexprMath.h>
+
+#include <folly/portability/GTest.h>
+
+namespace {
+
+class ConstexprMathTest : public testing::Test {};
+}
+
+TEST_F(ConstexprMathTest, constexpr_abs_unsigned) {
+  constexpr auto v = uint32_t(17);
+  constexpr auto a = folly::constexpr_abs(v);
+  EXPECT_EQ(17, a);
+  EXPECT_TRUE((std::is_same<const uint32_t, decltype(a)>::value));
+}
+
+TEST_F(ConstexprMathTest, constexpr_abs_signed_positive) {
+  constexpr auto v = int32_t(17);
+  constexpr auto a = folly::constexpr_abs(v);
+  EXPECT_EQ(17, a);
+  EXPECT_TRUE((std::is_same<const uint32_t, decltype(a)>::value));
+}
+
+TEST_F(ConstexprMathTest, constexpr_abs_signed_negative) {
+  constexpr auto v = int32_t(-17);
+  constexpr auto a = folly::constexpr_abs(v);
+  EXPECT_EQ(17, a);
+  EXPECT_TRUE((std::is_same<const uint32_t, decltype(a)>::value));
+}
+
+TEST_F(ConstexprMathTest, constexpr_abs_float_positive) {
+  constexpr auto v = 17.5f;
+  constexpr auto a = folly::constexpr_abs(v);
+  EXPECT_EQ(17.5, a);
+  EXPECT_TRUE((std::is_same<const float, decltype(a)>::value));
+}
+
+TEST_F(ConstexprMathTest, constexpr_abs_float_negative) {
+  constexpr auto v = -17.5f;
+  constexpr auto a = folly::constexpr_abs(v);
+  EXPECT_EQ(17.5, a);
+  EXPECT_TRUE((std::is_same<const float, decltype(a)>::value));
+}
+
+TEST_F(ConstexprMathTest, constexpr_abs_double_positive) {
+  constexpr auto v = 17.5;
+  constexpr auto a = folly::constexpr_abs(v);
+  EXPECT_EQ(17.5, a);
+  EXPECT_TRUE((std::is_same<const double, decltype(a)>::value));
+}
+
+TEST_F(ConstexprMathTest, constexpr_abs_double_negative) {
+  constexpr auto v = -17.5;
+  constexpr auto a = folly::constexpr_abs(v);
+  EXPECT_EQ(17.5, a);
+  EXPECT_TRUE((std::is_same<const double, decltype(a)>::value));
+}
+
+TEST_F(ConstexprMathTest, constexpr_log2_1) {
+  constexpr auto v = 1ull;
+  constexpr auto a = folly::constexpr_log2(v);
+  EXPECT_EQ(0ull, a);
+  EXPECT_TRUE((std::is_same<decltype(v), decltype(a)>::value));
+}
+
+TEST_F(ConstexprMathTest, constexpr_log2_2) {
+  constexpr auto v = 2ull;
+  constexpr auto a = folly::constexpr_log2(v);
+  EXPECT_EQ(1ull, a);
+  EXPECT_TRUE((std::is_same<decltype(v), decltype(a)>::value));
+}
+
+TEST_F(ConstexprMathTest, constexpr_log2_64) {
+  constexpr auto v = 64ull;
+  constexpr auto a = folly::constexpr_log2(v);
+  EXPECT_EQ(6ull, a);
+  EXPECT_TRUE((std::is_same<decltype(v), decltype(a)>::value));
+}
index ed0fd9775a261fc2ac0827f05abefc97d8aeffb2..f0ca1460fb4448b3c06e01354c51c3fac86a2e53 100644 (file)
@@ -49,6 +49,10 @@ array_test_SOURCES = ArrayTest.cpp
 array_test_LDADD = libfollytestmain.la
 TESTS += array_test
 
+constexpr_math_test_SOURCES = ConstexprMathTest.cpp
+constexpr_math_test_LDADD = libfollytestmain.la
+TESTS += constexpr_math_test
+
 if RUN_ARCH_SPECIFIC_TESTS
 small_locks_test_SOURCES = SmallLocksTest.cpp
 small_locks_test_LDADD = libfollytestmain.la