From 945a193fcd15edaf83956bfad2f6a9a149fda6bb Mon Sep 17 00:00:00 2001
From: Mark Santaniello <marksan@meta.com>
Date: Sat, 25 May 2024 01:30:00 -0700
Subject: [PATCH] Introduce reserve_if_available

Summary:
This is useful when writing generic code that handles containers.  Some examples:
- std::unordered_map provides reserve(), but std::map does not
- std::vector provides reserve(), but std::deque and std::list do not

Reviewed By: yfeldblum

Differential Revision: D57718859

fbshipit-source-id: 9cb146b2950746e416c938b0cc6484715f58bb69
---
 folly/container/Reserve.h            | 29 ++++++++++++++++++++++++++++
 folly/container/test/ReserveTest.cpp | 14 ++++++++++++++
 2 files changed, 43 insertions(+)

diff --git a/folly/container/Reserve.h b/folly/container/Reserve.h
index 5681086dc14..77a235ecbb9 100644
--- a/folly/container/Reserve.h
+++ b/folly/container/Reserve.h
@@ -36,6 +36,13 @@ using detect_bucket_count = decltype(FOLLY_DECLVAL(C).bucket_count());
 template <typename C>
 using detect_max_load_factor = decltype(FOLLY_DECLVAL(C).max_load_factor());
 
+template <typename C, typename... A>
+using detect_reserve = decltype(FOLLY_DECLVAL(C).reserve(FOLLY_DECLVAL(A)...));
+
+template <typename C>
+using container_detect_reserve =
+    detect_reserve<C, typename remove_cvref_t<C>::size_type>;
+
 } // namespace detail
 
 /**
@@ -78,4 +85,26 @@ struct grow_capacity_by_fn {
 
 inline constexpr grow_capacity_by_fn grow_capacity_by{};
 
+/**
+ * Useful when writing generic code that handles containers.
+ *
+ * Examples:
+ *  - std::unordered_map provides reserve(), but std::map does not
+ *  - std::vector provides reserve(), but std::deque and std::list do not
+ */
+struct reserve_if_available_fn {
+  template <typename C>
+  auto operator()(C& c, typename C::size_type const n) const
+      noexcept(!folly::is_detected_v<detail::container_detect_reserve, C&>) {
+    constexpr auto match =
+        folly::is_detected_v<detail::container_detect_reserve, C&>;
+    if constexpr (match) {
+      c.reserve(n);
+    }
+    return std::bool_constant<match>{};
+  }
+};
+
+inline constexpr reserve_if_available_fn reserve_if_available{};
+
 } // namespace folly
diff --git a/folly/container/test/ReserveTest.cpp b/folly/container/test/ReserveTest.cpp
index 8273a8bca7c..a474e7caecd 100644
--- a/folly/container/test/ReserveTest.cpp
+++ b/folly/container/test/ReserveTest.cpp
@@ -16,6 +16,7 @@
 
 #include <folly/container/Reserve.h>
 
+#include <list>
 #include <memory>
 #include <unordered_map>
 #include <vector>
@@ -157,3 +158,16 @@ TEST(ReserveUtil, F14NodeMapGrowBy) {
 TEST(ReserveUtil, UnorderedMapGrowBy) {
   testMapGrowBy<unordered_map>();
 }
+
+TEST(ReserveUtil, ReserveIfAvailableVector) {
+  std::vector<int> v;
+  auto r = folly::reserve_if_available(v, 42);
+  static_assert(std::is_same_v<decltype(r), std::true_type>);
+  EXPECT_GE(v.capacity(), 42);
+}
+
+TEST(ReserveUtil, ReserveIfAvailableList) {
+  std::list<int> l;
+  auto r = folly::reserve_if_available(l, 42);
+  static_assert(std::is_same_v<decltype(r), std::false_type>);
+}