Benjamin Buch
Benjamin Buch

Reputation: 6133

How can I call ExecutionPolicy algorithms in a constexpr context?

I want to call standard library algorithms with the ExecutionPolicy for vectorization. At the same time the call should also work in a constexpr context. Unfortunately the ExecutionPolicy overloads of the algorithms from the standard library are currently not constexpr. At compile time, of course, it doesn't matter if vectorization is used. So in this case I just want to call the overload without ExecutionPolicy.

This I have implemented as:

#include <execution>
#include <functional>
#include <type_traits>

template <typename Fn, typename ... T>
constexpr decltype(auto) unseq_invoke(Fn&& fn, T&& ... v) {
    if (std::is_constant_evaluated()) {
        return std::invoke(
            std::forward<Fn>(fn), std::forward<T>(v) ...);
    } else {
        return std::invoke(
            std::forward<Fn>(fn), std::execution::unseq, std::forward<T>(v) ...);
    }
}

Basically, this also works exactly as it should:

#include <iostream>
#include <string_view>

constexpr bool is_valid_c_str(std::string_view const view) {
    constexpr auto find_wrapper =
        []<typename ... T>(T&& ... v) {
            return std::find(std::forward<T>(v) ...);
        };

    return unseq_invoke(find_wrapper, view.begin(), view.end(), 0) == view.end();
}

int main() {
    using namespace std::literals;
    static constexpr auto a = "valid c_str"sv;
    static constexpr auto b = "invalid\0c_str"sv;

    std::cout << std::boolalpha
        << a << ": " << is_valid_c_str(a) << '\n'
        << b << ": " << is_valid_c_str(b) << '\n';

    static_assert(is_valid_c_str(a));
    static_assert(!is_valid_c_str(b));
}
valid c_str: true
invalidc_str: false

The problem is that I always have to wrap functions like std::find in a lambda. If I try to pass it directly as an argument, I get a <unresolved overloaded function type> error.

return unseq_invoke(std::find, view.begin(), view.end(), 0) == view.end();
main.cpp: In function ‘constexpr bool is_valid_c_str(std::string_view)’:
main.cpp:21:24: error: no matching function for call to ‘unseq_invoke(<unresolved overloaded function type>, std::basic_string_view<char>::const_iterator, std::basic_string_view<char>::const_iterator, int)’
   21 |     return unseq_invoke(std::find, view.begin(), view.end(), 0) == view.end();
      |            ~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
main.cpp:6:26: note: candidate: ‘template<class Fn, class ... T> constexpr decltype(auto) unseq_invoke(Fn&&, T&& ...)’
    6 | constexpr decltype(auto) unseq_invoke(Fn&& fn, T&& ... v) {
      |                          ^~~~~~~~~~~~
main.cpp:6:26: note:   template argument deduction/substitution failed:
main.cpp:21:24: note:   couldn’t deduce template parameter ‘Fn’
   21 |     return unseq_invoke(std::find, view.begin(), view.end(), 0) == view.end();
      |            ~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Of course, I could shorten that with a macro, but it would still be an explicit wrapper, and I don't like to use macros either.

Is there a way to make unseq_invoke(std::algorithm, ...) work without a wrapper around std::algorithm? Of course, an alternative approach to mine would also help me.

Upvotes: 1

Views: 143

Answers (0)

Related Questions