I'm trying to automatize calling a function multiple times using a combination of parameters. Something like:
int sum3(int a, int b, int c) {
return a b c;
}
std::vector<int> v = foo<sum3>({1, 2}, 0, {2, 3});
// Equivalent to:
// v.emplace_back(sum3(1, 0, 2));
// v.emplace_back(sum3(1, 0, 3));
// v.emplace_back(sum3(2, 0, 2));
// v.emplace_back(sum3(2, 0, 3));
Is something like this possible in C 20 using template metaprogramming?
Update I would be interested in nesting these expressions. For example:
int bar(int a) {
return a 1;
}
std::vector<int> v = foo<sum3>({1, 2}, foo<bar>({0, 1}), {2, 3});
// Equivalent to:
// std::vector<int> v = foo<sum3>({1, 2}, {1, 2}, {2, 3});
CodePudding user response:
This will much simpler with the help of range-v3
template<auto f, class... Ts>
auto foo(std::initializer_list<Ts>... il) {
return ranges::views::cartesian_product(il...)
| ranges::views::transform([](auto t) {
return std::apply([](auto... args) { return f(args...); }, t);
})
| ranges::to<std::vector>;
}
std::vector v = foo<sum3>({1, 2}, {0}, {2, 3});
where views::cartesian_product
has tier 1 priority in the C 23 ranges plan, and ranges::to
has been adopted by C 23.
CodePudding user response:
In order to accept brace-enclosed initilizer lists, values and other collections, like vectors, you'll need a helper class. Something like:
template<typename T>
struct span_or_init_list
{
span_or_init_list(std::initializer_list<T> list)
: span(list)
{}
span_or_init_list(std::span<const T> span)
: span(span)
{}
span_or_init_list(T const &val)
: span(&val, 1)
{}
template<typename Range>
span_or_init_list(Range const &v)
: span(v)
{}
std::span<const T> span;
};
Unfortunately the compiler cannot deduce span_or_init_list<T>
from something like {0, 1}
, so you need to do something metaprogramming to write foo
.
Since partial function template specialization is limited, a constexpr
variable could be used instead:
template<auto func, typename FuncT = decltype(func)>
inline constexpr int foo;
template<auto func, typename Ret, typename ...Args>
std::vector<Ret> foo_impl(span_or_init_list<std::decay_t<Args>> ...list)
{
// do work here
}
template<auto func, typename Ret, typename ...Args>
inline constexpr auto foo<func, Ret(*)(Args...)> = foo_impl<func, Ret, Args...>;
For the implementation of foo_impl
you could for example use @康桓瑋's solution from their answer.
So in the end these calls work:
std::vector<int> v1 = foo<sum3>({1, 2}, 0, {2, 3});
std::vector<int> v2 = foo<sum3>({1, 2}, foo<abs>({0, 1}), {2, 3});