folly::copy
authorYedidya Feldblum <yfeldblum@fb.com>
Thu, 29 Dec 2016 02:03:39 +0000 (18:03 -0800)
committerFacebook Github Bot <facebook-github-bot@users.noreply.github.com>
Thu, 29 Dec 2016 02:17:54 +0000 (18:17 -0800)
Summary:
[Folly] `folly::copy`.

Usable when you have a function with two overloads:

    class MyData;
    void something(MyData&&);
    void something(const MyData&);

Where the purpose is to make copies and moves explicit without having to spell out the full type names - in this case, for copies, to invoke copy constructors.

When the caller wants to pass a copy of an lvalue, the caller may:

    void foo() {
      MyData data;
      something(folly::copy(data)); // explicit copy
      something(std::move(data)); // explicit move
      something(data); // const& - neither move nor copy
    }

Reviewed By: markisaa, ericniebler

Differential Revision: D3462023

fbshipit-source-id: 6c777be288f2a7012c1b4b46dc988890b8662595

folly/Makefile.am
folly/Utility.h [new file with mode: 0644]
folly/test/Makefile.am
folly/test/UtilityTest.cpp [new file with mode: 0644]

index 085418cea2631833da012541b46ecf90cf82f7b7..6c6d4baf2540134e85b00f348961ff616359b656 100644 (file)
@@ -376,6 +376,7 @@ nobase_follyinclude_HEADERS = \
        Unit.h \
        Uri.h \
        Uri-inl.h \
+       Utility.h \
        Varint.h \
        VersionCheck.h
 
diff --git a/folly/Utility.h b/folly/Utility.h
new file mode 100644 (file)
index 0000000..ce5b153
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2016 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.
+ */
+
+#pragma once
+
+#include <type_traits>
+#include <utility>
+
+namespace folly {
+
+/**
+ *  copy
+ *
+ *  Usable when you have a function with two overloads:
+ *
+ *      class MyData;
+ *      void something(MyData&&);
+ *      void something(const MyData&);
+ *
+ *  Where the purpose is to make copies and moves explicit without having to
+ *  spell out the full type names - in this case, for copies, to invoke copy
+ *  constructors.
+ *
+ *  When the caller wants to pass a copy of an lvalue, the caller may:
+ *
+ *      void foo() {
+ *        MyData data;
+ *        something(folly::copy(data)); // explicit copy
+ *        something(std::move(data)); // explicit move
+ *        something(data); // const& - neither move nor copy
+ *      }
+ *
+ *  Note: If passed an rvalue, invokes the move-ctor, not the copy-ctor. This
+ *  can be used to to force a move, where just using std::move would not:
+ *
+ *      std::copy(std::move(data)); // force-move, not just a cast to &&
+ *
+ *  Note: The following text appears in the standard:
+ *
+ *  > In several places in this Clause the operation //DECAY_COPY(x)// is used.
+ *  > All such uses mean call the function `decay_copy(x)` and use the result,
+ *  > where `decay_copy` is defined as follows:
+ *  >
+ *  >   template <class T> decay_t<T> decay_copy(T&& v)
+ *  >     { return std::forward<T>(v); }
+ *  >
+ *  > http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4296.pdf
+ *  >   30.2.6 `decay_copy` [thread.decaycopy].
+ *
+ *  We mimic it, with a `noexcept` specifier for good measure.
+ */
+
+template <typename T>
+constexpr typename std::decay<T>::type copy(T&& value) noexcept(
+    noexcept(typename std::decay<T>::type(std::forward<T>(value)))) {
+  return std::forward<T>(value);
+}
+}
index f2785b38704c0760e4cc8995c72c04dd0691e90e..77a3ae8414c65a0e14133d9e9e7e96a9cf276621 100644 (file)
@@ -319,4 +319,8 @@ singleton_thread_local_test_SOURCES = SingletonThreadLocalTest.cpp
 singleton_thread_local_test_LDADD = libfollytestmain.la
 TESTS += singleton_thread_local_test
 
+utility_test_SOURCES = UtilityTest.cpp
+utility_test_LDADD = libfollytestmain.la
+TESTS += utility_test
+
 check_PROGRAMS += $(TESTS)
diff --git a/folly/test/UtilityTest.cpp b/folly/test/UtilityTest.cpp
new file mode 100644 (file)
index 0000000..9bd58da
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2016 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/Utility.h>
+
+#include <gtest/gtest.h>
+
+namespace {
+
+class UtilityTest : public testing::Test {};
+}
+
+TEST_F(UtilityTest, copy) {
+  struct MyData {};
+  struct Worker {
+    size_t rrefs = 0, crefs = 0;
+    void something(MyData&&) {
+      ++rrefs;
+    }
+    void something(const MyData&) {
+      ++crefs;
+    }
+  };
+
+  MyData data;
+  Worker worker;
+  worker.something(folly::copy(data));
+  worker.something(std::move(data));
+  worker.something(data);
+  EXPECT_EQ(2, worker.rrefs);
+  EXPECT_EQ(1, worker.crefs);
+}
+
+TEST_F(UtilityTest, copy_noexcept_spec) {
+  struct MyNoexceptCopyable {};
+  MyNoexceptCopyable noe;
+  EXPECT_TRUE(noexcept(folly::copy(noe)));
+  EXPECT_TRUE(noexcept(folly::copy(std::move(noe))));
+
+  struct MyThrowingCopyable {
+    MyThrowingCopyable() {}
+    MyThrowingCopyable(const MyThrowingCopyable&) noexcept(false) {}
+    MyThrowingCopyable(MyThrowingCopyable&&) = default;
+  };
+  MyThrowingCopyable thr;
+  EXPECT_FALSE(noexcept(folly::copy(thr)));
+  EXPECT_TRUE(noexcept(folly::copy(std::move(thr)))); // note: does not copy
+}