Home > Net >  Generate function parameters from a combination of initializer lists
Generate function parameters from a combination of initializer lists

Time:04-21

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.

Demo

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});

See full example on godbolt.org

  • Related