[libc++] Add utilites for instantiating functions with multiple types
authorNikolas Klauser <nikolasklauser@berlin.de>
Sat, 5 Nov 2022 01:57:52 +0000 (02:57 +0100)
committerNikolas Klauser <nikolasklauser@berlin.de>
Mon, 21 Nov 2022 19:35:06 +0000 (20:35 +0100)
We currently call a lot of functions with the same list of types. To avoid forgetting any of them, this patch adds type_lists and utilities for it. Specifically, it adds
- `type_list` - This is just a list of types
- `concatenate` - This allows concatenating type_lists
- `for_each` - Iterate over a type_list

Reviewed By: ldionne, #libc

Spies: jloser, EricWF, libcxx-commits

Differential Revision: https://reviews.llvm.org/D137476

libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy.pass.cpp
libcxx/test/std/language.support/support.limits/limits/is_specialized.pass.cpp
libcxx/test/std/strings/basic.string/string.access/at.pass.cpp
libcxx/test/support/test.support/type_algorithms.pass.cpp [new file with mode: 0644]
libcxx/test/support/test_iterators.h
libcxx/test/support/type_algorithms.h [new file with mode: 0644]

index 5ca0ce9..c241056 100644 (file)
@@ -24,6 +24,7 @@
 
 #include "almost_satisfies_types.h"
 #include "test_iterators.h"
+#include "type_algorithms.h"
 
 template <class In, class Out = In, class Sent = sentinel_wrapper<In>>
 concept HasCopyIt = requires(In in, Sent sent, Out out) { std::ranges::copy(in, sent, out); };
@@ -94,48 +95,28 @@ constexpr void test_iterators() {
   }
 }
 
-template <class In, class Out>
-constexpr void test_sentinels() {
-  test_iterators<In, Out>();
-  test_iterators<In, Out, sized_sentinel<In>>();
-  test_iterators<In, Out, sentinel_wrapper<In>>();
-}
-
-template <class Out>
-constexpr void test_in_iterators() {
-  test_iterators<cpp20_input_iterator<int*>, Out, sentinel_wrapper<cpp20_input_iterator<int*>>>();
-  test_sentinels<forward_iterator<int*>, Out>();
-  test_sentinels<bidirectional_iterator<int*>, Out>();
-  test_sentinels<random_access_iterator<int*>, Out>();
-  test_sentinels<contiguous_iterator<int*>, Out>();
-}
-
-template <class Out>
-constexpr void test_proxy_in_iterators() {
-  test_iterators<ProxyIterator<cpp20_input_iterator<int*>>, Out, sentinel_wrapper<ProxyIterator<cpp20_input_iterator<int*>>>>();
-  test_iterators<ProxyIterator<forward_iterator<int*>>, Out>();
-  test_iterators<ProxyIterator<bidirectional_iterator<int*>>, Out>();
-  test_iterators<ProxyIterator<random_access_iterator<int*>>, Out>();
-  test_iterators<ProxyIterator<contiguous_iterator<int*>>, Out>();
-}
-
 constexpr bool test() {
-  test_in_iterators<cpp20_input_iterator<int*>>();
-  test_in_iterators<forward_iterator<int*>>();
-  test_in_iterators<bidirectional_iterator<int*>>();
-  test_in_iterators<random_access_iterator<int*>>();
-  test_in_iterators<contiguous_iterator<int*>>();
-
-  test_proxy_in_iterators<ProxyIterator<cpp20_input_iterator<int*>>>();
-  test_proxy_in_iterators<ProxyIterator<forward_iterator<int*>>>();
-  test_proxy_in_iterators<ProxyIterator<bidirectional_iterator<int*>>>();
-  test_proxy_in_iterators<ProxyIterator<random_access_iterator<int*>>>();
-  test_proxy_in_iterators<ProxyIterator<contiguous_iterator<int*>>>();
+  meta::for_each(meta::forward_iterator_list<int*>{}, []<class Out>() {
+    test_iterators<cpp20_input_iterator<int*>, Out, sentinel_wrapper<cpp20_input_iterator<int*>>>();
+    test_iterators<ProxyIterator<cpp20_input_iterator<int*>>,
+                   ProxyIterator<Out>,
+                   sentinel_wrapper<ProxyIterator<cpp20_input_iterator<int*>>>>();
+
+    meta::for_each(meta::forward_iterator_list<int*>{}, []<class In>() {
+      test_iterators<In, Out>();
+      test_iterators<In, Out, sized_sentinel<In>>();
+      test_iterators<In, Out, sentinel_wrapper<In>>();
+
+      test_iterators<ProxyIterator<In>, ProxyIterator<Out>>();
+      test_iterators<ProxyIterator<In>, ProxyIterator<Out>, sized_sentinel<ProxyIterator<In>>>();
+      test_iterators<ProxyIterator<In>, ProxyIterator<Out>, sentinel_wrapper<ProxyIterator<In>>>();
+    });
+  });
 
   { // check that ranges::dangling is returned
     std::array<int, 4> out;
     std::same_as<std::ranges::in_out_result<std::ranges::dangling, int*>> auto ret =
-      std::ranges::copy(std::array {1, 2, 3, 4}, out.data());
+        std::ranges::copy(std::array{1, 2, 3, 4}, out.data());
     assert(ret.out == out.data() + 4);
     assert((out == std::array{1, 2, 3, 4}));
   }
index 3f3ecbd..0c39f9f 100644 (file)
 #include <limits>
 #include <complex>
 
-#include "test_macros.h"
+#include "type_algorithms.h"
 
-template <class T>
-void test()
-{
+struct Test {
+  template <class T>
+  void operator()() {
     static_assert(std::numeric_limits<T>::is_specialized,
                  "std::numeric_limits<T>::is_specialized");
     static_assert(std::numeric_limits<const T>::is_specialized,
@@ -39,37 +39,15 @@ void test()
                  "std::numeric_limits<volatile T>::is_specialized");
     static_assert(std::numeric_limits<const volatile T>::is_specialized,
                  "std::numeric_limits<const volatile T>::is_specialized");
-}
+  }
+};
 
 int main(int, char**)
 {
-    test<bool>();
-    test<char>();
-    test<wchar_t>();
-#if TEST_STD_VER > 17 && defined(__cpp_char8_t)
-    test<char8_t>();
-#endif
-    test<char16_t>();
-    test<char32_t>();
-    test<signed char>();
-    test<unsigned char>();
-    test<signed short>();
-    test<unsigned short>();
-    test<signed int>();
-    test<unsigned int>();
-    test<signed long>();
-    test<unsigned long>();
-    test<signed long long>();
-    test<unsigned long long>();
-#ifndef TEST_HAS_NO_INT128
-    test<__int128_t>();
-    test<__uint128_t>();
-#endif
-    test<float>();
-    test<double>();
-    test<long double>();
-    static_assert(!std::numeric_limits<std::complex<double> >::is_specialized,
-                 "!std::numeric_limits<std::complex<double> >::is_specialized");
+  meta::for_each(meta::arithmetic_types(), Test());
+
+  static_assert(!std::numeric_limits<std::complex<double> >::is_specialized,
+                "!std::numeric_limits<std::complex<double> >::is_specialized");
 
   return 0;
 }
index 3b991c5..3e17d07 100644 (file)
@@ -17,7 +17,9 @@
 
 #include "min_allocator.h"
 
+#include "make_string.h"
 #include "test_macros.h"
+#include "type_algorithms.h"
 
 template <class S>
 TEST_CONSTEXPR_CXX20 void
@@ -57,17 +59,24 @@ test(S s, typename S::size_type pos)
 template <class S>
 TEST_CONSTEXPR_CXX20 void test_string() {
   test(S(), 0);
-  test(S("123"), 0);
-  test(S("123"), 1);
-  test(S("123"), 2);
-  test(S("123"), 3);
+  test(S(MAKE_CSTRING(typename S::value_type, "123")), 0);
+  test(S(MAKE_CSTRING(typename S::value_type, "123")), 1);
+  test(S(MAKE_CSTRING(typename S::value_type, "123")), 2);
+  test(S(MAKE_CSTRING(typename S::value_type, "123")), 3);
 }
 
-TEST_CONSTEXPR_CXX20 bool test() {
-  test_string<std::string>();
+struct TestCaller {
+  template <class T>
+  TEST_CONSTEXPR_CXX20 void operator()() {
+        test_string<std::basic_string<T> >();
 #if TEST_STD_VER >= 11
-  test_string<std::basic_string<char, std::char_traits<char>, min_allocator<char>>>();
+        test_string<std::basic_string<T, std::char_traits<T>, min_allocator<T> > >();
 #endif
+  }
+};
+
+TEST_CONSTEXPR_CXX20 bool test() {
+  meta::for_each(meta::character_types(), TestCaller());
 
   return true;
 }
diff --git a/libcxx/test/support/test.support/type_algorithms.pass.cpp b/libcxx/test/support/test.support/type_algorithms.pass.cpp
new file mode 100644 (file)
index 0000000..f94487d
--- /dev/null
@@ -0,0 +1,76 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <algorithm>
+#include <cassert>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+// concatenate
+static_assert(std::is_same<meta::concatenate_t<meta::type_list<> >, meta::type_list<> >::value, "");
+static_assert(std::is_same<meta::concatenate_t<meta::type_list<int> >, meta::type_list<int> >::value, "");
+static_assert(
+    std::is_same<meta::concatenate_t<meta::type_list<int>, meta::type_list<long> >, meta::type_list<int, long> >::value,
+    "");
+static_assert(
+    std::is_same<meta::concatenate_t<meta::type_list<int>, meta::type_list<long>, meta::type_list<long long> >,
+                 meta::type_list<int, long, long long> >::value,
+    "");
+
+// apply_all
+template <int N>
+class NumT {};
+
+struct ApplyAllTest {
+  bool* is_called_array_;
+
+  TEST_CONSTEXPR ApplyAllTest(bool* is_called_array) : is_called_array_(is_called_array) {}
+
+  template <int N>
+  TEST_CONSTEXPR_CXX20 void check_num(NumT<N>) {
+    assert(!is_called_array_[N]);
+    is_called_array_[N] = true;
+  }
+
+  template <int N, int M>
+  TEST_CONSTEXPR_CXX20 void check_num(NumT<N>, NumT<M>) {
+    assert(!is_called_array_[N + M]);
+    is_called_array_[N + M] = true;
+  }
+
+  template <class... Types>
+  TEST_CONSTEXPR_CXX20 void operator()() {
+    check_num(Types()...);
+  }
+};
+
+struct Identity {
+  TEST_CONSTEXPR bool operator()(bool b) const { return b; }
+};
+
+TEST_CONSTEXPR_CXX20 void test_for_each() {
+  bool is_called_array[3] = {};
+  meta::for_each(meta::type_list<NumT<0>, NumT<1>, NumT<2> >(), ApplyAllTest(is_called_array));
+  assert(std::all_of(is_called_array, is_called_array + 3, Identity()));
+}
+
+TEST_CONSTEXPR_CXX20 bool test() {
+  test_for_each();
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 20
+  static_assert(test());
+#endif
+
+  return 0;
+}
index d5381b5..369393d 100644 (file)
@@ -18,6 +18,7 @@
 #include <utility>
 
 #include "test_macros.h"
+#include "type_algorithms.h"
 
 
 // This iterator meets C++20's Cpp17OutputIterator requirements, as described
@@ -1297,6 +1298,23 @@ template <std::ranges::input_range R>
   requires std::ranges::viewable_range<R&&>
 ProxyRange(R&&) -> ProxyRange<std::views::all_t<R&&>>;
 
+namespace meta {
+template <class Ptr>
+using random_access_iterator_list = type_list<Ptr, contiguous_iterator<Ptr>, random_access_iterator<Ptr>>;
+
+template <class Ptr>
+using bidirectional_iterator_list =
+    concatenate_t<random_access_iterator_list<Ptr>, type_list<bidirectional_iterator<Ptr>>>;
+
+template <class Ptr>
+using forward_iterator_list = concatenate_t<bidirectional_iterator_list<Ptr>, type_list<forward_iterator<Ptr>>>;
+
+template <class Ptr>
+using cpp20_input_iterator_list =
+    concatenate_t<forward_iterator_list<Ptr>, type_list<cpp20_input_iterator<Ptr>, cpp17_input_iterator<Ptr>>>;
+
+} // namespace meta
+
 #endif // TEST_STD_VER > 17
 
 #endif // SUPPORT_TEST_ITERATORS_H
diff --git a/libcxx/test/support/type_algorithms.h b/libcxx/test/support/type_algorithms.h
new file mode 100644 (file)
index 0000000..823978b
--- /dev/null
@@ -0,0 +1,105 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef TEST_SUPPORT_TYPE_ALGORITHMS_H
+#define TEST_SUPPORT_TYPE_ALGORITHMS_H
+
+#include <type_traits>
+
+#include "test_macros.h"
+
+namespace meta {
+template <class... Types>
+struct type_list {};
+
+// concatenates N type_lists to one (for N >= 1)
+template <class...>
+struct concatenate;
+
+template <class... Types>
+using concatenate_t = typename concatenate<Types...>::type;
+
+// for_each takes a type_list calls f with each element as the first template argument
+template <class... Types, class Functor>
+TEST_CONSTEXPR_CXX14 void for_each(type_list<Types...>, Functor f);
+
+// impl
+template <class... Types>
+struct concatenate<type_list<Types...> > {
+  using type = type_list<Types...>;
+};
+
+template <class... Types1, class... Types2>
+struct concatenate<type_list<Types1...>, type_list<Types2...> > {
+  using type = type_list<Types1..., Types2...>;
+};
+
+template <class... Types1, class... Types2, class... Rest>
+struct concatenate<type_list<Types1...>, type_list<Types2...>, Rest...> {
+  using type = concatenate_t<type_list<Types1..., Types2...>, Rest...>;
+};
+
+template <class... Types>
+TEST_CONSTEXPR_CXX14 void swallow(Types...) {}
+
+template <class... Types, class Functor>
+TEST_CONSTEXPR_CXX14 void for_each(type_list<Types...>, Functor f) {
+  swallow((f.template operator()<Types>(), 0)...);
+}
+
+// type categories defined in [basic.fundamental] plus extensions (without CV-qualifiers)
+
+using character_types =
+    type_list<char
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+              ,
+              wchar_t
+#endif
+#ifndef TEST_HAS_NO_CHAR8_T
+              ,
+              char8_t
+#endif
+#if TEST_STD_VER >= 11
+              ,
+              char16_t,
+              char32_t
+#endif
+              >;
+
+using signed_integer_types =
+    type_list<signed char,
+              short,
+              int,
+              long,
+              long long
+#ifndef TEST_HAS_NO_INT128
+              ,
+              __int128_t
+#endif
+              >;
+
+using unsigned_integer_types =
+    type_list<unsigned char,
+              unsigned short,
+              unsigned int,
+              unsigned long,
+              unsigned long long
+#ifndef TEST_HAS_NO_INT128
+              ,
+              __uint128_t
+#endif
+              >;
+
+using integral_types = concatenate_t<character_types, signed_integer_types, unsigned_integer_types, type_list<bool> >;
+
+using floating_point_types = type_list<float, double, long double>;
+
+using arithmetic_types = concatenate_t<integral_types, floating_point_types>;
+} // namespace meta
+
+#endif // TEST_SUPPORT_TYPE_ALGORITHMS_H