Skip to content

Commit

Permalink
[libc++] Add utilites for instantiating functions with multiple types
Browse files Browse the repository at this point in the history
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
  • Loading branch information
philnik777 committed Nov 21, 2022
1 parent bd68070 commit 1323461
Show file tree
Hide file tree
Showing 6 changed files with 243 additions and 76 deletions.
Expand Up @@ -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); };
Expand Down Expand Up @@ -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}));
}
Expand Down
Expand Up @@ -26,11 +26,11 @@
#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,
Expand All @@ -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;
}
23 changes: 16 additions & 7 deletions libcxx/test/std/strings/basic.string/string.access/at.pass.cpp
Expand Up @@ -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
Expand Down Expand Up @@ -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;
}
Expand Down
76 changes: 76 additions & 0 deletions libcxx/test/support/test.support/type_algorithms.pass.cpp
@@ -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;
}
18 changes: 18 additions & 0 deletions libcxx/test/support/test_iterators.h
Expand Up @@ -18,6 +18,7 @@
#include <utility>

#include "test_macros.h"
#include "type_algorithms.h"


// This iterator meets C++20's Cpp17OutputIterator requirements, as described
Expand Down Expand Up @@ -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
105 changes: 105 additions & 0 deletions libcxx/test/support/type_algorithms.h
@@ -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

0 comments on commit 1323461

Please sign in to comment.