Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

making a std::tuple from the result of a pack

Suppose I’m writing a function called ‘invoke_all’, which applies all the lambdas it receives (as template parameters) to the arguments, it looks like:

template <auto... fs, typename... Args>
constexpr auto invoke_all(Args &&...args) {
  (std::invoke(fs, std::forward<Args>(args)...), ...);
}

so far so good. call it like:

invoke_all<
      [](auto i, auto j) { std::println("lambda1: {} {}", i, j); },
      [](auto i, auto j) { std::println("lambda2: {} {}", i, j); }
      >
      (42, "abc"s);

but suppose I actually want those lambdas to return values, and pack all the return values in a std::tuple, the problem arises. This doesn’t compile:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

#include <functional>
#include <tuple>
#include <format>
#include <string>
#include <cassert>

template <auto... fs, typename... Args>
constexpr auto invoke_all(Args &&...args) {
  return std::tuple{(std::invoke(fs, std::forward<Args>(args)...), ...)};
}

using namespace std::literals;

int main() {
    auto results = invoke_all< //
        [](auto i, auto j) { return std::format("lambda1: {} {}", i, j); },
        [](auto i, auto j) { return std::format("lambda2: {} {}", i, j); }> //
    (42, "abc"s);

    assert((results == std::tuple{"lambda1: 42 abc"s, "lambda2: 42 abc"s}));
}

because the tuple that ‘invoke_all’ returns, is a tuple of size 1, not 2.

and this doesn’t compile either:

#include <functional>
#include <tuple>
#include <format>
#include <string>
#include <cassert>

template <auto... fs, typename... Args>
constexpr auto invoke_all(Args &&...args) {
  return std::tuple{std::invoke(fs, std::forward<Args>(args)...), ...};
}

using namespace std::literals;

int main() {
    auto results = invoke_all< //
        [](auto i, auto j) { return std::format("lambda1: {} {}", i, j); },
        [](auto i, auto j) { return std::format("lambda2: {} {}", i, j); }> //
    (42, "abc"s);

    assert((results == std::tuple{"lambda1: 42 abc"s, "lambda2: 42 abc"s}));
}

because the missing () around the pack expansion (I think).

So is there a way out of this, i.e., making a tuple from the result pack expansion?

>Solution :

You almost had it with your last attempt. The thing you need to remeber is that ... means expand into a comma separated list so when you do

template <auto... fs, typename... Args>
constexpr auto invoke_all(Args &&...args) {
  return std::tuple{std::invoke(fs, std::forward<Args>(args)...), ...};
}

You are trying to do a comma expression but it is missing the outer () required to do a fold expression. Instead what you want is

template <auto... fs, typename... Args>
constexpr auto invoke_all(Args &&...args) {
  return std::tuple{std::invoke(fs, std::forward<Args>(args)...)...};
}

which doesn’t have a ,. This expands out to

template <auto... fs, typename... Args>
constexpr auto invoke_all(Args &&...args) {
  return std::tuple{std::invoke(fs1, fwd_arg1, fwd_arg2, ..., fwd_argN), 
                    std::invoke(fs2, fwd_arg1, fwd_arg2, ..., fwd_argN)
                    ...,
                    std::invoke(fsN, fwd_arg1, fwd_arg2, ..., fwd_argN);
}

One additional thing to note with your test case, the result will not be a

std::tuple{"lambda1: 42 abc"s, "lambda2: 42 abc"s}

but is instead

std::tuple{"lambda1: 42 abc"s, "lambda2: 42"s}

This is because "abc"s is a temporary so it gets moved into the first lambda and then you are left with an empty string for the rest of the lambdas. Ideally you should not forward anything and just pass by value so everything is treated as an lvalue like

template <auto... fs, typename... Args>
constexpr auto invoke_all(Args &&...args) {
  return std::tuple{std::invoke(fs, args...)...};
}
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading