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.
BOOST_HOF_LIFTwould use thatunseq_invoke([](auto&& ...x){return std::find(std::forward<decltype(x)>(x)...);}, ...);but that's still a lot. The macro that @Caleth points, essentially expands to similar code.forwardunqualified is not a good idea. It will do ADL and is likely to prefer an overload found that way, which is definitively not what you want here. You always want to callstd::forward. Same forinvoke.