Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

2022/tuple-iteration-apply/ #75

Open
utterances-bot opened this issue Feb 14, 2022 · 11 comments
Open

2022/tuple-iteration-apply/ #75

utterances-bot opened this issue Feb 14, 2022 · 11 comments

Comments

@utterances-bot
Copy link

C++ Templates: How to Iterate through std::tuple: std::apply and More - C++ Stories

In the previous article on the tuple iteration, we covered the basics. As a result, we implemented a function template that took a tuple and could nicely print it to the output. There was also a version with operator <<.
Today we can go further and see some other techniques. The first one is with std::apply from C++17, a helper function for tuples.

https://www.cppstories.com/2022/tuple-iteration-apply/

Copy link

Hi Bartlomiej. I was doing some iteration over tuple like last week too.
You can check it on https://github.com/iPMSoftware/CppFunAndLearn/blob/master/template_recursive_iteration_over_tuple/main.cpp

Copy link

This HelperCallable looks familiar, it looks like generic lambda,
So I'd just use lambda in such case:

auto printImpl = [](const auto&... tupleArgs) {
    size_t index = 0;
    auto printElem = [&index](const auto& x) {
        if (index++ > 0) 
            std::cout << ", ";
        std::cout << x;
        };

    (printElem(tupleArgs), ...);
};

But actually this is not a real problem -- since you have types (Args...) for printImpl to instantiate:

template <typename... Args>
void printTupleApplyFn(const std::tuple<Args...>& tp) {
    std::cout << "(";
    std::apply(printImpl<Args...>, tp); // (!)
    std::cout << ")";
}

@fenbf
Copy link
Owner

fenbf commented Feb 14, 2022

Thanks @iPMSoftware, I'll see your implementation. Do you have some different techniques used there?

Thanks @PiotrNycz - unfortunately even with Args... it doesn't compile: std::apply(printImpl<Args...>, tp); see here https://godbolt.org/z/YWEn3YEMq

Copy link

@fenbf It compiles - you have just trivial error in functionname - see https://godbolt.org/z/WTscsnvnz
((printTupleApplyFn, not printTupleApply)) - missing Fn suffix

@fenbf
Copy link
Owner

fenbf commented Feb 14, 2022

@PiotrNycz ah, eh, wow! yes, that's the bug :) Thanks for pointing this out, I'll update the article

Copy link

It is always nice to help.

As a bonus, one idea how to print nicely all of these tuple elementa w/o extra additional lambda/function nor index:

template <typename TupleT>
void printTupleApply(const TupleT& tp) {
    std::cout << "(";
    if constexpr (std::tuple_size_v<TupleT> >= 0u)
        std::apply([](const auto& a0, const auto&... a1) {
                std::cout << a0;
                ((std::cout << ", " << a1), ...);
            }, tp);
    std::cout << ")";
}



@fenbf
Copy link
Owner

fenbf commented Feb 15, 2022

Thanks @PiotrNycz I've just updated the article with a new section with that code.

Copy link

Everybody makes mistakes, including me ;) It should be > 0 not >= 0 in my if constexpr condition. But I noticed you skipped that part, a wise move - the more focused article, the better. BTW, nice book "C++17 in detail" - I have my own copy.

@fenbf
Copy link
Owner

fenbf commented Feb 15, 2022

@PiotrNycz heh :) Having an empty tuple is probably very rare, maybe it could be even solved by having some special function template specialization to report a warning in that case... or ignore it. It's probably only needed for generic code in some libraries.
thanks for a good opinion about the book.

Copy link

Here is a quite simple application, which mimics a stream of heterogeneous data and still works with the compatible sub-content. It uses what has been discussed in this post and if constexpr:

std::tuple tp{10, 20, 3.14, "blabla", 42};
auto mult = [](auto&& x) {
    if constexpr (std::is_arithmetic_v<std::remove_cvref_t<decltype(x)>>) {
      x *= 2;
    }
};
printTuple(tp);
for_each_tuple(tp, mult);
printTuple(tp);

I tried to do this with concepts, but couldn't manage as it reports couldn't deduce template parameter 'Fn' on multiply function. See the snippet:

emplate <class T, class U>
concept Multipliable = requires(T a, U b) {
  a*b;
};

template <class T, class U>
concept NotMultipliable = !Multipliable<T,U>;

template <class T> requires Multipliable<T, int>
void multiply(T&& x) {
  x *= 2;
}

template <class T> requires NotMultipliable<T,int>
void multiply(T&& x) {}

...
for_each_tuple(tp, multiply);

Any ideas on how to accomplish this with concepts?

@PiotrNycz
Copy link

@tahsinkose In C++ a function template is not a thing you can pass as argument to other functions. You can pass function template instantiation (as well as objects, variables other functions (pointers to functions). Here -- you need to wrap this function template with lambda:

for_each_tuple(tp, [] (auto&& ...args) { 
         multiply(std::forward<decltype(args)>(args)...); } );

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants