handle: Support functors alongside function pointers

This allows for more flexibility in the type signatures of the
provided Drop and Ref functionality. Backwards compatibility is
maintained.

Change-Id: I9b8d66fc72d27d5feb4e01f2266dcd317733c150
Signed-off-by: William A. Kennington III <wak@google.com>
diff --git a/src/stdplus/handle/copyable.hpp b/src/stdplus/handle/copyable.hpp
index ef93263..a7419d5 100644
--- a/src/stdplus/handle/copyable.hpp
+++ b/src/stdplus/handle/copyable.hpp
@@ -13,25 +13,25 @@
 template <typename T, typename... As>
 struct Copyable
 {
-    template <void (*drop)(T&&, As&...), T (*ref)(const T&, As&...)>
-    class Handle : public Managed<T, As...>::template Handle<drop>
+    template <typename Drop, typename Ref>
+    class HandleF : public Managed<T, As...>::template HandleF<Drop>
     {
       public:
-        using MHandle = typename Managed<T, As...>::template Handle<drop>;
+        using MHandleF = typename Managed<T, As...>::template HandleF<Drop>;
 
         /** @brief Creates a handle referencing the object
          *
          *  @param[in] maybeV - Optional object being managed
          */
         template <typename... Vs>
-        constexpr explicit Handle(const std::optional<T>& maybeV, Vs&&... vs) :
-            MHandle(std::nullopt, std::forward<Vs>(vs)...)
+        constexpr explicit HandleF(const std::optional<T>& maybeV, Vs&&... vs) :
+            MHandleF(std::nullopt, std::forward<Vs>(vs)...)
         {
             reset(maybeV);
         }
         template <typename... Vs>
-        constexpr explicit Handle(const T& maybeV, Vs&&... vs) :
-            MHandle(std::nullopt, std::forward<Vs>(vs)...)
+        constexpr explicit HandleF(const T& maybeV, Vs&&... vs) :
+            MHandleF(std::nullopt, std::forward<Vs>(vs)...)
         {
             reset(maybeV);
         }
@@ -41,28 +41,29 @@
          *  @param[in] maybeV - Maybe the object being managed
          */
         template <typename... Vs>
-        constexpr explicit Handle(std::optional<T>&& maybeV, Vs&&... vs) :
-            MHandle(std::move(maybeV), std::forward<Vs>(vs)...)
+        constexpr explicit HandleF(std::optional<T>&& maybeV, Vs&&... vs) :
+            MHandleF(std::move(maybeV), std::forward<Vs>(vs)...)
         {
         }
         template <typename... Vs>
-        constexpr explicit Handle(T&& maybeV, Vs&&... vs) :
-            MHandle(std::move(maybeV), std::forward<Vs>(vs)...)
+        constexpr explicit HandleF(T&& maybeV, Vs&&... vs) :
+            MHandleF(std::move(maybeV), std::forward<Vs>(vs)...)
         {
         }
 
-        constexpr Handle(const Handle& other) : MHandle(std::nullopt, other.as)
+        constexpr HandleF(const HandleF& other) :
+            MHandleF(std::nullopt, other.as)
         {
             reset(other.maybe_value());
         }
 
-        constexpr Handle(Handle&& other) noexcept(
-            std::is_nothrow_move_constructible_v<MHandle>) :
-            MHandle(std::move(other))
+        constexpr HandleF(HandleF&& other) noexcept(
+            std::is_nothrow_move_constructible_v<MHandleF>) :
+            MHandleF(std::move(other))
         {
         }
 
-        constexpr Handle& operator=(const Handle& other)
+        constexpr HandleF& operator=(const HandleF& other)
         {
             if (this != &other)
             {
@@ -73,13 +74,13 @@
             return *this;
         }
 
-        constexpr Handle& operator=(Handle&& other)
+        constexpr HandleF& operator=(HandleF&& other)
         {
-            MHandle::operator=(std::move(other));
+            MHandleF::operator=(std::move(other));
             return *this;
         }
 
-        using MHandle::reset;
+        using MHandleF::reset;
 
         /** @brief Resets the managed value to a new value
          *         Takes a new reference on the value
@@ -106,9 +107,22 @@
         template <size_t... Indices>
         T doRef(const T& v, std::index_sequence<Indices...>)
         {
-            return ref(v, std::get<Indices>(this->as)...);
+            return Ref()(v, std::get<Indices>(this->as)...);
         }
     };
+
+    template <T (*ref)(const T&, As&...)>
+    struct Reffer
+    {
+        T operator()(const T& t, As&... as) noexcept(noexcept(ref))
+        {
+            return ref(t, as...);
+        }
+    };
+
+    template <void (*drop)(T&&, As&...), T (*ref)(const T&, As&...)>
+    using Handle = HandleF<typename Managed<T, As...>::template Dropper<drop>,
+                           Reffer<ref>>;
 };
 
 } // namespace stdplus
diff --git a/src/stdplus/handle/managed.hpp b/src/stdplus/handle/managed.hpp
index 94c0bfa..df4a142 100644
--- a/src/stdplus/handle/managed.hpp
+++ b/src/stdplus/handle/managed.hpp
@@ -15,8 +15,10 @@
  *           We could make a simple file descriptor wrapper that is
  *           automatically closed:
  *
- *           void closefd(int&& fd) { close(fd); }
- *           using Fd = Managed<int>::Handle<closefd>;
+ *           struct CloseFd {
+ *               void operator()(int&& fd) { close(fd); }
+ *           };
+ *           using Fd = Managed<int>::HandleF<closefd>;
  *
  *           void some_func()
  *           {
@@ -29,8 +31,9 @@
 template <typename T, typename... As>
 struct Managed
 {
-    template <void (*drop)(T&&, As&...)>
-    class Handle
+
+    template <typename Drop>
+    class HandleF
     {
       public:
         /** @brief Creates a handle owning the object
@@ -38,20 +41,20 @@
          *  @param[in] maybeV - Maybe the object being managed
          */
         template <typename... Vs>
-        constexpr explicit Handle(std::optional<T>&& maybeV, Vs&&... vs) :
+        constexpr explicit HandleF(std::optional<T>&& maybeV, Vs&&... vs) :
             as(std::forward<Vs>(vs)...), maybeT(std::move(maybeV))
         {
         }
         template <typename... Vs>
-        constexpr explicit Handle(T&& maybeV, Vs&&... vs) :
+        constexpr explicit HandleF(T&& maybeV, Vs&&... vs) :
             as(std::forward<Vs>(vs)...), maybeT(std::move(maybeV))
         {
         }
 
-        Handle(const Handle& other) = delete;
-        Handle& operator=(const Handle& other) = delete;
+        HandleF(const HandleF& other) = delete;
+        HandleF& operator=(const HandleF& other) = delete;
 
-        constexpr Handle(Handle&& other) noexcept(
+        constexpr HandleF(HandleF&& other) noexcept(
             std::is_nothrow_move_constructible_v<std::tuple<As...>>&&
                 std::is_nothrow_move_constructible_v<std::optional<T>>) :
             as(std::move(other.as)),
@@ -60,7 +63,7 @@
             other.maybeT = std::nullopt;
         }
 
-        constexpr Handle& operator=(Handle&& other)
+        constexpr HandleF& operator=(HandleF&& other)
         {
             if (this != &other)
             {
@@ -71,7 +74,7 @@
             return *this;
         }
 
-        virtual ~Handle()
+        virtual ~HandleF()
         {
             try
             {
@@ -214,10 +217,22 @@
         {
             if (maybeT)
             {
-                drop(std::move(*maybeT), std::get<Indices>(as)...);
+                Drop()(std::move(*maybeT), std::get<Indices>(as)...);
             }
         }
     };
+
+    template <void (*drop)(T&&, As&...)>
+    struct Dropper
+    {
+        void operator()(T&& t, As&... as) noexcept(noexcept(drop))
+        {
+            drop(std::move(t), as...);
+        }
+    };
+
+    template <void (*drop)(T&&, As&...)>
+    using Handle = HandleF<Dropper<drop>>;
 };
 
 } // namespace stdplus
diff --git a/test/handle/copyable.cpp b/test/handle/copyable.cpp
index 6ce210f..b55b216 100644
--- a/test/handle/copyable.cpp
+++ b/test/handle/copyable.cpp
@@ -15,34 +15,58 @@
 static std::vector<int> dropped;
 static int stored_drop = 0;
 
+struct SimpleRef
+{
+    int operator()(const int& i) noexcept
+    {
+        reffed.push_back(i);
+        return i + 1;
+    }
+};
+
 int ref(const int& i)
 {
-    reffed.push_back(i);
-    return i + 1;
+    return SimpleRef()(i);
 }
 
+struct SimpleDrop
+{
+    void operator()(int&& i) noexcept
+    {
+        dropped.push_back(std::move(i));
+    }
+};
+
 void drop(int&& i)
 {
-    dropped.push_back(std::move(i));
+    SimpleDrop()(std::move(i));
 }
 
-int ref(const int& i, std::string&, int& si)
+struct StoreRef
 {
-    reffed.push_back(i);
-    // Make sure we can update the stored data
-    stored_ref = si++;
-    return i + 1;
-}
+    int operator()(const int& i, std::string&, int& si)
+    {
+        reffed.push_back(i);
+        // Make sure we can update the stored data
+        stored_ref = si++;
+        return i + 1;
+    }
+};
 
-void drop(int&& i, std::string&, int& si)
+struct StoreDrop
 {
-    dropped.push_back(std::move(i));
-    // Make sure we can update the stored data
-    stored_drop = si++;
-}
+    void operator()(int&& i, std::string&, int& si)
+    {
+        dropped.push_back(std::move(i));
+        // Make sure we can update the stored data
+        stored_drop = si++;
+    }
+};
 
-using SimpleHandle = Copyable<int>::Handle<drop, ref>;
-using StoreHandle = Copyable<int, std::string, int>::Handle<drop, ref>;
+using SimpleHandleOld = Copyable<int>::Handle<drop, ref>;
+using SimpleHandle = Copyable<int>::HandleF<SimpleDrop, SimpleRef>;
+using StoreHandle =
+    Copyable<int, std::string, int>::HandleF<StoreDrop, StoreRef>;
 
 class CopyableHandleTest : public ::testing::Test
 {
@@ -62,7 +86,7 @@
 
 TEST_F(CopyableHandleTest, EmptyNoStorage)
 {
-    SimpleHandle h(std::nullopt);
+    SimpleHandleOld h(std::nullopt);
     EXPECT_FALSE(h);
     EXPECT_THROW(h.value(), std::bad_optional_access);
     h.reset();
diff --git a/test/handle/managed.cpp b/test/handle/managed.cpp
index 155ba59..fffffde 100644
--- a/test/handle/managed.cpp
+++ b/test/handle/managed.cpp
@@ -14,20 +14,32 @@
 static std::vector<int> dropped;
 static int stored = 0;
 
+struct SimpleDrop
+{
+    void operator()(int&& i) noexcept
+    {
+        dropped.push_back(std::move(i));
+    }
+};
+
 void drop(int&& i)
 {
-    dropped.push_back(std::move(i));
+    SimpleDrop()(std::move(i));
 }
 
-void drop(int&& i, std::string&, int& si)
+struct StoreDrop
 {
-    dropped.push_back(std::move(i));
-    // Make sure we can update the stored data
-    stored = si++;
-}
+    void operator()(int&& i, std::string&, int& si)
+    {
+        dropped.push_back(std::move(i));
+        // Make sure we can update the stored data
+        stored = si++;
+    }
+};
 
-using SimpleHandle = Managed<int>::Handle<drop>;
-using StoreHandle = Managed<int, std::string, int>::Handle<drop>;
+using SimpleHandleOld = Managed<int>::Handle<drop>;
+using SimpleHandle = Managed<int>::HandleF<SimpleDrop>;
+using StoreHandle = Managed<int, std::string, int>::HandleF<StoreDrop>;
 
 class ManagedHandleTest : public ::testing::Test
 {
@@ -45,7 +57,7 @@
 
 TEST_F(ManagedHandleTest, EmptyNoStorage)
 {
-    SimpleHandle h(std::nullopt);
+    SimpleHandleOld h(std::nullopt);
     EXPECT_FALSE(h);
     EXPECT_THROW(h.value(), std::bad_optional_access);
     EXPECT_THROW((void)h.release(), std::bad_optional_access);