Emplacement in folly::padded::Adaptor, if the adapted class allows.
authorYedidya Feldblum <yfeldblum@fb.com>
Tue, 11 Aug 2015 18:47:39 +0000 (11:47 -0700)
committerfacebook-github-bot-9 <folly-bot@fb.com>
Tue, 11 Aug 2015 19:22:06 +0000 (12:22 -0700)
Summary: [Folly] Emplacement in folly::padded::Adaptor, if the adapted class allows.

Reviewed By: @Gownta

Differential Revision: D2157162

folly/ContainerTraits.h [new file with mode: 0644]
folly/Makefile.am
folly/Padded.h
folly/test/ContainerTraitsTest.cpp [new file with mode: 0644]
folly/test/PaddedTest.cpp

diff --git a/folly/ContainerTraits.h b/folly/ContainerTraits.h
new file mode 100644 (file)
index 0000000..5f6e5d4
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2015 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.
+ */
+
+#ifndef FOLLY_BASE_CONTAINER_TRAITS_H_
+#define FOLLY_BASE_CONTAINER_TRAITS_H_
+
+#include <folly/Traits.h>
+
+namespace folly {
+
+FOLLY_CREATE_HAS_MEMBER_FN_TRAITS(container_emplace_back_traits, emplace_back);
+
+template <class Container, typename... Args>
+inline
+typename std::enable_if<
+    container_emplace_back_traits<Container, void(Args...)>::value>::type
+container_emplace_back_or_push_back(Container& container, Args&&... args) {
+  container.emplace_back(std::forward<Args>(args)...);
+}
+
+template <class Container, typename... Args>
+inline
+typename std::enable_if<
+    !container_emplace_back_traits<Container, void(Args...)>::value>::type
+container_emplace_back_or_push_back(Container& container, Args&&... args) {
+  using v = typename Container::value_type;
+  container.push_back(v(std::forward<Args>(args)...));
+}
+
+}
+
+#endif
index ce4319adf2cd41d292a971d717646c2353425af6..6fe63d0b22bafe0468877a6458a40827986ad9d9 100644 (file)
@@ -36,6 +36,7 @@ nobase_follyinclude_HEADERS = \
        Chrono.h \
        ConcurrentSkipList.h \
        ConcurrentSkipList-inl.h \
+       ContainerTraits.h \
        Conv.h \
        CpuId.h \
        CPortability.h \
index c5b53a7c94ad88c6aef460e29ffd6193dd18046d..9f8af135e2d6b7d1499e829e539657a1888a3ce0 100644 (file)
@@ -28,6 +28,7 @@
 #include <boost/iterator/iterator_adaptor.hpp>
 
 #include <folly/Portability.h>
+#include <folly/ContainerTraits.h>
 
 /**
  * Code that aids in storing data aligned on block (possibly cache-line)
@@ -350,7 +351,7 @@ class Adaptor {
 
   Adaptor(const Adaptor&) = default;
   Adaptor& operator=(const Adaptor&) = default;
-  Adaptor(Adaptor&& other)
+  Adaptor(Adaptor&& other) noexcept
     : c_(std::move(other.c_)),
       lastCount_(other.lastCount_) {
     other.lastCount_ = Node::kElementCount;
@@ -424,12 +425,13 @@ class Adaptor {
     return c_.back().data()[lastCount_ - 1];
   }
 
+  template <typename... Args>
+  void emplace_back(Args&&... args) {
+    new (allocate_back()) value_type(std::forward<Args>(args)...);
+  }
+
   void push_back(value_type x) {
-    if (lastCount_ == Node::kElementCount) {
-      c_.push_back(Node());
-      lastCount_ = 0;
-    }
-    c_.back().data()[lastCount_++] = std::move(x);
+    emplace_back(std::move(x));
   }
 
   void pop_back() {
@@ -490,6 +492,14 @@ class Adaptor {
   }
 
  private:
+  value_type* allocate_back() {
+    if (lastCount_ == Node::kElementCount) {
+      container_emplace_back_or_push_back(c_);
+      lastCount_ = 0;
+    }
+    return &c_.back().data()[lastCount_++];
+  }
+
   static Node fullNode(const value_type& value) {
     Node n;
     std::fill(n.data(), n.data() + kElementsPerNode, value);
diff --git a/folly/test/ContainerTraitsTest.cpp b/folly/test/ContainerTraitsTest.cpp
new file mode 100644 (file)
index 0000000..fccef81
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2015 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/ContainerTraits.h>
+
+#include <gtest/gtest.h>
+
+using namespace std;
+using namespace folly;
+
+struct Node {
+  size_t copies = 0;
+  Node() noexcept {};
+  Node(const Node& n) noexcept { copies = n.copies; ++copies; }
+  Node(Node&& n) noexcept { swap(copies, n.copies); ++copies; }
+};
+
+template <class T>
+class VectorWrapper {
+public:
+  using value_type = T;
+  vector<T>& underlying;
+  explicit VectorWrapper(vector<T>& v) : underlying(v) {}
+  void push_back(const T& t) { underlying.push_back(t); }
+};
+
+TEST(ContainerTraits, WithoutEmplaceBack) {
+  vector<Node> v;
+  VectorWrapper<Node> vw(v);
+  container_emplace_back_or_push_back(vw);
+  EXPECT_EQ(1, v.at(0).copies);
+}
+
+TEST(ContainerTraits, WithEmplaceBack) {
+  vector<Node> v;
+  container_emplace_back_or_push_back(v);
+  EXPECT_EQ(0, v.at(0).copies);
+}
index 406d88c0317046f7d3985d3efc329f7bf149fc06..04ca6da09794b97a7b9832d1127bb578b498b149 100644 (file)
@@ -239,3 +239,24 @@ TEST_F(IntAdaptorTest, ResizeConstructor) {
     EXPECT_EQ(42, a[i]);
   }
 }
+
+TEST_F(IntAdaptorTest, SimpleEmplaceBack) {
+  for (int i = 0; i < n_; ++i) {
+    EXPECT_EQ((i == 0), a_.empty());
+    EXPECT_EQ(i, a_.size());
+    a_.emplace_back(i);
+  }
+  EXPECT_EQ(n_, a_.size());
+
+  int k = 0;
+  for (auto it = a_.begin(); it != a_.end(); ++it, ++k) {
+    EXPECT_EQ(k, a_[k]);
+    EXPECT_EQ(k, *it);
+  }
+  EXPECT_EQ(n_, k);
+
+  auto p = a_.move();
+  EXPECT_TRUE(a_.empty());
+  EXPECT_EQ(16, p.second);
+  EXPECT_TRUE(v_ == p.first);
+}