From 993de57926e7b17306ac9c5c46781a15d1b04414 Mon Sep 17 00:00:00 2001
From: Yedidya Feldblum <yfeldblum@fb.com>
Date: Mon, 15 Feb 2021 17:17:58 -0800
Subject: [PATCH] Let some utility functions be invocable objects

Summary:
[Folly] Let some utility functions be invocable objects: `to_signed`, `to_unsigned`, `to_narrow`, `to_underlying`.

Additionally, force the template type param to be deduced.

Reviewed By: markisaa

Differential Revision: D21959729

fbshipit-source-id: eedd9f2f84efb62c99ed0d5f65123a7fe000cf0a
---
 folly/Utility.h | 63 ++++++++++++++++++++++++++++++-------------------
 1 file changed, 39 insertions(+), 24 deletions(-)

diff --git a/folly/Utility.h b/folly/Utility.h
index 4d72f6c77..527e17d88 100644
--- a/folly/Utility.h
+++ b/folly/Utility.h
@@ -291,21 +291,29 @@ class MoveOnly {
 
 using MoveOnly = moveonly_::MoveOnly;
 
-template <typename T>
-constexpr auto to_signed(T const& t) -> typename std::make_signed<T>::type {
-  using S = typename std::make_signed<T>::type;
-  // note: static_cast<S>(t) would be more straightforward, but it would also be
-  // implementation-defined behavior and that is typically to be avoided; the
-  // following code optimized into the same thing, though
-  constexpr auto m = static_cast<T>(std::numeric_limits<S>::max());
-  return m < t ? -static_cast<S>(~t) + S{-1} : static_cast<S>(t);
-}
-
-template <typename T>
-constexpr auto to_unsigned(T const& t) -> typename std::make_unsigned<T>::type {
-  using U = typename std::make_unsigned<T>::type;
-  return static_cast<U>(t);
-}
+struct to_signed_fn {
+  template <typename..., typename T>
+  constexpr auto operator()(T const& t) const noexcept ->
+      typename std::make_signed<T>::type {
+    using S = typename std::make_signed<T>::type;
+    // note: static_cast<S>(t) would be more straightforward, but it would also
+    // be implementation-defined behavior and that is typically to be avoided;
+    // the following code optimized into the same thing, though
+    constexpr auto m = static_cast<T>(std::numeric_limits<S>::max());
+    return m < t ? -static_cast<S>(~t) + S{-1} : static_cast<S>(t);
+  }
+};
+FOLLY_INLINE_VARIABLE constexpr to_signed_fn to_signed;
+
+struct to_unsigned_fn {
+  template <typename..., typename T>
+  constexpr auto operator()(T const& t) const noexcept ->
+      typename std::make_unsigned<T>::type {
+    using U = typename std::make_unsigned<T>::type;
+    return static_cast<U>(t);
+  }
+};
+FOLLY_INLINE_VARIABLE constexpr to_unsigned_fn to_unsigned;
 
 template <typename Src>
 class to_narrow_convertible {
@@ -356,16 +364,23 @@ class to_narrow_convertible {
 //  to take advantage of the undefined-behavior sanitizer's inspection of all
 //  implicit conversions - it checks for truncation, with suppressions in place
 //  for warnings which guard against narrowing implicit conversions.
-template <typename Src>
-constexpr auto to_narrow(Src const& src) -> to_narrow_convertible<Src> {
-  return to_narrow_convertible<Src>{src};
-}
+struct to_narrow_fn {
+  template <typename..., typename Src>
+  constexpr auto operator()(Src const& src) const noexcept
+      -> to_narrow_convertible<Src> {
+    return to_narrow_convertible<Src>{src};
+  }
+};
+FOLLY_INLINE_VARIABLE constexpr to_narrow_fn to_narrow;
 
-template <class E>
-constexpr std::underlying_type_t<E> to_underlying(E e) noexcept {
-  static_assert(std::is_enum<E>::value, "not an enum type");
-  return static_cast<std::underlying_type_t<E>>(e);
-}
+struct to_underlying_fn {
+  template <typename..., class E>
+  constexpr std::underlying_type_t<E> operator()(E e) const noexcept {
+    static_assert(std::is_enum<E>::value, "not an enum type");
+    return static_cast<std::underlying_type_t<E>>(e);
+  }
+};
+FOLLY_INLINE_VARIABLE constexpr to_underlying_fn to_underlying;
 
 /*
  * FOLLY_DECLVAL(T)
-- 
2.26.2