Viewing file: test_utils.h (38.04 KB) -rw-r--r-- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
// -*- C++ -*- //===-- utils.h -----------------------------------------------------------===// // // 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 // //===----------------------------------------------------------------------===//
// File contains common utilities that tests rely on
// Do not #include <algorithm>, because if we do we will not detect accidental dependencies. #include <atomic> #include <cstdint> #include <cstdlib> #include <cstring> #include <iostream> #include <iterator> #include <memory> #include <sstream> #include <vector>
#include "pstl_test_config.h"
namespace TestUtils {
typedef double float64_t; typedef float float32_t;
template <class T, std::size_t N> constexpr size_t const_size(const T (&array)[N]) noexcept { return N; }
template <typename T> class Sequence;
// Handy macros for error reporting #define EXPECT_TRUE(condition, message) ::TestUtils::expect(true, condition, __FILE__, __LINE__, message) #define EXPECT_FALSE(condition, message) ::TestUtils::expect(false, condition, __FILE__, __LINE__, message)
// Check that expected and actual are equal and have the same type. #define EXPECT_EQ(expected, actual, message) ::TestUtils::expect_equal(expected, actual, __FILE__, __LINE__, message)
// Check that sequences started with expected and actual and have had size n are equal and have the same type. #define EXPECT_EQ_N(expected, actual, n, message) \ ::TestUtils::expect_equal(expected, actual, n, __FILE__, __LINE__, message)
// Issue error message from outstr, adding a newline. // Real purpose of this routine is to have a place to hang a breakpoint. inline void issue_error_message(std::stringstream& outstr) { outstr << std::endl; std::cerr << outstr.str(); std::exit(EXIT_FAILURE); }
inline void expect(bool expected, bool condition, const char* file, int32_t line, const char* message) { if (condition != expected) { std::stringstream outstr; outstr << "error at " << file << ":" << line << " - " << message; issue_error_message(outstr); } }
// Do not change signature to const T&. // Function must be able to detect const differences between expected and actual. template <typename T> void expect_equal(T& expected, T& actual, const char* file, int32_t line, const char* message) { if (!(expected == actual)) { std::stringstream outstr; outstr << "error at " << file << ":" << line << " - " << message << ", expected " << expected << " got " << actual; issue_error_message(outstr); } }
template <typename T> void expect_equal(Sequence<T>& expected, Sequence<T>& actual, const char* file, int32_t line, const char* message) { size_t n = expected.size(); size_t m = actual.size(); if (n != m) { std::stringstream outstr; outstr << "error at " << file << ":" << line << " - " << message << ", expected sequence of size " << n << " got sequence of size " << m; issue_error_message(outstr); return; } size_t error_count = 0; for (size_t k = 0; k < n && error_count < 10; ++k) { if (!(expected[k] == actual[k])) { std::stringstream outstr; outstr << "error at " << file << ":" << line << " - " << message << ", at index " << k << " expected " << expected[k] << " got " << actual[k]; issue_error_message(outstr); ++error_count; } } }
template <typename Iterator1, typename Iterator2, typename Size> void expect_equal(Iterator1 expected_first, Iterator2 actual_first, Size n, const char* file, int32_t line, const char* message) { size_t error_count = 0; for (size_t k = 0; k < n && error_count < 10; ++k, ++expected_first, ++actual_first) { if (!(*expected_first == *actual_first)) { std::stringstream outstr; outstr << "error at " << file << ":" << line << " - " << message << ", at index " << k; issue_error_message(outstr); ++error_count; } } }
// ForwardIterator is like type Iterator, but restricted to be a forward iterator. // Only the forward iterator signatures that are necessary for tests are present. // Post-increment in particular is deliberatly omitted since our templates should avoid using it // because of efficiency considerations. template <typename Iterator, typename IteratorTag> class ForwardIterator { public: typedef IteratorTag iterator_category; typedef typename std::iterator_traits<Iterator>::value_type value_type; typedef typename std::iterator_traits<Iterator>::difference_type difference_type; typedef typename std::iterator_traits<Iterator>::pointer pointer; typedef typename std::iterator_traits<Iterator>::reference reference;
protected: Iterator my_iterator; typedef value_type element_type;
public: ForwardIterator() = default; explicit ForwardIterator(Iterator i) : my_iterator(i) {} reference operator*() const { return *my_iterator; } Iterator operator->() const { return my_iterator; } ForwardIterator operator++() { ++my_iterator; return *this; } ForwardIterator operator++(int32_t) { auto retval = *this; my_iterator++; return retval; } friend bool operator==(const ForwardIterator& i, const ForwardIterator& j) { return i.my_iterator == j.my_iterator; } friend bool operator!=(const ForwardIterator& i, const ForwardIterator& j) { return i.my_iterator != j.my_iterator; }
Iterator iterator() const { return my_iterator; } };
template <typename Iterator, typename IteratorTag> class BidirectionalIterator : public ForwardIterator<Iterator, IteratorTag> { typedef ForwardIterator<Iterator, IteratorTag> base_type;
public: BidirectionalIterator() = default; explicit BidirectionalIterator(Iterator i) : base_type(i) {} BidirectionalIterator(const base_type& i) : base_type(i.iterator()) {}
BidirectionalIterator operator++() { ++base_type::my_iterator; return *this; } BidirectionalIterator operator--() { --base_type::my_iterator; return *this; } BidirectionalIterator operator++(int32_t) { auto retval = *this; base_type::my_iterator++; return retval; } BidirectionalIterator operator--(int32_t) { auto retval = *this; base_type::my_iterator--; return retval; } };
template <typename Iterator, typename F> void fill_data(Iterator first, Iterator last, F f) { typedef typename std::iterator_traits<Iterator>::value_type T; for (std::size_t i = 0; first != last; ++first, ++i) { *first = T(f(i)); } }
// Sequence<T> is a container of a sequence of T with lots of kinds of iterators. // Prefixes on begin/end mean: // c = "const" // f = "forward" // No prefix indicates non-const random-access iterator. template <typename T> class Sequence { std::vector<T> m_storage;
public: typedef typename std::vector<T>::iterator iterator; typedef typename std::vector<T>::const_iterator const_iterator; typedef ForwardIterator<iterator, std::forward_iterator_tag> forward_iterator; typedef ForwardIterator<const_iterator, std::forward_iterator_tag> const_forward_iterator;
typedef BidirectionalIterator<iterator, std::bidirectional_iterator_tag> bidirectional_iterator; typedef BidirectionalIterator<const_iterator, std::bidirectional_iterator_tag> const_bidirectional_iterator;
typedef T value_type; explicit Sequence(size_t size) : m_storage(size) {}
// Construct sequence [f(0), f(1), ... f(size-1)] // f can rely on its invocations being sequential from 0 to size-1. template <typename Func> Sequence(size_t size, Func f) { m_storage.reserve(size); // Use push_back because T might not have a default constructor for (size_t k = 0; k < size; ++k) m_storage.push_back(T(f(k))); } Sequence(const std::initializer_list<T>& data) : m_storage(data) {}
const_iterator begin() const { return m_storage.begin(); } const_iterator end() const { return m_storage.end(); } iterator begin() { return m_storage.begin(); } iterator end() { return m_storage.end(); } const_iterator cbegin() const { return m_storage.cbegin(); } const_iterator cend() const { return m_storage.cend(); } forward_iterator fbegin() { return forward_iterator(m_storage.begin()); } forward_iterator fend() { return forward_iterator(m_storage.end()); } const_forward_iterator cfbegin() const { return const_forward_iterator(m_storage.cbegin()); } const_forward_iterator cfend() const { return const_forward_iterator(m_storage.cend()); } const_forward_iterator fbegin() const { return const_forward_iterator(m_storage.cbegin()); } const_forward_iterator fend() const { return const_forward_iterator(m_storage.cend()); }
const_bidirectional_iterator cbibegin() const { return const_bidirectional_iterator(m_storage.cbegin()); } const_bidirectional_iterator cbiend() const { return const_bidirectional_iterator(m_storage.cend()); }
bidirectional_iterator bibegin() { return bidirectional_iterator(m_storage.begin()); } bidirectional_iterator biend() { return bidirectional_iterator(m_storage.end()); }
std::size_t size() const { return m_storage.size(); } const T* data() const { return m_storage.data(); } typename std::vector<T>::reference operator[](size_t j) { return m_storage[j]; } const T& operator[](size_t j) const { return m_storage[j]; }
// Fill with given value void fill(const T& value) { for (size_t i = 0; i < m_storage.size(); i++) m_storage[i] = value; }
void print() const;
template <typename Func> void fill(Func f) { fill_data(m_storage.begin(), m_storage.end(), f); } };
template <typename T> void Sequence<T>::print() const { std::cout << "size = " << size() << ": { "; std::copy(begin(), end(), std::ostream_iterator<T>(std::cout, " ")); std::cout << " } " << std::endl; }
// Predicates for algorithms template <typename DataType> struct is_equal_to { is_equal_to(const DataType& expected) : m_expected(expected) {} bool operator()(const DataType& actual) const { return actual == m_expected; }
private: DataType m_expected; };
// Low-quality hash function, returns value between 0 and (1<<bits)-1 // Warning: low-order bits are quite predictable. inline size_t HashBits(size_t i, size_t bits) { size_t mask = bits >= 8 * sizeof(size_t) ? ~size_t(0) : (size_t(1) << bits) - 1; return (424157 * i ^ 0x24aFa) & mask; }
// Stateful unary op template <typename T, typename U> class Complement { int32_t val;
public: Complement(T v) : val(v) {} U operator()(const T& x) const { return U(val - x); } };
// Tag used to prevent accidental use of converting constructor, even if use is explicit. struct OddTag { };
class Sum;
// Type with limited set of operations. Not default-constructible. // Only available operator is "==". // Typically used as value type in tests. class Number { int32_t value; friend class Add; friend class Sum; friend class IsMultiple; friend class Congruent; friend Sum operator+(const Sum& x, const Sum& y);
public: Number(int32_t val, OddTag) : value(val) {} friend bool operator==(const Number& x, const Number& y) { return x.value == y.value; } friend std::ostream& operator<<(std::ostream& o, const Number& d) { return o << d.value; } };
// Stateful predicate for Number. Not default-constructible. class IsMultiple { long modulus;
public: // True if x is multiple of modulus bool operator()(Number x) const { return x.value % modulus == 0; } IsMultiple(long modulus_, OddTag) : modulus(modulus_) {} };
// Stateful equivalence-class predicate for Number. Not default-constructible. class Congruent { long modulus;
public: // True if x and y have same remainder for the given modulus. // Note: this is not quite the same as "equivalent modulo modulus" when x and y have different // sign, but nonetheless AreCongruent is still an equivalence relationship, which is all // we need for testing. bool operator()(Number x, Number y) const { return x.value % modulus == y.value % modulus; } Congruent(long modulus_, OddTag) : modulus(modulus_) {} };
// Stateful reduction operation for Number class Add { long bias;
public: explicit Add(OddTag) : bias(1) {} Number operator()(Number x, const Number& y) { return Number(x.value + y.value + (bias - 1), OddTag()); } };
// Class similar to Number, but has default constructor and +. class Sum : public Number { public: Sum() : Number(0, OddTag()) {} Sum(long x, OddTag) : Number(x, OddTag()) {} friend Sum operator+(const Sum& x, const Sum& y) { return Sum(x.value + y.value, OddTag()); } };
// Type with limited set of operations, which includes an associative but not commutative operation. // Not default-constructible. // Typically used as value type in tests involving "GENERALIZED_NONCOMMUTATIVE_SUM". class MonoidElement { size_t a, b;
public: MonoidElement(size_t a_, size_t b_, OddTag) : a(a_), b(b_) {} friend bool operator==(const MonoidElement& x, const MonoidElement& y) { return x.a == y.a && x.b == y.b; } friend std::ostream& operator<<(std::ostream& o, const MonoidElement& x) { return o << "[" << x.a << ".." << x.b << ")"; } friend class AssocOp; };
// Stateful associative op for MonoidElement // It's not really a monoid since the operation is not allowed for any two elements. // But it's good enough for testing. class AssocOp { unsigned c;
public: explicit AssocOp(OddTag) : c(5) {} MonoidElement operator()(const MonoidElement& x, const MonoidElement& y) { unsigned d = 5; EXPECT_EQ(d, c, "state lost"); EXPECT_EQ(x.b, y.a, "commuted?");
return MonoidElement(x.a, y.b, OddTag()); } };
// Multiplication of matrix is an associative but not commutative operation // Typically used as value type in tests involving "GENERALIZED_NONCOMMUTATIVE_SUM". template <typename T> struct Matrix2x2 { T a[2][2]; Matrix2x2() : a{{1, 0}, {0, 1}} {} Matrix2x2(T x, T y) : a{{0, x}, {x, y}} {} #if !_PSTL_ICL_19_VC14_VC141_TEST_SCAN_RELEASE_BROKEN Matrix2x2(const Matrix2x2& m) : a{{m.a[0][0], m.a[0][1]}, {m.a[1][0], m.a[1][1]}} {} Matrix2x2& operator=(const Matrix2x2& m) { a[0][0] = m.a[0][0], a[0][1] = m.a[0][1], a[1][0] = m.a[1][0], a[1][1] = m.a[1][1]; return *this; } #endif };
template <typename T> bool operator==(const Matrix2x2<T>& left, const Matrix2x2<T>& right) { return left.a[0][0] == right.a[0][0] && left.a[0][1] == right.a[0][1] && left.a[1][0] == right.a[1][0] && left.a[1][1] == right.a[1][1]; }
template <typename T> Matrix2x2<T> multiply_matrix(const Matrix2x2<T>& left, const Matrix2x2<T>& right) { Matrix2x2<T> result; for (int32_t i = 0; i < 2; ++i) { for (int32_t j = 0; j < 2; ++j) { result.a[i][j] = left.a[i][0] * right.a[0][j] + left.a[i][1] * right.a[1][j]; } } return result; }
//============================================================================ // Adapters for creating different types of iterators. // // In this block we implemented some adapters for creating differnet types of iterators. // It's needed for extending the unit testing of Parallel STL algorithms. // We have adapters for iterators with different tags (forward_iterator_tag, bidirectional_iterator_tag), reverse iterators. // The input iterator should be const or non-const, non-reverse random access iterator. // Iterator creates in "MakeIterator": // firstly, iterator is "packed" by "IteratorTypeAdapter" (creating forward or bidirectional iterator) // then iterator is "packed" by "ReverseAdapter" (if it's possible) // So, from input iterator we may create, for example, reverse bidirectional iterator. // "Main" functor for testing iterators is named "invoke_on_all_iterator_types".
// Base adapter template <typename Iterator> struct BaseAdapter { typedef Iterator iterator_type; iterator_type operator()(Iterator it) { return it; } };
// Check if the iterator is reverse iterator // Note: it works only for iterators that created by std::reverse_iterator template <typename NotReverseIterator> struct isReverse : std::false_type { };
template <typename Iterator> struct isReverse<std::reverse_iterator<Iterator>> : std::true_type { };
// Reverse adapter template <typename Iterator, typename IsReverse> struct ReverseAdapter { typedef std::reverse_iterator<Iterator> iterator_type; iterator_type operator()(Iterator it) { #if _PSTL_CPP14_MAKE_REVERSE_ITERATOR_PRESENT return std::make_reverse_iterator(it); #else return iterator_type(it); #endif } };
// Non-reverse adapter template <typename Iterator> struct ReverseAdapter<Iterator, std::false_type> : BaseAdapter<Iterator> { };
// Iterator adapter by type (by default std::random_access_iterator_tag) template <typename Iterator, typename IteratorTag> struct IteratorTypeAdapter : BaseAdapter<Iterator> { };
// Iterator adapter for forward iterator template <typename Iterator> struct IteratorTypeAdapter<Iterator, std::forward_iterator_tag> { typedef ForwardIterator<Iterator, std::forward_iterator_tag> iterator_type; iterator_type operator()(Iterator it) { return iterator_type(it); } };
// Iterator adapter for bidirectional iterator template <typename Iterator> struct IteratorTypeAdapter<Iterator, std::bidirectional_iterator_tag> { typedef BidirectionalIterator<Iterator, std::bidirectional_iterator_tag> iterator_type; iterator_type operator()(Iterator it) { return iterator_type(it); } };
//For creating iterator with new type template <typename InputIterator, typename IteratorTag, typename IsReverse> struct MakeIterator { typedef IteratorTypeAdapter<InputIterator, IteratorTag> IterByType; typedef ReverseAdapter<typename IterByType::iterator_type, IsReverse> ReverseIter;
typename ReverseIter::iterator_type operator()(InputIterator it) { return ReverseIter()(IterByType()(it)); } };
// Useful constant variables constexpr std::size_t GuardSize = 5; constexpr std::ptrdiff_t sizeLimit = 1000;
template <typename Iter, typename Void = void> // local iterator_traits for non-iterators struct iterator_traits_ { };
template <typename Iter> // For iterators struct iterator_traits_<Iter, typename std::enable_if<!std::is_void<typename Iter::iterator_category>::value, void>::type> { typedef typename Iter::iterator_category iterator_category; };
template <typename T> // For pointers struct iterator_traits_<T*> { typedef std::random_access_iterator_tag iterator_category; };
// is iterator Iter has tag Tag template <typename Iter, typename Tag> using is_same_iterator_category = std::is_same<typename iterator_traits_<Iter>::iterator_category, Tag>;
// if we run with reverse or const iterators we shouldn't test the large range template <typename IsReverse, typename IsConst> struct invoke_if_ { template <typename Op, typename... Rest> void operator()(bool is_allow, Op op, Rest&&... rest) { if (is_allow) op(std::forward<Rest>(rest)...); } }; template <> struct invoke_if_<std::false_type, std::false_type> { template <typename Op, typename... Rest> void operator()(bool, Op op, Rest&&... rest) { op(std::forward<Rest>(rest)...); } };
// Base non_const_wrapper struct. It is used to distinguish non_const testcases // from a regular one. For non_const testcases only compilation is checked. struct non_const_wrapper { };
// Generic wrapper to specify iterator type to execute callable Op on. // The condition can be either positive(Op is executed only with IteratorTag) // or negative(Op is executed with every type of iterators except IteratorTag) template <typename Op, typename IteratorTag, bool IsPositiveCondition = true> struct non_const_wrapper_tagged : non_const_wrapper { template <typename Policy, typename Iterator> typename std::enable_if<IsPositiveCondition == is_same_iterator_category<Iterator, IteratorTag>::value, void>::type operator()(Policy&& exec, Iterator iter) { Op()(exec, iter); }
template <typename Policy, typename InputIterator, typename OutputIterator> typename std::enable_if<IsPositiveCondition == is_same_iterator_category<OutputIterator, IteratorTag>::value, void>::type operator()(Policy&& exec, InputIterator input_iter, OutputIterator out_iter) { Op()(exec, input_iter, out_iter); }
template <typename Policy, typename Iterator> typename std::enable_if<IsPositiveCondition != is_same_iterator_category<Iterator, IteratorTag>::value, void>::type operator()(Policy&&, Iterator) { }
template <typename Policy, typename InputIterator, typename OutputIterator> typename std::enable_if<IsPositiveCondition != is_same_iterator_category<OutputIterator, IteratorTag>::value, void>::type operator()(Policy&&, InputIterator, OutputIterator) { } };
// These run_for_* structures specify with which types of iterators callable object Op // should be executed. template <typename Op> struct run_for_rnd : non_const_wrapper_tagged<Op, std::random_access_iterator_tag> { };
template <typename Op> struct run_for_rnd_bi : non_const_wrapper_tagged<Op, std::forward_iterator_tag, false> { };
template <typename Op> struct run_for_rnd_fw : non_const_wrapper_tagged<Op, std::bidirectional_iterator_tag, false> { };
// Invoker for different types of iterators. template <typename IteratorTag, typename IsReverse> struct iterator_invoker { template <typename Iterator> using make_iterator = MakeIterator<Iterator, IteratorTag, IsReverse>; template <typename Iterator> using IsConst = typename std::is_const< typename std::remove_pointer<typename std::iterator_traits<Iterator>::pointer>::type>::type; template <typename Iterator> using invoke_if = invoke_if_<IsReverse, IsConst<Iterator>>;
// A single iterator version which is used for non_const testcases template <typename Policy, typename Op, typename Iterator> typename std::enable_if<is_same_iterator_category<Iterator, std::random_access_iterator_tag>::value && std::is_base_of<non_const_wrapper, Op>::value, void>::type operator()(Policy&& exec, Op op, Iterator iter) { op(std::forward<Policy>(exec), make_iterator<Iterator>()(iter)); }
// A version with 2 iterators which is used for non_const testcases template <typename Policy, typename Op, typename InputIterator, typename OutputIterator> typename std::enable_if<is_same_iterator_category<OutputIterator, std::random_access_iterator_tag>::value && std::is_base_of<non_const_wrapper, Op>::value, void>::type operator()(Policy&& exec, Op op, InputIterator input_iter, OutputIterator out_iter) { op(std::forward<Policy>(exec), make_iterator<InputIterator>()(input_iter), make_iterator<OutputIterator>()(out_iter)); }
template <typename Policy, typename Op, typename Iterator, typename Size, typename... Rest> typename std::enable_if<is_same_iterator_category<Iterator, std::random_access_iterator_tag>::value, void>::type operator()(Policy&& exec, Op op, Iterator begin, Size n, Rest&&... rest) { invoke_if<Iterator>()(n <= sizeLimit, op, exec, make_iterator<Iterator>()(begin), n, std::forward<Rest>(rest)...); }
template <typename Policy, typename Op, typename Iterator, typename... Rest> typename std::enable_if<is_same_iterator_category<Iterator, std::random_access_iterator_tag>::value && !std::is_base_of<non_const_wrapper, Op>::value, void>::type operator()(Policy&& exec, Op op, Iterator inputBegin, Iterator inputEnd, Rest&&... rest) { invoke_if<Iterator>()(std::distance(inputBegin, inputEnd) <= sizeLimit, op, exec, make_iterator<Iterator>()(inputBegin), make_iterator<Iterator>()(inputEnd), std::forward<Rest>(rest)...); }
template <typename Policy, typename Op, typename InputIterator, typename OutputIterator, typename... Rest> typename std::enable_if<is_same_iterator_category<OutputIterator, std::random_access_iterator_tag>::value, void>::type operator()(Policy&& exec, Op op, InputIterator inputBegin, InputIterator inputEnd, OutputIterator outputBegin, Rest&&... rest) { invoke_if<InputIterator>()(std::distance(inputBegin, inputEnd) <= sizeLimit, op, exec, make_iterator<InputIterator>()(inputBegin), make_iterator<InputIterator>()(inputEnd), make_iterator<OutputIterator>()(outputBegin), std::forward<Rest>(rest)...); }
template <typename Policy, typename Op, typename InputIterator, typename OutputIterator, typename... Rest> typename std::enable_if<is_same_iterator_category<OutputIterator, std::random_access_iterator_tag>::value, void>::type operator()(Policy&& exec, Op op, InputIterator inputBegin, InputIterator inputEnd, OutputIterator outputBegin, OutputIterator outputEnd, Rest&&... rest) { invoke_if<InputIterator>()(std::distance(inputBegin, inputEnd) <= sizeLimit, op, exec, make_iterator<InputIterator>()(inputBegin), make_iterator<InputIterator>()(inputEnd), make_iterator<OutputIterator>()(outputBegin), make_iterator<OutputIterator>()(outputEnd), std::forward<Rest>(rest)...); }
template <typename Policy, typename Op, typename InputIterator1, typename InputIterator2, typename OutputIterator, typename... Rest> typename std::enable_if<is_same_iterator_category<OutputIterator, std::random_access_iterator_tag>::value, void>::type operator()(Policy&& exec, Op op, InputIterator1 inputBegin1, InputIterator1 inputEnd1, InputIterator2 inputBegin2, InputIterator2 inputEnd2, OutputIterator outputBegin, OutputIterator outputEnd, Rest&&... rest) { invoke_if<InputIterator1>()( std::distance(inputBegin1, inputEnd1) <= sizeLimit, op, exec, make_iterator<InputIterator1>()(inputBegin1), make_iterator<InputIterator1>()(inputEnd1), make_iterator<InputIterator2>()(inputBegin2), make_iterator<InputIterator2>()(inputEnd2), make_iterator<OutputIterator>()(outputBegin), make_iterator<OutputIterator>()(outputEnd), std::forward<Rest>(rest)...); } };
// Invoker for reverse iterators only // Note: if we run with reverse iterators we shouldn't test the large range template <typename IteratorTag> struct iterator_invoker<IteratorTag, /* IsReverse = */ std::true_type> {
template <typename Iterator> using make_iterator = MakeIterator<Iterator, IteratorTag, std::true_type>;
// A single iterator version which is used for non_const testcases template <typename Policy, typename Op, typename Iterator> typename std::enable_if<is_same_iterator_category<Iterator, std::random_access_iterator_tag>::value && std::is_base_of<non_const_wrapper, Op>::value, void>::type operator()(Policy&& exec, Op op, Iterator iter) { op(std::forward<Policy>(exec), make_iterator<Iterator>()(iter)); }
// A version with 2 iterators which is used for non_const testcases template <typename Policy, typename Op, typename InputIterator, typename OutputIterator> typename std::enable_if<is_same_iterator_category<OutputIterator, std::random_access_iterator_tag>::value && std::is_base_of<non_const_wrapper, Op>::value, void>::type operator()(Policy&& exec, Op op, InputIterator input_iter, OutputIterator out_iter) { op(std::forward<Policy>(exec), make_iterator<InputIterator>()(input_iter), make_iterator<OutputIterator>()(out_iter)); }
template <typename Policy, typename Op, typename Iterator, typename Size, typename... Rest> typename std::enable_if<is_same_iterator_category<Iterator, std::random_access_iterator_tag>::value, void>::type operator()(Policy&& exec, Op op, Iterator begin, Size n, Rest&&... rest) { if (n <= sizeLimit) op(exec, make_iterator<Iterator>()(begin + n), n, std::forward<Rest>(rest)...); }
template <typename Policy, typename Op, typename Iterator, typename... Rest> typename std::enable_if<is_same_iterator_category<Iterator, std::random_access_iterator_tag>::value && !std::is_base_of<non_const_wrapper, Op>::value, void>::type operator()(Policy&& exec, Op op, Iterator inputBegin, Iterator inputEnd, Rest&&... rest) { if (std::distance(inputBegin, inputEnd) <= sizeLimit) op(exec, make_iterator<Iterator>()(inputEnd), make_iterator<Iterator>()(inputBegin), std::forward<Rest>(rest)...); }
template <typename Policy, typename Op, typename InputIterator, typename OutputIterator, typename... Rest> typename std::enable_if<is_same_iterator_category<OutputIterator, std::random_access_iterator_tag>::value, void>::type operator()(Policy&& exec, Op op, InputIterator inputBegin, InputIterator inputEnd, OutputIterator outputBegin, Rest&&... rest) { if (std::distance(inputBegin, inputEnd) <= sizeLimit) op(exec, make_iterator<InputIterator>()(inputEnd), make_iterator<InputIterator>()(inputBegin), make_iterator<OutputIterator>()(outputBegin + (inputEnd - inputBegin)), std::forward<Rest>(rest)...); }
template <typename Policy, typename Op, typename InputIterator, typename OutputIterator, typename... Rest> typename std::enable_if<is_same_iterator_category<OutputIterator, std::random_access_iterator_tag>::value, void>::type operator()(Policy&& exec, Op op, InputIterator inputBegin, InputIterator inputEnd, OutputIterator outputBegin, OutputIterator outputEnd, Rest&&... rest) { if (std::distance(inputBegin, inputEnd) <= sizeLimit) op(exec, make_iterator<InputIterator>()(inputEnd), make_iterator<InputIterator>()(inputBegin), make_iterator<OutputIterator>()(outputEnd), make_iterator<OutputIterator>()(outputBegin), std::forward<Rest>(rest)...); }
template <typename Policy, typename Op, typename InputIterator1, typename InputIterator2, typename OutputIterator, typename... Rest> typename std::enable_if<is_same_iterator_category<OutputIterator, std::random_access_iterator_tag>::value, void>::type operator()(Policy&& exec, Op op, InputIterator1 inputBegin1, InputIterator1 inputEnd1, InputIterator2 inputBegin2, InputIterator2 inputEnd2, OutputIterator outputBegin, OutputIterator outputEnd, Rest&&... rest) { if (std::distance(inputBegin1, inputEnd1) <= sizeLimit) op(exec, make_iterator<InputIterator1>()(inputEnd1), make_iterator<InputIterator1>()(inputBegin1), make_iterator<InputIterator2>()(inputEnd2), make_iterator<InputIterator2>()(inputBegin2), make_iterator<OutputIterator>()(outputEnd), make_iterator<OutputIterator>()(outputBegin), std::forward<Rest>(rest)...); } };
// We can't create reverse iterator from forward iterator template <> struct iterator_invoker<std::forward_iterator_tag, /*isReverse=*/std::true_type> { template <typename... Rest> void operator()(Rest&&...) { } };
template <typename IsReverse> struct reverse_invoker { template <typename... Rest> void operator()(Rest&&... rest) { // Random-access iterator iterator_invoker<std::random_access_iterator_tag, IsReverse>()(std::forward<Rest>(rest)...);
// Forward iterator iterator_invoker<std::forward_iterator_tag, IsReverse>()(std::forward<Rest>(rest)...);
// Bidirectional iterator iterator_invoker<std::bidirectional_iterator_tag, IsReverse>()(std::forward<Rest>(rest)...); } };
struct invoke_on_all_iterator_types { template <typename... Rest> void operator()(Rest&&... rest) { reverse_invoker</* IsReverse = */ std::false_type>()(std::forward<Rest>(rest)...); reverse_invoker</* IsReverse = */ std::true_type>()(std::forward<Rest>(rest)...); } }; //============================================================================
// Invoke op(policy,rest...) for each possible policy. template <typename Op, typename... T> void invoke_on_all_policies(Op op, T&&... rest) { using namespace __pstl::execution;
// Try static execution policies invoke_on_all_iterator_types()(seq, op, std::forward<T>(rest)...); invoke_on_all_iterator_types()(unseq, op, std::forward<T>(rest)...); invoke_on_all_iterator_types()(par, op, std::forward<T>(rest)...); invoke_on_all_iterator_types()(par_unseq, op, std::forward<T>(rest)...); }
template <typename F> struct NonConstAdapter { F my_f; NonConstAdapter(const F& f) : my_f(f) {}
template <typename... Types> auto operator()(Types&&... args) -> decltype(std::declval<F>(). operator()(std::forward<Types>(args)...)) { return my_f(std::forward<Types>(args)...); } };
template <typename F> NonConstAdapter<F> non_const(const F& f) { return NonConstAdapter<F>(f); }
// Wrapper for types. It's need for counting of constructing and destructing objects template <typename T> class Wrapper { public: Wrapper() { my_field = std::shared_ptr<T>(new T()); ++my_count; } Wrapper(const T& input) { my_field = std::shared_ptr<T>(new T(input)); ++my_count; } Wrapper(const Wrapper& input) { my_field = input.my_field; ++my_count; } Wrapper(Wrapper&& input) { my_field = input.my_field; input.my_field = nullptr; ++move_count; } Wrapper& operator=(const Wrapper& input) { my_field = input.my_field; return *this; } Wrapper& operator=(Wrapper&& input) { my_field = input.my_field; input.my_field = nullptr; ++move_count; return *this; } bool operator==(const Wrapper& input) const { return my_field == input.my_field; } bool operator<(const Wrapper& input) const { return *my_field < *input.my_field; } bool operator>(const Wrapper& input) const { return *my_field > *input.my_field; } friend std::ostream& operator<<(std::ostream& stream, const Wrapper& input) { return stream << *(input.my_field); } ~Wrapper() { --my_count; if (move_count > 0) { --move_count; } } T* get_my_field() const { return my_field.get(); }; static size_t Count() { return my_count; } static size_t MoveCount() { return move_count; } static void SetCount(const size_t& n) { my_count = n; } static void SetMoveCount(const size_t& n) { move_count = n; }
private: static std::atomic<size_t> my_count; static std::atomic<size_t> move_count; std::shared_ptr<T> my_field; };
template <typename T> std::atomic<size_t> Wrapper<T>::my_count = {0};
template <typename T> std::atomic<size_t> Wrapper<T>::move_count = {0};
template <typename InputIterator, typename T, typename BinaryOperation, typename UnaryOperation> T transform_reduce_serial(InputIterator first, InputIterator last, T init, BinaryOperation binary_op, UnaryOperation unary_op) noexcept { for (; first != last; ++first) { init = binary_op(init, unary_op(*first)); } return init; }
static const char* done() { #if _PSTL_TEST_SUCCESSFUL_KEYWORD return "done"; #else return "passed"; #endif }
// test_algo_basic_* functions are used to execute // f on a very basic sequence of elements of type T.
// Should be used with unary predicate template <typename T, typename F> static void test_algo_basic_single(F&& f) { size_t N = 10; Sequence<T> in(N, [](size_t v) -> T { return T(v); });
invoke_on_all_policies(f, in.begin()); }
// Should be used with binary predicate template <typename T, typename F> static void test_algo_basic_double(F&& f) { size_t N = 10; Sequence<T> in(N, [](size_t v) -> T { return T(v); }); Sequence<T> out(N, [](size_t v) -> T { return T(v); });
invoke_on_all_policies(f, in.begin(), out.begin()); }
template <typename Policy, typename F> static void invoke_if(Policy&&, F f) { #if _PSTL_ICC_16_VC14_TEST_SIMD_LAMBDA_DEBUG_32_BROKEN || _PSTL_ICC_17_VC141_TEST_SIMD_LAMBDA_DEBUG_32_BROKEN __pstl::__internal::invoke_if_not(__pstl::__internal::allow_unsequenced<Policy>(), f); #else f(); #endif }
} /* namespace TestUtils */
|