diff --git a/include/poolstl/seq_fwd.hpp b/include/poolstl/seq_fwd.hpp new file mode 100644 index 0000000..ba71580 --- /dev/null +++ b/include/poolstl/seq_fwd.hpp @@ -0,0 +1,119 @@ +// Copyright (C) 2023 Adam Lugowski. All rights reserved. +// Use of this source code is governed by: +// the BSD 2-clause license, the MIT license, or at your choosing the BSL-1.0 license found in the LICENSE.*.txt files. +// SPDX-License-Identifier: BSD-2-Clause OR MIT OR BSL-1.0 + +#ifndef POOLSTL_VARIANT_POLICY_HPP +#define POOLSTL_VARIANT_POLICY_HPP + +#include +#include + +/* INDEPENDENT FILE. DOES NOT REQUIRE REST OF POOLSTL. + * + * An execution policy that simply forwards to the basic non-policy (sequential) standard library functions. + * + * Allows writing code in the parallel style even if tooling does not support doing so. + * + * The sequential policy will be poolstl::seq_fwd. + * You may override by defining POOLSTL_SEQ_FWD_POLICY to be the policy you'd prefer instead. + */ + + +#ifndef POOLSTL_SEQ_FWD_POLICY +namespace poolstl { + namespace execution { + struct seq_fwd_policy { + }; + + constexpr seq_fwd_policy seq_fwd; + } + using execution::seq_fwd; +} +#define POOLSTL_SEQ_FWD_POLICY poolstl::execution::seq_fwd_policy +#endif + +#if (__cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L)) && \ + (!defined(_GLIBCXX_RELEASE) || _GLIBCXX_RELEASE >= 9) +#define POOLSTL_SEQ_FWD_HAVE_CXX17_LIB 1 +#else +#define POOLSTL_SEQ_FWD_HAVE_CXX17_LIB 0 +#endif + +namespace poolstl { + namespace internal { + /** + * To enable/disable seq_fwd overload resolution + */ + template + using enable_if_seq_fwd = + typename std::enable_if< + std::is_same::type>::type>::value, + Tp>::type; + } +} + +/* + * Forward a specified policy to the native sequential (no policy) method. + */ + +#define POOLSTL_DEFINE_SEQ_FWD(NS, FNAME) \ + template \ + auto FNAME(EP&&, ARGS&&...args) -> \ + poolstl::internal::enable_if_seq_fwd(args)...))> { \ + return NS::FNAME(std::forward(args)...); \ + } + +#define POOLSTL_DEFINE_SEQ_FWD_VOID(NS, FNAME) \ + template \ + poolstl::internal::enable_if_seq_fwd FNAME(EP&&, ARGS&&... args) { \ + NS::FNAME(std::forward(args)...); \ + } + + +namespace std { + // + + POOLSTL_DEFINE_SEQ_FWD(std, all_of) + POOLSTL_DEFINE_SEQ_FWD(std, any_of) + POOLSTL_DEFINE_SEQ_FWD(std, none_of) + + POOLSTL_DEFINE_SEQ_FWD(std, count) + POOLSTL_DEFINE_SEQ_FWD(std, count_if) + + POOLSTL_DEFINE_SEQ_FWD(std, copy) + POOLSTL_DEFINE_SEQ_FWD(std, copy_n) + + POOLSTL_DEFINE_SEQ_FWD_VOID(std, fill) + POOLSTL_DEFINE_SEQ_FWD(std, fill_n) + + POOLSTL_DEFINE_SEQ_FWD(std, find) + POOLSTL_DEFINE_SEQ_FWD(std, find_if) + POOLSTL_DEFINE_SEQ_FWD(std, find_if_not) + + POOLSTL_DEFINE_SEQ_FWD_VOID(std, for_each) +#if POOLSTL_SEQ_FWD_HAVE_CXX17_LIB + POOLSTL_DEFINE_SEQ_FWD(std, for_each_n) +#endif + + POOLSTL_DEFINE_SEQ_FWD(std, transform) + + // + +#if POOLSTL_SEQ_FWD_HAVE_CXX17_LIB + POOLSTL_DEFINE_SEQ_FWD(std, exclusive_scan) + POOLSTL_DEFINE_SEQ_FWD(std, reduce) + POOLSTL_DEFINE_SEQ_FWD(std, transform_reduce) +#endif +} + +#ifdef POOLSTL_VERSION_MAJOR +namespace poolstl { + // + + POOLSTL_DEFINE_SEQ_FWD_VOID(poolstl, for_each_chunk) +} +#endif + +#endif diff --git a/include/poolstl/variant_policy.hpp b/include/poolstl/variant_policy.hpp index bdd2e10..15354cf 100644 --- a/include/poolstl/variant_policy.hpp +++ b/include/poolstl/variant_policy.hpp @@ -6,10 +6,17 @@ #ifndef POOLSTL_VARIANT_POLICY_HPP #define POOLSTL_VARIANT_POLICY_HPP -#include "execution" - +#include +#include #include +#if (__cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L)) && \ + (!defined(_GLIBCXX_RELEASE) || _GLIBCXX_RELEASE >= 9) +#define POOLSTL_VARIANT_HAVE_CXX17_LIB 1 +#else +#define POOLSTL_VARIANT_HAVE_CXX17_LIB 0 +#endif + namespace poolstl { namespace execution { /** @@ -24,9 +31,12 @@ namespace poolstl { }; namespace internal { + // Example variant definition + /* using poolstl_policy_variant = std::variant< poolstl::execution::parallel_policy, poolstl::execution::sequenced_policy>; + */ } } @@ -52,25 +62,6 @@ namespace poolstl { } } -/* - * Forward poolstl::seq to the native sequential (no policy) method. - */ - -#define POOLSTL_DEFINE_SEQ_FWD(NS, FNAME) \ - template \ - auto FNAME(EP&&, ARGS&&...args) -> \ - poolstl::internal::enable_if_seq(args)...))> { \ - return NS::FNAME(std::forward(args)...); \ - } - -#define POOLSTL_DEFINE_SEQ_FWD_VOID(NS, FNAME) \ - template \ - poolstl::internal::enable_if_seq FNAME(EP&&, ARGS&&... args) { \ - NS::FNAME(std::forward(args)...); \ - } - -#if POOLSTL_HAVE_CXX17 - /* * Dynamically choose policy from a std::variant. * Useful to choose between parallel and sequential policies at runtime via par_if. @@ -89,55 +80,48 @@ namespace poolstl { return std::visit([&](auto&& pol) { return NS::FNAME(pol, std::forward(args)...); }, policy.var); \ } -#else -#define POOLSTL_DEFINE_PAR_IF_FWD_VOID(NS, FNAME) -#define POOLSTL_DEFINE_PAR_IF_FWD(NS, FNAME) -#endif -/* - * Define both the sequential forward and dynamic chooser. - */ -#define POOLSTL_DEFINE_BOTH_SEQ_FWD_AND_PAR_IF(NS, FNAME) \ - POOLSTL_DEFINE_SEQ_FWD(NS, FNAME) \ - POOLSTL_DEFINE_PAR_IF_FWD(NS, FNAME) - -#define POOLSTL_DEFINE_BOTH_SEQ_FWD_AND_PAR_IF_VOID(NS, FNAME) \ - POOLSTL_DEFINE_SEQ_FWD_VOID(NS, FNAME) \ - POOLSTL_DEFINE_PAR_IF_FWD_VOID(NS, FNAME) - namespace std { - POOLSTL_DEFINE_BOTH_SEQ_FWD_AND_PAR_IF(std, all_of) - POOLSTL_DEFINE_BOTH_SEQ_FWD_AND_PAR_IF(std, any_of) - POOLSTL_DEFINE_BOTH_SEQ_FWD_AND_PAR_IF(std, none_of) + // + + POOLSTL_DEFINE_PAR_IF_FWD(std, all_of) + POOLSTL_DEFINE_PAR_IF_FWD(std, any_of) + POOLSTL_DEFINE_PAR_IF_FWD(std, none_of) - POOLSTL_DEFINE_BOTH_SEQ_FWD_AND_PAR_IF(std, count) - POOLSTL_DEFINE_BOTH_SEQ_FWD_AND_PAR_IF(std, count_if) + POOLSTL_DEFINE_PAR_IF_FWD(std, count) + POOLSTL_DEFINE_PAR_IF_FWD(std, count_if) - POOLSTL_DEFINE_BOTH_SEQ_FWD_AND_PAR_IF(std, copy) - POOLSTL_DEFINE_BOTH_SEQ_FWD_AND_PAR_IF(std, copy_n) + POOLSTL_DEFINE_PAR_IF_FWD(std, copy) + POOLSTL_DEFINE_PAR_IF_FWD(std, copy_n) - POOLSTL_DEFINE_BOTH_SEQ_FWD_AND_PAR_IF_VOID(std, fill) - POOLSTL_DEFINE_BOTH_SEQ_FWD_AND_PAR_IF(std, fill_n) + POOLSTL_DEFINE_PAR_IF_FWD_VOID(std, fill) + POOLSTL_DEFINE_PAR_IF_FWD(std, fill_n) - POOLSTL_DEFINE_BOTH_SEQ_FWD_AND_PAR_IF(std, find) - POOLSTL_DEFINE_BOTH_SEQ_FWD_AND_PAR_IF(std, find_if) - POOLSTL_DEFINE_BOTH_SEQ_FWD_AND_PAR_IF(std, find_if_not) + POOLSTL_DEFINE_PAR_IF_FWD(std, find) + POOLSTL_DEFINE_PAR_IF_FWD(std, find_if) + POOLSTL_DEFINE_PAR_IF_FWD(std, find_if_not) - POOLSTL_DEFINE_BOTH_SEQ_FWD_AND_PAR_IF_VOID(std, for_each) -#if POOLSTL_HAVE_CXX17_LIB - POOLSTL_DEFINE_BOTH_SEQ_FWD_AND_PAR_IF(std, for_each_n) + POOLSTL_DEFINE_PAR_IF_FWD_VOID(std, for_each) +#if POOLSTL_VARIANT_HAVE_CXX17_LIB + POOLSTL_DEFINE_PAR_IF_FWD(std, for_each_n) #endif - POOLSTL_DEFINE_BOTH_SEQ_FWD_AND_PAR_IF(std, transform) + POOLSTL_DEFINE_PAR_IF_FWD(std, transform) + + // -#if POOLSTL_HAVE_CXX17_LIB - POOLSTL_DEFINE_BOTH_SEQ_FWD_AND_PAR_IF(std, exclusive_scan) - POOLSTL_DEFINE_BOTH_SEQ_FWD_AND_PAR_IF(std, reduce) - POOLSTL_DEFINE_BOTH_SEQ_FWD_AND_PAR_IF(std, transform_reduce) +#if POOLSTL_VARIANT_HAVE_CXX17_LIB + POOLSTL_DEFINE_PAR_IF_FWD(std, exclusive_scan) + POOLSTL_DEFINE_PAR_IF_FWD(std, reduce) + POOLSTL_DEFINE_PAR_IF_FWD(std, transform_reduce) #endif } +#ifdef POOLSTL_VERSION_MAJOR namespace poolstl { - POOLSTL_DEFINE_BOTH_SEQ_FWD_AND_PAR_IF_VOID(poolstl, for_each_chunk) + // + + POOLSTL_DEFINE_PAR_IF_FWD_VOID(poolstl, for_each_chunk) } +#endif #endif diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index fd9d6ad..3b589d0 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -71,6 +71,14 @@ add_executable(cpp11_test cpp11_test.cpp) target_link_libraries(cpp11_test PUBLIC poolSTL::poolSTL) target_compile_features(cpp11_test PUBLIC cxx_std_11) +# Test seq_fwd policy +add_executable(seq_fwd_test seq_fwd_test.cpp) +target_link_libraries(seq_fwd_test PRIVATE Catch2::Catch2WithMain poolSTL::poolSTL) +target_compile_definitions(seq_fwd_test PRIVATE CATCH_CONFIG_FAST_COMPILE) +target_compile_features(seq_fwd_test PUBLIC cxx_std_17) +catch_discover_tests(seq_fwd_test) + + # Test std::execution supplementation. # The test code uses only std::execution::par, seq, and par_unseq. # On compilers with support poolSTL does nothing, else poolSTL aliases poolstl::par to the std::execution policies. diff --git a/tests/seq_fwd_test.cpp b/tests/seq_fwd_test.cpp new file mode 100644 index 0000000..3aaf825 --- /dev/null +++ b/tests/seq_fwd_test.cpp @@ -0,0 +1,62 @@ +// Copyright (C) 2023 Adam Lugowski. All rights reserved. +// Use of this source code is governed by: +// the BSD 2-clause license, the MIT license, or at your choosing the BSL-1.0 license found in the LICENSE.*.txt files. +// SPDX-License-Identifier: BSD-2-Clause OR MIT OR BSL-1.0 + + +#include +#include + +#include + +#if __has_include() +// seq_fwd not included in the poolSTL amalgam, so skip these tests on CI steps that test the amalgam. + +#include + +#include "utils.hpp" + + +TEST_CASE("fwd_count", "[alg][algorithm][seq_fwd]") { + for (auto vec_size : test_arr_sizes) { + auto haystack = iota_vector(vec_size); + + { + int needle = 5; + auto seq = std::count( haystack.cbegin(), haystack.cend(), needle); + auto par = std::count(poolstl::seq_fwd, haystack.cbegin(), haystack.cend(), needle); + REQUIRE(seq == par); + } + { + auto pred = [&](auto x) { return x % 2 == 0; }; + auto seq = std::count_if( haystack.cbegin(), haystack.cend(), pred); + auto par = std::count_if(poolstl::seq_fwd, haystack.cbegin(), haystack.cend(), pred); + REQUIRE(seq == par); + } + } +} + +TEST_CASE("fwd_for_each", "[alg][algorithm][seq_fwd]") { + std::atomic sum{0}; + for (auto num_iters : test_arr_sizes) { + auto v = iota_vector(num_iters); + + for (auto which_impl : {0, 1}) { + sum = 0; + auto f = [&](auto) { ++sum; }; + switch (which_impl) { + case 0: + std::for_each(v.cbegin(), v.cend(), f); + break; + case 1: + std::for_each(poolstl::seq_fwd, v.cbegin(), v.cend(), f); + break; + default: break; + } + REQUIRE(sum == num_iters); + } + } +} + +std::mt19937 rng{1}; +#endif