Differential D138364 Diff 476707 libcxx/test/libcxx/algorithms/alg.modifying.operations/copy_move_trivial.pass.cpp
Changeset View
Changeset View
Standalone View
Standalone View
libcxx/test/libcxx/algorithms/alg.modifying.operations/copy_move_trivial.pass.cpp
- This file was added.
//===----------------------------------------------------------------------===// | |||||
// | |||||
// 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 | |||||
// | |||||
//===----------------------------------------------------------------------===// | |||||
// UNSUPPORTED: c++03, c++11, c++14, c++17 | |||||
// When the debug mode is enabled, we don't unwrap iterators in `std::copy` and similar algorithms so we don't get this | |||||
// optimization. | |||||
// UNSUPPORTED: libcpp-has-debug-mode | |||||
// In the modules build, adding another overload of `memmove` doesn't work. | |||||
// UNSUPPORTED: modules-build | |||||
// GCC complains about "ambiguating" `__builtin_memmove`. | |||||
// UNSUPPORTED: gcc | |||||
// <algorithm> | |||||
// These tests checks that `std::copy` and `std::move` (including their variations like `copy_n`) forward to | |||||
// `memmove` when possible. | |||||
#include <cstddef> | |||||
struct Foo { | |||||
int i = 0; | |||||
Foo() = default; | |||||
Foo(int set_i) : i(set_i) {} | |||||
friend bool operator==(const Foo&, const Foo&) = default; | |||||
}; | |||||
static bool memmove_called = false; | |||||
// This template is a better match than the actual `builtin_memmove` (it can match the pointer type exactly, without an | |||||
// implicit conversion to `void*`), so it should hijack the call inside `std::copy` and similar algorithms if it's made. | |||||
template <class Dst, class Src> | |||||
constexpr void* __builtin_memmove(Dst* dst, Src* src, size_t count) { | |||||
memmove_called = true; | |||||
return __builtin_memmove(static_cast<void*>(dst), static_cast<const void*>(src), count); | |||||
} | |||||
#include <algorithm> | |||||
#include <cassert> | |||||
#include <cstdint> | |||||
#include <iterator> | |||||
#include <ranges> | |||||
#include <type_traits> | |||||
#include "test_iterators.h" | |||||
static_assert(std::is_trivially_copyable_v<Foo>); | |||||
template <size_t N, class Iter> | |||||
requires (N == 0) | |||||
constexpr auto wrap_n_times(Iter i) { | |||||
return i; | |||||
} | |||||
template <size_t N, class Iter> | |||||
requires (N != 0) | |||||
constexpr auto wrap_n_times(Iter i) { | |||||
return std::make_reverse_iterator(wrap_n_times<N - 1>(i)); | |||||
} | |||||
static_assert(std::is_same_v<decltype(wrap_n_times<2>(std::declval<int*>())), | |||||
std::reverse_iterator<std::reverse_iterator<int*>>>); | |||||
template <class InIter, template <class> class SentWrapper, class OutIter, size_t W1, size_t W2, class Func> | |||||
void test_one(Func func) { | |||||
using From = std::iter_value_t<InIter>; | |||||
using To = std::iter_value_t<OutIter>; | |||||
// Normal case. | |||||
{ | |||||
const size_t N = 4; | |||||
From input[N] = {{1}, {2}, {3}, {4}}; | |||||
To output[N]; | |||||
auto in = wrap_n_times<W1>(InIter(input)); | |||||
auto in_end = wrap_n_times<W1>(InIter(input + N)); | |||||
auto sent = SentWrapper<decltype(in_end)>(in_end); | |||||
auto out = wrap_n_times<W2>(OutIter(output)); | |||||
assert(!memmove_called); | |||||
func(in, sent, out, N); | |||||
assert(memmove_called); | |||||
memmove_called = false; | |||||
assert(std::equal(input, input + N, output, [](const From& lhs, const To& rhs) { | |||||
// Prevents warnings/errors due to mismatched signed-ness. | |||||
return lhs == static_cast<From>(rhs); | |||||
})); | |||||
} | |||||
// Empty input sequence. | |||||
{ | |||||
const size_t N = 0; | |||||
From input[1] = {1}; | |||||
To output[1] = {2}; | |||||
auto in = wrap_n_times<W1>(InIter(input)); | |||||
auto in_end = wrap_n_times<W1>(InIter(input + N)); | |||||
auto sent = SentWrapper<decltype(in_end)>(in_end); | |||||
auto out = wrap_n_times<W2>(OutIter(output)); | |||||
assert(!memmove_called); | |||||
func(in, sent, out, N); | |||||
assert(output[0] == To{2}); | |||||
assert(memmove_called); | |||||
memmove_called = false; | |||||
} | |||||
} | |||||
template <class InIter, template <class> class SentWrapper, class OutIter, size_t W1, size_t W2> | |||||
void test_copy_and_move() { | |||||
// Classic. | |||||
if constexpr (std::same_as<InIter, SentWrapper<InIter>>) { | |||||
test_one<InIter, SentWrapper, OutIter, W1, W2>([](auto first, auto last, auto out, size_t) { | |||||
std::copy(first, last, out); | |||||
}); | |||||
test_one<InIter, SentWrapper, OutIter, W1, W2>([](auto first, auto last, auto out, size_t n) { | |||||
std::copy_backward(first, last, out + n); | |||||
}); | |||||
test_one<InIter, SentWrapper, OutIter, W1, W2>([](auto first, auto, auto out, size_t n) { | |||||
std::copy_n(first, n, out); | |||||
}); | |||||
test_one<InIter, SentWrapper, OutIter, W1, W2>([](auto first, auto last, auto out, size_t) { | |||||
std::move(first, last, out); | |||||
}); | |||||
test_one<InIter, SentWrapper, OutIter, W1, W2>([](auto first, auto last, auto out, size_t n) { | |||||
std::move_backward(first, last, out + n); | |||||
}); | |||||
} | |||||
// Ranges. | |||||
test_one<InIter, SentWrapper, OutIter, W1, W2>([](auto first, auto last, auto out, size_t) { | |||||
std::ranges::copy(first, last, out); | |||||
}); | |||||
test_one<InIter, SentWrapper, OutIter, W1, W2>([](auto first, auto last, auto out, size_t n) { | |||||
std::ranges::copy_backward(first, last, out + n); | |||||
}); | |||||
test_one<InIter, SentWrapper, OutIter, W1, W2>([](auto first, auto, auto out, size_t n) { | |||||
std::ranges::copy_n(first, n, out); | |||||
}); | |||||
test_one<InIter, SentWrapper, OutIter, W1, W2>([](auto first, auto last, auto out, size_t) { | |||||
std::ranges::move(first, last, out); | |||||
}); | |||||
test_one<InIter, SentWrapper, OutIter, W1, W2>([](auto first, auto last, auto out, size_t n) { | |||||
std::ranges::move_backward(first, last, out + n); | |||||
}); | |||||
} | |||||
template <size_t W1, size_t W2, class From, class To, template <class> class SentWrapper> | |||||
void test_all_permutations_with_counts_from_to_sent() { | |||||
test_copy_and_move<From*, SentWrapper, To*, W1, W2>(); | |||||
test_copy_and_move<contiguous_iterator<From*>, SentWrapper, To*, W1, W2>(); | |||||
test_copy_and_move<From*, SentWrapper, contiguous_iterator<To*>, W1, W2>(); | |||||
test_copy_and_move<contiguous_iterator<From*>, SentWrapper, contiguous_iterator<To*>, W1, W2>(); | |||||
if (!std::same_as<From, To>) { | |||||
test_copy_and_move<To*, SentWrapper, From*, W1, W2>(); | |||||
test_copy_and_move<contiguous_iterator<To*>, SentWrapper, From*, W1, W2>(); | |||||
test_copy_and_move<To*, SentWrapper, contiguous_iterator<From*>, W1, W2>(); | |||||
test_copy_and_move<contiguous_iterator<To*>, SentWrapper, contiguous_iterator<From*>, W1, W2>(); | |||||
} | |||||
} | |||||
template <size_t W1, size_t W2> | |||||
void test_all_permutations_with_counts() { | |||||
// Built-in. | |||||
test_all_permutations_with_counts_from_to_sent<W1, W2, int, int, std::type_identity_t>(); | |||||
// User-defined. | |||||
test_all_permutations_with_counts_from_to_sent<W1, W2, Foo, Foo, std::type_identity_t>(); | |||||
// Conversions. | |||||
test_all_permutations_with_counts_from_to_sent<W1, W2, char32_t, std::int32_t, sized_sentinel>(); | |||||
test_all_permutations_with_counts_from_to_sent<W1, W2, std::int32_t, std::uint32_t, sized_sentinel>(); | |||||
} | |||||
void test() { | |||||
test_all_permutations_with_counts<0, 0>(); | |||||
test_all_permutations_with_counts<0, 2>(); | |||||
test_all_permutations_with_counts<2, 0>(); | |||||
test_all_permutations_with_counts<2, 2>(); | |||||
test_all_permutations_with_counts<2, 4>(); | |||||
test_all_permutations_with_counts<4, 4>(); | |||||
} | |||||
int main(int, char**) { | |||||
test(); | |||||
// The test relies on a global variable, so it cannot be made `constexpr`; the `memmove` optimization is not used in | |||||
// `constexpr` mode anyway. | |||||
return 0; | |||||
} |