[libc++][pstl] Implementation of a parallel std::mismatch() based on __pstl::__parallel_find() (#209291)

This PR adds a parallel version of `std::mismatch` as one of the backend
operations.
It also provides an implementation based on `__pstl::__parallel_find()`
which does the heavy lifting.
`libdispatch` and `std_thread` backends expose this implementation under
their backend tags, while the `serial` backend redirects the calls to
the serial `std::mismatch`.

4 flavours of the function are exposed: 3-legged, 3-legged with
predicate, 4-legged, 4-legged with predicate.
3-legged flavours are implemented in the `default` (`composition`)
backend by redirecting the call to the 4-legged flavours.

Included tests check that:

- Semantics of the iterator-only functions is correct.
- Semantics of the predicated functions is correct.
- Custom types are supported by the implementation.
- The functions correctly SFINAE out when the first argument is not an
execution policy.
- The `nodiscard` policy is followed.
- The `noexcept` policy is followed.
- `static_assert` verifies iterators' categories.

Part of #99938.
This commit is contained in:
Michael G. Kazakov 2026-07-26 15:51:39 +01:00 committed by GitHub
parent a81db64570
commit 150f33ce28
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 809 additions and 1 deletions

View file

@ -680,6 +680,7 @@ set(files
__pstl/cpu_algos/find_if.h
__pstl/cpu_algos/for_each.h
__pstl/cpu_algos/merge.h
__pstl/cpu_algos/mismatch.h
__pstl/cpu_algos/stable_sort.h
__pstl/cpu_algos/transform.h
__pstl/cpu_algos/transform_reduce.h

View file

@ -31,6 +31,7 @@ _LIBCPP_PUSH_MACROS
# include <__type_traits/remove_cvref.h>
# include <__utility/forward.h>
# include <__utility/move.h>
# include <__utility/pair.h>
_LIBCPP_BEGIN_NAMESPACE_STD
@ -138,6 +139,95 @@ count(_ExecutionPolicy&& __policy, _ForwardIterator __first, _ForwardIterator __
std::forward<_ExecutionPolicy>(__policy), std::move(__first), std::move(__last), __value);
}
template <class _ExecutionPolicy,
class _ForwardIterator1,
class _ForwardIterator2,
class _BinaryPredicate,
class _RawPolicy = __remove_cvref_t<_ExecutionPolicy>,
enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI pair<_ForwardIterator1, _ForwardIterator2>
mismatch(_ExecutionPolicy&& __policy,
_ForwardIterator1 __first1,
_ForwardIterator1 __last1,
_ForwardIterator2 __first2,
_ForwardIterator2 __last2,
_BinaryPredicate __pred) {
_LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "mismatch requires ForwardIterators");
_LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "mismatch requires ForwardIterators");
using _Implementation = __pstl::__dispatch<__pstl::__mismatch, __pstl::__current_configuration, _RawPolicy>;
return __pstl::__handle_exception<_Implementation>(
std::forward<_ExecutionPolicy>(__policy),
std::move(__first1),
std::move(__last1),
std::move(__first2),
std::move(__last2),
std::move(__pred));
}
template <class _ExecutionPolicy,
class _ForwardIterator1,
class _ForwardIterator2,
class _RawPolicy = __remove_cvref_t<_ExecutionPolicy>,
enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI pair<_ForwardIterator1, _ForwardIterator2>
mismatch(_ExecutionPolicy&& __policy,
_ForwardIterator1 __first1,
_ForwardIterator1 __last1,
_ForwardIterator2 __first2,
_ForwardIterator2 __last2) {
_LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "mismatch requires ForwardIterators");
_LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "mismatch requires ForwardIterators");
using _Implementation = __pstl::__dispatch<__pstl::__mismatch, __pstl::__current_configuration, _RawPolicy>;
return __pstl::__handle_exception<_Implementation>(
std::forward<_ExecutionPolicy>(__policy),
std::move(__first1),
std::move(__last1),
std::move(__first2),
std::move(__last2),
equal_to{});
}
template <class _ExecutionPolicy,
class _ForwardIterator1,
class _ForwardIterator2,
class _BinaryPredicate,
class _RawPolicy = __remove_cvref_t<_ExecutionPolicy>,
enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI pair<_ForwardIterator1, _ForwardIterator2>
mismatch(_ExecutionPolicy&& __policy,
_ForwardIterator1 __first1,
_ForwardIterator1 __last1,
_ForwardIterator2 __first2,
_BinaryPredicate __pred) {
_LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "mismatch requires ForwardIterators");
_LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "mismatch requires ForwardIterators");
using _Implementation = __pstl::__dispatch<__pstl::__mismatch_3leg, __pstl::__current_configuration, _RawPolicy>;
return __pstl::__handle_exception<_Implementation>(
std::forward<_ExecutionPolicy>(__policy),
std::move(__first1),
std::move(__last1),
std::move(__first2),
std::move(__pred));
}
template <class _ExecutionPolicy,
class _ForwardIterator1,
class _ForwardIterator2,
class _RawPolicy = __remove_cvref_t<_ExecutionPolicy>,
enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI pair<_ForwardIterator1, _ForwardIterator2> mismatch(
_ExecutionPolicy&& __policy, _ForwardIterator1 __first1, _ForwardIterator1 __last1, _ForwardIterator2 __first2) {
_LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "mismatch requires ForwardIterators");
_LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "mismatch requires ForwardIterators");
using _Implementation = __pstl::__dispatch<__pstl::__mismatch_3leg, __pstl::__current_configuration, _RawPolicy>;
return __pstl::__handle_exception<_Implementation>(
std::forward<_ExecutionPolicy>(__policy),
std::move(__first1),
std::move(__last1),
std::move(__first2),
equal_to{});
}
template <class _ExecutionPolicy,
class _ForwardIterator1,
class _ForwardIterator2,

View file

@ -324,6 +324,21 @@ struct __adjacent_difference;
// operator()(_Policy&& __policy, _ForwardIterator1 __first1, _ForwardIterator1 __last1,
// _ForwardIterator2 __first2, _BinaryOperation &&__op) const noexcept;
template <class _Backend, class _ExecutionPolicy>
struct __mismatch;
// template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _Comp>
// optional<pair<_ForwardIterator1, _ForwardIterator2>>
// operator()(_Policy&& __policy, _ForwardIterator1 __first1, _ForwardIterator1 __last1,
// _ForwardIterator2 __first2, _ForwardIterator2 __last2,
// _Comp __comp) const noexcept;
template <class _Backend, class _ExecutionPolicy>
struct __mismatch_3leg;
// template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _Comp>
// optional<pair<_ForwardIterator1, _ForwardIterator2>>
// operator()(_Policy&& __policy, _ForwardIterator1 __first1, _ForwardIterator1 __last1,
// _ForwardIterator2 __first2, _Comp __comp) const noexcept;
} // namespace __pstl
_LIBCPP_END_NAMESPACE_STD

View file

@ -16,6 +16,7 @@
#include <__algorithm/find_if.h>
#include <__algorithm/for_each_n.h>
#include <__algorithm/is_sorted.h>
#include <__algorithm/mismatch.h>
#include <__config>
#include <__functional/identity.h>
#include <__functional/not_fn.h>
@ -63,6 +64,10 @@ namespace __pstl {
// - is_partitioned
// - find_first_of
//
// mismatch family
// ---------------
// - mismatch_3leg
//
// for_each family
// ---------------
// - for_each_n
@ -212,6 +217,38 @@ struct __find_first_of<__default_backend_tag, _ExecutionPolicy> {
}
};
//////////////////////////////////////////////////////////////
// mismatch family
//////////////////////////////////////////////////////////////
template <class _ExecutionPolicy>
struct __mismatch_3leg<__default_backend_tag, _ExecutionPolicy> {
template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _Comp>
optional<pair<_ForwardIterator1, _ForwardIterator2>>
operator()(_Policy&& __policy,
_ForwardIterator1 __first1,
_ForwardIterator1 __last1,
_ForwardIterator2 __first2,
_Comp __comp) const noexcept {
if constexpr (__has_random_access_iterator_category_or_concept<_ForwardIterator1>::value &&
__has_random_access_iterator_category_or_concept<_ForwardIterator2>::value) {
// Forward to the 4-legged version of mismatch.
using _Mismatch = __dispatch<__mismatch, __current_configuration, _ExecutionPolicy>;
_ForwardIterator2 __last2 = __first2 + (__last1 - __first1);
return _Mismatch()(
__policy,
std::move(__first1),
std::move(__last1),
std::move(__first2),
std::move(__last2),
std::move(__comp));
} else {
// Currently only random access iterators are supported for parallel mismatch_3leg.
return std::mismatch(std::move(__first1), std::move(__last1), std::move(__first2), std::move(__comp));
}
}
};
//////////////////////////////////////////////////////////////
// for_each family
//////////////////////////////////////////////////////////////

View file

@ -37,6 +37,7 @@
#include <__pstl/cpu_algos/find_if.h>
#include <__pstl/cpu_algos/for_each.h>
#include <__pstl/cpu_algos/merge.h>
#include <__pstl/cpu_algos/mismatch.h>
#include <__pstl/cpu_algos/stable_sort.h>
#include <__pstl/cpu_algos/transform.h>
#include <__pstl/cpu_algos/transform_reduce.h>
@ -241,7 +242,7 @@ struct __cpu_traits<__libdispatch_backend_tag> {
auto __this_chunk_size = __chunk == 0 ? __partitions.__first_chunk_size_ : __partitions.__chunk_size_;
auto __index = __chunk == 0 ? 0
: (__chunk * __partitions.__chunk_size_) +
(__partitions.__first_chunk_size_ - __partitions.__chunk_size_);
(__partitions.__first_chunk_size_ - __partitions.__chunk_size_);
if (__this_chunk_size != 1) {
std::__construct_at(
__values.get() + __chunk,
@ -368,6 +369,10 @@ template <class _ExecutionPolicy>
struct __merge<__libdispatch_backend_tag, _ExecutionPolicy>
: __cpu_parallel_merge<__libdispatch_backend_tag, _ExecutionPolicy> {};
template <class _ExecutionPolicy>
struct __mismatch<__libdispatch_backend_tag, _ExecutionPolicy>
: __cpu_parallel_mismatch<__libdispatch_backend_tag, _ExecutionPolicy> {};
template <class _ExecutionPolicy>
struct __stable_sort<__libdispatch_backend_tag, _ExecutionPolicy>
: __cpu_parallel_stable_sort<__libdispatch_backend_tag, _ExecutionPolicy> {};

View file

@ -13,6 +13,7 @@
#include <__algorithm/find_if.h>
#include <__algorithm/for_each.h>
#include <__algorithm/merge.h>
#include <__algorithm/mismatch.h>
#include <__algorithm/stable_sort.h>
#include <__algorithm/transform.h>
#include <__config>
@ -55,6 +56,25 @@ struct __find_if<__serial_backend_tag, _ExecutionPolicy> {
}
};
template <class _ExecutionPolicy>
struct __mismatch<__serial_backend_tag, _ExecutionPolicy> {
template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _Predicate>
_LIBCPP_HIDE_FROM_ABI optional<pair<_ForwardIterator1, _ForwardIterator2>>
operator()(_Policy&&,
_ForwardIterator1 __first1,
_ForwardIterator1 __last1,
_ForwardIterator2 __first2,
_ForwardIterator2 __last2,
_Predicate&& __pred) const noexcept {
return std::mismatch(
std::move(__first1),
std::move(__last1),
std::move(__first2),
std::move(__last2),
std::forward<_Predicate>(__pred));
}
};
template <class _ExecutionPolicy>
struct __for_each<__serial_backend_tag, _ExecutionPolicy> {
template <class _Policy, class _ForwardIterator, class _Function>

View file

@ -17,6 +17,7 @@
#include <__pstl/cpu_algos/find_if.h>
#include <__pstl/cpu_algos/for_each.h>
#include <__pstl/cpu_algos/merge.h>
#include <__pstl/cpu_algos/mismatch.h>
#include <__pstl/cpu_algos/stable_sort.h>
#include <__pstl/cpu_algos/transform.h>
#include <__pstl/cpu_algos/transform_reduce.h>
@ -100,6 +101,10 @@ template <class _ExecutionPolicy>
struct __merge<__std_thread_backend_tag, _ExecutionPolicy>
: __cpu_parallel_merge<__std_thread_backend_tag, _ExecutionPolicy> {};
template <class _ExecutionPolicy>
struct __mismatch<__std_thread_backend_tag, _ExecutionPolicy>
: __cpu_parallel_mismatch<__std_thread_backend_tag, _ExecutionPolicy> {};
template <class _ExecutionPolicy>
struct __stable_sort<__std_thread_backend_tag, _ExecutionPolicy>
: __cpu_parallel_stable_sort<__std_thread_backend_tag, _ExecutionPolicy> {};

View file

@ -0,0 +1,90 @@
//===----------------------------------------------------------------------===//
//
// 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 _LIBCPP___PSTL_CPU_ALGOS_MISMATCH_H
#define _LIBCPP___PSTL_CPU_ALGOS_MISMATCH_H
#include <__algorithm/min.h>
#include <__algorithm/mismatch.h>
#include <__config>
#include <__functional/operations.h>
#include <__iterator/concepts.h>
#include <__iterator/iterator_traits.h>
#include <__pstl/backend_fwd.h>
#include <__pstl/cpu_algos/cpu_traits.h>
#include <__pstl/cpu_algos/find_if.h>
#include <__type_traits/is_execution_policy.h>
#include <__utility/move.h>
#include <__utility/pair.h>
#include <optional>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
#endif
_LIBCPP_PUSH_MACROS
#include <__undef_macros>
#if _LIBCPP_STD_VER >= 17
_LIBCPP_BEGIN_NAMESPACE_STD
namespace __pstl {
template <class _Backend, class _RawExecutionPolicy>
struct __cpu_parallel_mismatch {
template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _Predicate>
_LIBCPP_HIDE_FROM_ABI optional<pair<_ForwardIterator1, _ForwardIterator2>>
operator()(_Policy&&,
_ForwardIterator1 __first1,
_ForwardIterator1 __last1,
_ForwardIterator2 __first2,
_ForwardIterator2 __last2,
_Predicate __pred) const noexcept {
if constexpr (__is_parallel_execution_policy_v<_RawExecutionPolicy> &&
__has_random_access_iterator_category_or_concept<_ForwardIterator1>::value &&
__has_random_access_iterator_category_or_concept<_ForwardIterator2>::value) {
// Look for a mismatch only in the prefix of the two ranges.
auto __n = std::min(__last1 - __first1, __last2 - __first2);
// Find a position in the first range where the predicate is false against the corresponding position in the
// second range.
auto __res = __pstl::__parallel_find<_Backend>(
__first1,
__first1 + __n,
[&__pred, __first1, __first2](_ForwardIterator1 __brick_first1, _ForwardIterator1 __brick_last1) {
// Run the sequential mismatch algorithm on these ranges:
// [__brick_first1, __brick_last1) and
// [__first2 + (__brick_first1 - __first1), __first2 + (__brick_last1 - __first1))
auto __brick_first2 = __first2 + (__brick_first1 - __first1);
return std::mismatch(std::move(__brick_first1), std::move(__brick_last1), std::move(__brick_first2), __pred)
.first;
},
less<>{}, // `less` here means the lowest index among the mismatches
true // `true` here means we want the first mismatch, not the last
);
if (!__res) {
return std::nullopt; // Failed to run the algorithm, propagate the error.
}
auto __idx = *__res - __first1;
return pair<_ForwardIterator1, _ForwardIterator2>{std::move(*__res), __first2 + __idx};
} else {
// Non-random access iterators cannot be processed in parallel, fall back to the sequential implementation.
// Unsequenced execution is also implicitly covered by the sequential implementation.
return std::mismatch(
std::move(__first1), std::move(__last1), std::move(__first2), std::move(__last2), std::move(__pred));
}
}
};
} // namespace __pstl
_LIBCPP_END_NAMESPACE_STD
#endif // _LIBCPP_STD_VER >= 17
_LIBCPP_POP_MACROS
#endif // _LIBCPP___PSTL_CPU_ALGOS_MISMATCH_H

View file

@ -2388,6 +2388,9 @@ module std {
module merge {
header "__pstl/cpu_algos/merge.h"
}
module mismatch {
header "__pstl/cpu_algos/mismatch.h"
}
module stable_sort {
header "__pstl/cpu_algos/stable_sort.h"
export std_core.utility_core.empty

View file

@ -135,6 +135,22 @@ void f(non_forward_iterator non_fwd,
(void)std::merge(pol, it, it, it, it, non_output, pred); // expected-error@*:* {{static assertion failed: merge}}
}
{
(void)std::mismatch(pol, non_fwd, non_fwd, it); // expected-error@*:* {{static assertion failed: mismatch}}
(void)std::mismatch(pol, it, it, non_fwd); // expected-error@*:* {{static assertion failed: mismatch}}
(void)std::mismatch(pol, non_fwd, non_fwd, it, it); // expected-error@*:* {{static assertion failed: mismatch}}
(void)std::mismatch(pol, it, it, non_fwd, non_fwd); // expected-error@*:* {{static assertion failed: mismatch}}
(void)std::mismatch(pol, non_fwd, non_fwd, it, pred); // expected-error@*:* {{static assertion failed: mismatch}}
(void)std::mismatch(pol, it, it, non_fwd, pred); // expected-error@*:* {{static assertion failed: mismatch}}
(void)std::mismatch(
pol, non_fwd, non_fwd, it, it, pred); // expected-error@*:* {{static assertion failed: mismatch}}
(void)std::mismatch(
pol, it, it, non_fwd, non_fwd, pred); // expected-error@*:* {{static assertion failed: mismatch}}
}
{
(void)std::move(pol, non_fwd, non_fwd, out); // expected-error@*:* {{static assertion failed: move}}
(void)std::move(pol, it, it, non_fwd); // expected-error@*:* {{static assertion failed: move}}

View file

@ -63,4 +63,12 @@ void test() {
std::adjacent_difference(std::execution::par, std::begin(a), std::end(a), std::begin(b));
// expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}}
std::adjacent_difference(std::execution::par, std::begin(a), std::end(a), std::begin(b), pred2);
// expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}}
std::mismatch(std::execution::par, std::begin(a), std::end(a), std::begin(b));
// expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}}
std::mismatch(std::execution::par, std::begin(a), std::end(a), std::begin(b), std::end(b));
// expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}}
std::mismatch(std::execution::par, std::begin(a), std::end(a), std::begin(b), pred2);
// expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}}
std::mismatch(std::execution::par, std::begin(a), std::end(a), std::begin(b), std::end(b), pred2);
}

View file

@ -0,0 +1,226 @@
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
// REQUIRES: std-at-least-c++17
// UNSUPPORTED: libcpp-has-no-incomplete-pstl
// template <class ExecutionPolicy,
// class ForwardIterator1,
// class ForwardIterator2>
// pair<ForwardIterator1, ForwardIterator2> mismatch(ExecutionPolicy&& exec,
// ForwardIterator1 first1,
// ForwardIterator1 last1,
// ForwardIterator2 first2);
//
// template <class ExecutionPolicy,
// class ForwardIterator1,
// class ForwardIterator2>
// pair<ForwardIterator1, ForwardIterator2> mismatch(ExecutionPolicy&& exec,
// ForwardIterator1 first1,
// ForwardIterator1 last1,
// ForwardIterator2 first2,
// ForwardIterator2 last2);
#include <algorithm>
#include <cassert>
#include <functional>
#include <iterator>
#include <limits>
#include <numeric>
#include "test_execution_policies.h"
#include "test_iterators.h"
#include "test_macros.h"
#include "type_algorithms.h"
#include "runway_sample.h"
EXECUTION_POLICY_SFINAE_TEST(mismatch);
static_assert(sfinae_test_mismatch<int, int*, int*, int*>);
static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*>);
static_assert(sfinae_test_mismatch<int, int*, int*, int*, int*>);
static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*, int*>);
// The types X and Y are provided to test that the mismatch algorithm can be used with heterogeneous custom types.
struct X {
X() = delete;
X(int i) : i_(i) {}
X(const X&) = delete;
int value() const { return i_; }
private:
int i_;
};
struct Y {
Y() = delete;
Y(int i) : i_(i) {}
Y(const Y&) = delete;
int value() const { return i_; }
private:
int i_;
};
bool operator==(const X& lhs, const Y& rhs) { return lhs.value() == rhs.value(); }
template <class Iter1, class Iter2>
struct Test {
template <class ExecutionPolicy>
void operator()(ExecutionPolicy&& policy) {
{
// check the return types
int lhs[1] = {0};
int rhs[1] = {0};
auto res_3legged = std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::begin(lhs)), Iter2(std::begin(rhs)));
auto res_4legged = std::mismatch(
policy, Iter1(std::begin(lhs)), Iter1(std::begin(lhs)), Iter2(std::begin(rhs)), Iter2(std::begin(rhs)));
static_assert(std::is_same_v<decltype(res_3legged), std::pair<Iter1, Iter2>>);
static_assert(std::is_same_v<decltype(res_4legged), std::pair<Iter1, Iter2>>);
}
{
// empty ranges
int lhs[1] = {0};
int rhs[1] = {0};
assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::begin(lhs)), Iter2(std::begin(rhs))) ==
std::make_pair(Iter1(std::begin(lhs)), Iter2(std::begin(rhs))));
assert(
std::mismatch(
policy, Iter1(std::begin(lhs)), Iter1(std::begin(lhs)), Iter2(std::begin(rhs)), Iter2(std::begin(rhs))) ==
std::make_pair(Iter1(std::begin(lhs)), Iter2(std::begin(rhs))));
}
{
// single element only
int lhs[1] = {0};
int rhs[1] = {0};
assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
assert(std::mismatch(
policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
}
{ // same range without mismatch
int lhs[8] = {0, 1, 2, 3, 0, 1, 2, 3};
int rhs[8] = {0, 1, 2, 3, 0, 1, 2, 3};
assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
assert(std::mismatch(
policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
}
{ // same range with mismatch
int lhs[8] = {0, 1, 2, 2, 0, 1, 2, 3};
int rhs[8] = {0, 1, 2, 3, 0, 1, 2, 3};
assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
std::make_pair(Iter1(std::begin(lhs) + 3), Iter2(std::begin(rhs) + 3)));
assert(std::mismatch(
policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
std::make_pair(Iter1(std::begin(lhs) + 3), Iter2(std::begin(rhs) + 3)));
}
{ // second range is smaller
int lhs[8] = {0, 1, 2, 2, 0, 1, 2, 3};
int rhs[2] = {0, 1};
assert(std::mismatch(
policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
}
{ // first range is smaller
int lhs[2] = {0, 1};
int rhs[8] = {0, 1, 2, 2, 0, 1, 2, 3};
assert(std::mismatch(
policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
}
{ // same size, mismatching at various positions
int lhs[1073];
int rhs[1073];
std::iota(std::begin(lhs), std::end(lhs), 0);
std::copy(std::begin(lhs), std::end(lhs), std::begin(rhs));
runway_sample(std::size(lhs), [&](size_t i) {
lhs[i] = -1;
assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
assert(
std::mismatch(
policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
lhs[i] = i;
});
assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
assert(std::mismatch(
policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
}
{ // same values, different lengths
int lhs[739];
int rhs[739];
std::fill(std::begin(lhs), std::end(lhs), 42);
std::fill(std::begin(rhs), std::end(rhs), 42);
runway_sample(std::size(lhs), [&](size_t i) {
// lhs is shorter
assert(std::mismatch(
policy,
Iter1(std::begin(lhs)),
Iter1(std::begin(lhs) + i),
Iter2(std::begin(rhs)),
Iter2(std::end(rhs))) == std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
// rhs is shorter
assert(std::mismatch(policy,
Iter1(std::begin(lhs)),
Iter1(std::end(lhs)),
Iter2(std::begin(rhs)),
Iter2(std::begin(rhs) + i)) ==
std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
});
}
}
};
template <class Iter1, class Iter2>
struct TestXY {
template <class ExecutionPolicy>
void operator()(ExecutionPolicy&& policy) {
{ // same ranges
X lhs[3] = {X(1), X(5), X(7)};
Y rhs[3] = {Y(1), Y(5), Y(7)};
assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
assert(std::mismatch(
policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
}
{ // one element mismatch
X lhs[3] = {X(1), X(5), X(7)};
Y rhs[3] = {Y(1), Y(5), Y(8)};
assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
assert(std::mismatch(
policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
}
}
};
int main(int, char**) {
types::for_each(types::forward_iterator_list<const int*>{}, types::apply_type_identity{[](auto v) {
using Iter = typename decltype(v)::type;
types::for_each(
types::forward_iterator_list<const int*>{},
TestIteratorWithPolicies<types::partial_instantiation<Test, Iter>::template apply>{});
}});
types::for_each(types::forward_iterator_list<const X*>{}, types::apply_type_identity{[](auto v) {
using Iter = typename decltype(v)::type;
types::for_each(
types::forward_iterator_list<const Y*>{},
TestIteratorWithPolicies<types::partial_instantiation<TestXY, Iter>::template apply>{});
}});
return 0;
}

View file

@ -0,0 +1,267 @@
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
// REQUIRES: std-at-least-c++17
// UNSUPPORTED: libcpp-has-no-incomplete-pstl
// template <class ExecutionPolicy,
// class ForwardIterator1,
// class ForwardIterator2,
// class BinaryPredicate>
// pair<ForwardIterator1, ForwardIterator2> mismatch(ExecutionPolicy&& exec,
// ForwardIterator1 first1,
// ForwardIterator1 last1,
// ForwardIterator2 first2,
// BinaryPredicate pred);
//
// template <class ExecutionPolicy,
// class ForwardIterator1,
// class ForwardIterator2,
// class BinaryPredicate>
// pair<ForwardIterator1, ForwardIterator2> mismatch(ExecutionPolicy&& exec,
// ForwardIterator1 first1,
// ForwardIterator1 last1,
// ForwardIterator2 first2,
// ForwardIterator2 last2,
// BinaryPredicate pred);
#include <algorithm>
#include <cassert>
#include <functional>
#include <iterator>
#include <limits>
#include <numeric>
#include "test_execution_policies.h"
#include "test_iterators.h"
#include "test_macros.h"
#include "type_algorithms.h"
#include "runway_sample.h"
EXECUTION_POLICY_SFINAE_TEST(mismatch);
static_assert(sfinae_test_mismatch<int, int*, int*, int*, bool (*)(int, int)>);
static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*, bool (*)(int, int)>);
static_assert(sfinae_test_mismatch<int, int*, int*, int*, int*, bool (*)(int, int)>);
static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*, int*, bool (*)(int, int)>);
// The types X and Y are provided to test that the mismatch algorithm can be used with heterogeneous custom types.
struct X {
X() = delete;
X(int i) : i_(i) {}
X(const X&) = delete;
int value() const { return i_; }
private:
int i_;
};
struct Y {
Y() = delete;
Y(int i) : i_(i) {}
Y(const Y&) = delete;
int value() const { return i_; }
private:
int i_;
};
struct Pred {
bool operator()(int lhs, int rhs) const { return lhs * 2 == rhs; }
bool operator()(const X& lhs, const Y& rhs) const { return lhs.value() * 2 == rhs.value(); }
};
template <class Iter1, class Iter2>
struct Test {
template <class ExecutionPolicy>
void operator()(ExecutionPolicy&& policy) {
{
// check the return types
int lhs[1] = {0};
int rhs[1] = {0};
auto res_3legged =
std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::begin(lhs)), Iter2(std::begin(rhs)), Pred{});
auto res_4legged = std::mismatch(
policy,
Iter1(std::begin(lhs)),
Iter1(std::begin(lhs)),
Iter2(std::begin(rhs)),
Iter2(std::begin(rhs)),
Pred{});
static_assert(std::is_same_v<decltype(res_3legged), std::pair<Iter1, Iter2>>);
static_assert(std::is_same_v<decltype(res_4legged), std::pair<Iter1, Iter2>>);
}
{
// empty ranges
int lhs[1] = {0};
int rhs[1] = {0};
assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::begin(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
std::make_pair(Iter1(std::begin(lhs)), Iter2(std::begin(rhs))));
assert(std::mismatch(policy,
Iter1(std::begin(lhs)),
Iter1(std::begin(lhs)),
Iter2(std::begin(rhs)),
Iter2(std::begin(rhs)),
Pred{}) == std::make_pair(Iter1(std::begin(lhs)), Iter2(std::begin(rhs))));
}
{
// single element only
int lhs[1] = {1};
int rhs[1] = {2};
assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
assert(std::mismatch(policy,
Iter1(std::begin(lhs)),
Iter1(std::end(lhs)),
Iter2(std::begin(rhs)),
Iter2(std::end(rhs)),
Pred{}) == std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
}
{ // same range without mismatch
int lhs[8] = {0, 1, 2, 3, 0, 1, 2, 3};
int rhs[8] = {0, 2, 4, 6, 0, 2, 4, 6};
assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
assert(std::mismatch(policy,
Iter1(std::begin(lhs)),
Iter1(std::end(lhs)),
Iter2(std::begin(rhs)),
Iter2(std::end(rhs)),
Pred{}) == std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
}
{ // same range with mismatch
int lhs[8] = {0, 1, 2, 3, 0, 1, 2, 3};
int rhs[8] = {0, 2, 4, 7, 0, 2, 4, 6};
assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
std::make_pair(Iter1(std::begin(lhs) + 3), Iter2(std::begin(rhs) + 3)));
assert(std::mismatch(policy,
Iter1(std::begin(lhs)),
Iter1(std::end(lhs)),
Iter2(std::begin(rhs)),
Iter2(std::end(rhs)),
Pred{}) == std::make_pair(Iter1(std::begin(lhs) + 3), Iter2(std::begin(rhs) + 3)));
}
{ // second range is smaller
int lhs[8] = {0, 1, 2, 2, 0, 1, 2, 3};
int rhs[2] = {0, 2};
assert(std::mismatch(policy,
Iter1(std::begin(lhs)),
Iter1(std::end(lhs)),
Iter2(std::begin(rhs)),
Iter2(std::end(rhs)),
Pred{}) == std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
}
{ // first range is smaller
int lhs[2] = {0, 1};
int rhs[8] = {0, 2, 2, 2, 0, 1, 2, 3};
assert(std::mismatch(policy,
Iter1(std::begin(lhs)),
Iter1(std::end(lhs)),
Iter2(std::begin(rhs)),
Iter2(std::end(rhs)),
Pred{}) == std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
}
{ // same size, mismatching at various positions
int lhs[1073];
int rhs[1073];
std::iota(std::begin(lhs), std::end(lhs), 0);
std::transform(std::begin(lhs), std::end(lhs), std::begin(rhs), [](int x) { return x * 2; });
runway_sample(std::size(lhs), [&](size_t i) {
lhs[i] = -1;
assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
assert(std::mismatch(policy,
Iter1(std::begin(lhs)),
Iter1(std::end(lhs)),
Iter2(std::begin(rhs)),
Iter2(std::end(rhs)),
Pred{}) == std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
lhs[i] = i;
});
assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
assert(std::mismatch(policy,
Iter1(std::begin(lhs)),
Iter1(std::end(lhs)),
Iter2(std::begin(rhs)),
Iter2(std::end(rhs)),
Pred{}) == std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
}
{ // same values, different lengths
int lhs[739];
int rhs[739];
std::fill(std::begin(lhs), std::end(lhs), 42);
std::fill(std::begin(rhs), std::end(rhs), 84);
runway_sample(std::size(lhs), [&](size_t i) {
// lhs is shorter
assert(std::mismatch(policy,
Iter1(std::begin(lhs)),
Iter1(std::begin(lhs) + i),
Iter2(std::begin(rhs)),
Iter2(std::end(rhs)),
Pred{}) == std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
// rhs is shorter
assert(std::mismatch(policy,
Iter1(std::begin(lhs)),
Iter1(std::end(lhs)),
Iter2(std::begin(rhs)),
Iter2(std::begin(rhs) + i),
Pred{}) == std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
});
}
}
};
template <class Iter1, class Iter2>
struct TestXY {
template <class ExecutionPolicy>
void operator()(ExecutionPolicy&& policy) {
{ // same ranges
X lhs[3] = {X(1), X(5), X(7)};
Y rhs[3] = {Y(2), Y(10), Y(14)};
assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
assert(std::mismatch(policy,
Iter1(std::begin(lhs)),
Iter1(std::end(lhs)),
Iter2(std::begin(rhs)),
Iter2(std::end(rhs)),
Pred{}) == std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
}
{ // one element mismatch
X lhs[3] = {X(1), X(5), X(7)};
Y rhs[3] = {Y(2), Y(10), Y(13)};
assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
assert(std::mismatch(policy,
Iter1(std::begin(lhs)),
Iter1(std::end(lhs)),
Iter2(std::begin(rhs)),
Iter2(std::end(rhs)),
Pred{}) == std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
}
}
};
int main(int, char**) {
types::for_each(types::forward_iterator_list<const int*>{}, types::apply_type_identity{[](auto v) {
using Iter = typename decltype(v)::type;
types::for_each(
types::forward_iterator_list<const int*>{},
TestIteratorWithPolicies<types::partial_instantiation<Test, Iter>::template apply>{});
}});
types::for_each(types::forward_iterator_list<const X*>{}, types::apply_type_identity{[](auto v) {
using Iter = typename decltype(v)::type;
types::for_each(
types::forward_iterator_list<const Y*>{},
TestIteratorWithPolicies<types::partial_instantiation<TestXY, Iter>::template apply>{});
}});
return 0;
}

View file

@ -248,6 +248,31 @@ int main(int, char**) {
});
}
{
auto compare = maybe_throw(tokens[5], [](int x, int y) -> bool { return x < y; });
// mismatch(first1, last1, first2)
assert_non_throwing([=, &policy] {
(void)std::mismatch(policy, std::move(first1), std::move(last1), std::move(first2));
});
// mismatch(first1, last1, first2, last2)
assert_non_throwing([=, &policy] {
(void)std::mismatch(policy, std::move(first1), std::move(last1), std::move(first2), std::move(last2));
});
// mismatch(first1, last1, first2, pred)
assert_non_throwing([=, &policy] {
(void)std::mismatch(policy, std::move(first1), std::move(last1), std::move(first2), compare);
});
// mismatch(first1, last1, first2, last2, pred)
assert_non_throwing([=, &policy] {
(void)std::mismatch(
policy, std::move(first1), std::move(last1), std::move(first2), std::move(last2), compare);
});
}
{
// move(first, last, dest)
assert_non_throwing([=, &policy] {