Reputation: 6133
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