std::visit for std::tuple

1,469 views
Skip to first unread message

Roman Orlov

unread,
Mar 6, 2017, 8:48:33 AM3/6/17
to ISO C++ Standard - Future Proposals
Hi there,

Sometimes we need to iterate std::tuple instance and there is no standard solution for it.
For myself I solved it like this

template<typename T, typename V, size_t... I>
void visit_impl(T&& t, V&& v, std::index_sequence<I...>)
{
   
(..., v(std::get<I>(t)));
}


template<typename T, typename V>
void visit(T&& t, V&& v)
{
    visit_impl
(std::forward<T>(t), std::forward<V>(v),
        std
::make_index_sequence<std::tuple_size<
           
typename std::decay<T>::type>::value>());
}


auto t = std::make_tuple(100, "500", '!');
visit
(t, [](auto& x) { std::cout << x; });

So I suggest to add 'visit' functions to the <tuple> library. Just like for std::varinat.

Barry Revzin

unread,
Mar 6, 2017, 10:24:07 AM3/6/17
to ISO C++ Standard - Future Proposals
What you want isn't really visiting, it's more of a loop. There was a proposal for exactly that (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0589r0.pdf) but the response was that this would be better handled through reflection tools. Since basically you want to loop over all the members of the tuple - so if we had a good mechanism for reflection, this just falls out of that. 

Vicente J. Botet Escriba

unread,
Mar 6, 2017, 1:55:45 PM3/6/17
to std-pr...@isocpp.org
I wrote a proposal that match a variadic number of sum (variant-like) types.

    http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0050r0.pdf

I would need to adapt it to the current std::variant interface.

When we consider any type as a sum type with a single alternative, the variadic match function works for pack types and could as well work for tuples.

At the same time there were other related proposals (for variant) and even if there were some interest, we need a common proposal.

    http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0080r0.pdf



Vicente

Vicente J. Botet Escriba

unread,
Mar 6, 2017, 3:24:09 PM3/6/17
to std-pr...@isocpp.org
Well, reflection will give you the means but will not give you visitation, nor loop. We need these kind of proposal on top of reflection.

In order to be able to do that for any product-type, as we are able to extract all the elements using structure binding, we need a common interface: to iterate, either to visit or either to access each one of the members directly (tuple-like access). All the interfaces are equivalent, that is, we can implement each function using the others.

In http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0327r1.pdf I propose a tuple-like access that takes in account the same types that support structure binding. Boost.Hana uses Sequence (iteration, fold and make) as basic functions.

The question then is what is the complexity (performance) of all these operations when implemented using the others.
The tuple-like access (direct access and the size) is what the compiler knows already for a tuple-like type, as std::tuple, or a struct.
I believe that reflection should provide a tuple-like interface for those kind of types. Then you wan implement visit for all of them.

I believe the function you are proposing is useful, as the implementation is simple. p0327r1 names it for_each.

I like P0589, it makes it possible to do simple things simpler. People know better how to iterate.
However I prefer in general the functional approach. No raw loops.

Vicente
 

Nicol Bolas

unread,
Mar 6, 2017, 3:54:26 PM3/6/17
to ISO C++ Standard - Future Proposals
It seems to me that the generalized tuple unpacking proposal makes this all obsolete:

auto t = std::make_tuple(100, "500", '!');
[](auto& x) { std::cout << x; }([:]t)...;

Roman Orlov

unread,
Mar 8, 2017, 6:04:26 AM3/8/17
to ISO C++ Standard - Future Proposals
Thanks for feedback, P0589 is a good solution. But besides raw loops there should be 'visit' functions (implemented via tuple-based for loop) to use them in higher-order functions.

понедельник, 6 марта 2017 г., 18:24:07 UTC+3 пользователь Barry Revzin написал:

Roman Orlov

unread,
Mar 8, 2017, 6:16:42 AM3/8/17
to ISO C++ Standard - Future Proposals
I agree with you. I think there should be a choice to use raw loops or to use functional approach. Such functions can be implemented via raw loops (P0589). But if there are only raw loops we have to manually implement 'visit' functions to use them in higher-order functions. I'd like to have these functions in the standard library.

понедельник, 6 марта 2017 г., 23:24:09 UTC+3 пользователь Vicente J. Botet Escriba написал:

Arthur O'Dwyer

unread,
Mar 8, 2017, 7:36:38 PM3/8/17
to ISO C++ Standard - Future Proposals
It strikes me that what you're asking for is not tuple_visit, but rather tuple_map (in the functional-programming sense): I have a tuple<A,B,C>, and I have a functor X that signifies a mapping from A to D, from B to E, from C to F; and I want to jam the two things together somehow to produce a tuple<D,E,F>.  The case where D,E,F all happen to be "void" (or rather, "std::monostate" — paging Matt Calabrese! where's my regular void? ;)) is just a particularly boring special case of the general problem.
Alternatively/additionally, you could use tuple_foldl or tuple_foldr, which would allow you to use void instead of monostate; but those would unnecessarily imply an ordering (left-to-right or right-to-left) that you might not care about in the general case where you just want to visit all the members and don't really care in what order they're visited.

All of these (tuple_map, tuple_foldl, tuple_foldr) are available in Jared Hoberock's <tuple_utility> header, and probably elsewhere too.
Should they be standardized? IMHO, yes. But not with such a confusing name as "std::visit".

–Arthur

Matthew Woehlke

unread,
Mar 9, 2017, 11:27:12 AM3/9/17
to std-pr...@isocpp.org
On 2017-03-06 10:24, Barry Revzin wrote:
> What you want isn't really visiting, it's more of a loop. There was a
> proposal for exactly that
> (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0589r0.pdf) but
> the response was that this would be better handled through reflection
> tools.

That's not exactly how I remember it. Note:

Do we want a facility like this? SF 20 | F 6 | N 1 | A 0 | SA 0

The biggest objection, based on the wiki notes, seems to be that "it's
not a loop", and also folks concerned that if you exit the loop from an
early instantiation, the remaining instantiations still need to be well
formed. There were also various mutterings about the syntax (e.g. should
we write `static for` or `constexpr for` or some such instead of just
`for`).

That said, IIRC the consensus was to send it to SG7 to bake for a while...

On 2017-03-06 15:54, Nicol Bolas wrote:
> It seems to me that the generalized tuple unpacking proposal
> <http://wg21.link/P0535> makes this all obsolete:
>
> auto t = std::make_tuple(100, "500", '!');
> [](auto& x) { std::cout << x; }([:]t)...;

This is probably true for the OP's proposal. Less so for P0589; it
doesn't allow for the flow control that P0589 can provide.

--
Matthew
Reply all
Reply to author
Forward
0 new messages