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);