Add make_array to folly
authorMark Isaacson <markisaa@fb.com>
Tue, 19 Apr 2016 07:46:56 +0000 (00:46 -0700)
committerFacebook Github Bot 1 <facebook-github-bot-1-bot@fb.com>
Tue, 19 Apr 2016 07:50:21 +0000 (00:50 -0700)
Summary: This function is being proposed in WG21, the C++ standards body for inclusion in the STL via the Library Fundamentals v2 TS. Using the normal constructor for a std::array with an initializer list introduces a source of coupling between the # of elements you put in the initializer list and the size of the array you specify as a template argument. Worse still, if you put less things in the initializer list than the template argument specifies, it doesn't warn you that you've probably made a pretty devious and subtle error. With this function your array size will always be the same as the # of things you actually put in it. What's more, in some cases this can deduce the type of the elements as well.

Reviewed By: yfeldblum

Differential Revision: D3164432

fb-gh-sync-id: beceaae2ee01cd5f93dec86cf36efdb78a28b4a3
fbshipit-source-id: beceaae2ee01cd5f93dec86cf36efdb78a28b4a3

folly/Array.h [new file with mode: 0644]
folly/Makefile.am
folly/Traits.h
folly/test/ArrayTest.cpp [new file with mode: 0644]
folly/test/Makefile.am
folly/test/TraitsTest.cpp

diff --git a/folly/Array.h b/folly/Array.h
new file mode 100644 (file)
index 0000000..77faafe
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * 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 <folly/Traits.h>
+#include <array>
+#include <type_traits>
+#include <utility>
+
+namespace folly {
+
+namespace array_detail {
+template <typename>
+struct is_ref_wrapper : std::false_type {};
+template <typename T>
+struct is_ref_wrapper<std::reference_wrapper<T>> : std::true_type {};
+
+template <typename T>
+using not_ref_wrapper =
+    folly::Negation<is_ref_wrapper<typename std::decay<T>::type>>;
+
+template <typename D, typename...>
+struct return_type_helper {
+  using type = D;
+};
+template <typename... TList>
+struct return_type_helper<void, TList...> {
+  static_assert(
+      folly::Conjunction<not_ref_wrapper<TList>...>::value,
+      "TList cannot contain reference_wrappers when D is void");
+  using type = typename std::common_type<TList...>::type;
+};
+
+template <typename D, typename... TList>
+using return_type = std::
+    array<typename return_type_helper<D, TList...>::type, sizeof...(TList)>;
+} // !array_detail
+
+template <typename D = void, typename... TList>
+constexpr array_detail::return_type<D, TList...> make_array(TList&&... t) {
+  using value_type =
+      typename array_detail::return_type_helper<D, TList...>::type;
+  return {static_cast<value_type>(std::forward<TList>(t))...};
+}
+
+} // !folly
index d9e8e77..5eb2768 100644 (file)
@@ -25,6 +25,7 @@ nobase_follyinclude_HEADERS = \
        ApplyTuple.h \
        Arena.h \
        Arena-inl.h \
+       Array.h \
        Assume.h \
        AtomicBitSet.h \
        AtomicHashArray.h \
index f34dc74..598a63a 100644 (file)
@@ -132,6 +132,25 @@ template <class T> struct IsZeroInitializable
       traits_detail::has_true_IsZeroInitializable<T>::value
     > {};
 
+template <typename...>
+struct Conjunction : std::true_type {};
+template <typename T>
+struct Conjunction<T> : T {};
+template <typename T, typename... TList>
+struct Conjunction<T, TList...>
+    : std::conditional<T::value, Conjunction<TList...>, T>::type {};
+
+template <typename...>
+struct Disjunction : std::false_type {};
+template <typename T>
+struct Disjunction<T> : T {};
+template <typename T, typename... TList>
+struct Disjunction<T, TList...>
+    : std::conditional<T::value, T, Disjunction<TList...>>::type {};
+
+template <typename T>
+struct Negation : std::integral_constant<bool, !T::value> {};
+
 } // namespace folly
 
 /**
diff --git a/folly/test/ArrayTest.cpp b/folly/test/ArrayTest.cpp
new file mode 100644 (file)
index 0000000..2bfc73a
--- /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.
+ */
+#include <folly/Array.h>
+#include <gtest/gtest.h>
+#include <string>
+
+using namespace std;
+using folly::make_array;
+
+TEST(make_array, base_case) {
+  auto arr = make_array<int>();
+  static_assert(
+      is_same<typename decltype(arr)::value_type, int>::value,
+      "Wrong array type");
+  EXPECT_EQ(arr.size(), 0);
+}
+
+TEST(make_array, deduce_size_primitive) {
+  auto arr = make_array<int>(1, 2, 3, 4, 5);
+  static_assert(
+      is_same<typename decltype(arr)::value_type, int>::value,
+      "Wrong array type");
+  EXPECT_EQ(arr.size(), 5);
+}
+
+TEST(make_array, deduce_size_class) {
+  auto arr = make_array<string>(string{"foo"}, string{"bar"});
+  static_assert(
+      is_same<typename decltype(arr)::value_type, std::string>::value,
+      "Wrong array type");
+  EXPECT_EQ(arr.size(), 2);
+  EXPECT_EQ(arr[1], "bar");
+}
+
+TEST(make_array, deduce_everything) {
+  auto arr = make_array(string{"foo"}, string{"bar"});
+  static_assert(
+      is_same<typename decltype(arr)::value_type, std::string>::value,
+      "Wrong array type");
+  EXPECT_EQ(arr.size(), 2);
+  EXPECT_EQ(arr[1], "bar");
+}
+
+TEST(make_array, fixed_common_type) {
+  auto arr = make_array<double>(1.0, 2.5f, 3, 4, 5);
+  static_assert(
+      is_same<typename decltype(arr)::value_type, double>::value,
+      "Wrong array type");
+  EXPECT_EQ(arr.size(), 5);
+}
+
+TEST(make_array, deduced_common_type) {
+  auto arr = make_array(1.0, 2.5f, 3, 4, 5);
+  static_assert(
+      is_same<typename decltype(arr)::value_type, double>::value,
+      "Wrong array type");
+  EXPECT_EQ(arr.size(), 5);
+}
index 71fe7a8..229efaf 100644 (file)
@@ -37,6 +37,10 @@ spin_lock_test_SOURCES = SpinLockTest.cpp
 spin_lock_test_LDADD = libfollytestmain.la
 TESTS += spin_lock_test
 
+array_test_SOURCES = ArrayTest.cpp
+array_test_LDADD = libfollytestmain.la
+TESTS += array_test
+
 if RUN_ARCH_SPECIFIC_TESTS
 small_locks_test_SOURCES = SmallLocksTest.cpp
 small_locks_test_LDADD = libfollytestmain.la
index 82cded6..453c29e 100644 (file)
@@ -18,6 +18,7 @@
 
 #include <cstring>
 #include <string>
+#include <type_traits>
 #include <utility>
 
 #include <folly/ScopeGuard.h>
@@ -84,6 +85,25 @@ TEST(Traits, bitAndInit) {
   EXPECT_FALSE(IsZeroInitializable<vector<int>>::value);
 }
 
+TEST(Trait, logicOperators) {
+  static_assert(Conjunction<true_type>::value, "");
+  static_assert(!Conjunction<false_type>::value, "");
+  static_assert(is_same<Conjunction<true_type>::type, true_type>::value, "");
+  static_assert(is_same<Conjunction<false_type>::type, false_type>::value, "");
+  static_assert(Conjunction<true_type, true_type>::value, "");
+  static_assert(!Conjunction<true_type, false_type>::value, "");
+
+  static_assert(Disjunction<true_type>::value, "");
+  static_assert(!Disjunction<false_type>::value, "");
+  static_assert(is_same<Disjunction<true_type>::type, true_type>::value, "");
+  static_assert(is_same<Disjunction<false_type>::type, false_type>::value, "");
+  static_assert(Disjunction<true_type, true_type>::value, "");
+  static_assert(Disjunction<true_type, false_type>::value, "");
+
+  static_assert(!Negation<true_type>::value, "");
+  static_assert(Negation<false_type>::value, "");
+}
+
 TEST(Traits, is_negative) {
   EXPECT_TRUE(folly::is_negative(-1));
   EXPECT_FALSE(folly::is_negative(0));