Ideas for extending std::tuple with field-name based access

168 views
Skip to first unread message

Johannes Schaub

unread,
Oct 12, 2016, 7:48:24 AM10/12/16
to std-pr...@isocpp.org
Hello all,

Using type-tags to access tuple fields is a common technique to access
fields by "name"

struct position_tag : type<double> { };
struct color_tag : type<QColor> { };

my_tuple<field<position_tag, color_tag> m;

QColor c1 = std::get<color_tag>(m);
QColor c2 = std::get<QColor>(m);
QColor c3 = std::get<1>(m);

I was thinking about extending std::tuple with support for tags. This
can be done (i believe) most straight forward with string template
arguments, if we would have support for them in the future

std::tuple<field<double, "position">, field<QColor, "color">> getStop();

auto m = getStop();
double pos = std::get<"position">(m);

An alternative (but uglier) implementation would abuse unnamed struct
definitions in template-arguments with the "type" baseclass of above
to communicate the type

std::tuple<struct : typed<double> { type position; },
struct : typed<QColor> { type color; }> getStop();

Here, "type" is declared in base-class "typed" as a type-alias for its
template-parameter. When tuple sees a parameter has "typed<T>" as a
base class, it inherits from it. Therefore you could write

auto m = getStop();
QColor c = m.color;

std::get<N> would cast the unnamed-struct to "struct_type::type",
requiring a standard-layout struct (can be tested-for with a
type-trait). So the unnamed-struct cannot "escape" to the users of the
tuple.

What do you think about the two approaches? Are there existing papers
about perhaps other techniques?

Johannes Schaub

unread,
Oct 12, 2016, 7:54:09 AM10/12/16
to std-pr...@isocpp.org
And, a more important question: Do we need proposals for extending
std::tuple this way, or would unnamed-struct definitions in function
return types satisfy this use-case better?

struct { double position; QColor color; } getStop();

auto m = getStop();
QColor c1 = m.color;
QColor c2 = std::get<QColor>(m);
QColor c3 = std::get<1>(m);

Is there any drawback from using this technique over extending
std::tuple? As far as I'm aware, the generic functions in std:: that
operate on tuples do so by facilitating std::tuple_size, std::get etc,
so they could equally work on these one-off structs, right (of course,
it would need some "built-in" support from the implementation to
inspect above unnamed struct without the user having to write
specializations for those tuple traits, I guess?).

Michał Dominiak

unread,
Oct 12, 2016, 7:58:44 AM10/12/16
to std-pr...@isocpp.org
You can already somewhat do this:

auto get_something() {
    struct ret_type { int some_value; };
    return ret_type{ 1 };
}

--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CANu6V4W6ahF9QpfwXyb08965XCONdounDV1zf39-OnKNLWq%3DBw%40mail.gmail.com.

TONGARI J

unread,
Oct 12, 2016, 9:09:20 AM10/12/16
to ISO C++ Standard - Future Proposals
I'm not aware of any existing proposal, but some experimental language features I'm developing will allow you to do this:

std::tuple<double.position, QColor.color> getStop();
auto m = getStop();
double pos = m.position;
QColor c = m.color;

Let me know if you need more detail.

Simon Brand

unread,
Oct 12, 2016, 9:13:32 AM10/12/16
to std-pr...@isocpp.org
Interesting, is that double.position some special syntax exception, or does it generate an anonymous class with a single public data member with the given type and name? Can that be used with existing classes with no changes?
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/19048c95-770c-4413-b5ff-aa405c389621%40isocpp.org.

-- 
Simon Brand
Staff Software Engineer
Codeplay Software Ltd
Level C, Argyle House, 3 Lady Lawson St, Edinburgh, EH3 9DR
Tel: 0131 466 0503
Fax: 0131 557 6600
Website: http://www.codeplay.com
Twitter: https://twitter.com/codeplaysoft

This email and any attachments may contain confidential and /or privileged information and is for use by the addressee only. If you are not the intended recipient, please notify Codeplay Software Ltd immediately and delete the message from your computer. You may not copy or forward it, or use or disclose its contents to any other person. Any views or other information in this message which do not relate to our business are not authorized by Codeplay software Ltd, nor does this message form part of any contract unless so stated.
As internet communications are capable of data corruption Codeplay Software Ltd does not accept any responsibility for any changes made to this message after it was sent. Please note that Codeplay Software Ltd does not accept any liability or responsibility for viruses and it is your responsibility to scan any attachments.
Company registered in England and Wales, number: 04567874
Registered office: 81 Linkfield Street, Redhill RH1 6BY 

TONGARI J

unread,
Oct 12, 2016, 9:33:22 AM10/12/16
to ISO C++ Standard - Future Proposals
On Wednesday, October 12, 2016 at 9:13:32 PM UTC+8, Simon Brand wrote:
Interesting, is that double.position some special syntax exception, or does it generate an anonymous class with a single public data member with the given type and name? Can that be used with existing classes with no changes?

OK, let me explain a little more:
A type like double.position is what I call a "designating type", it's always incomplete and is only for decomposition purpose.
To use such types with existing templates, changes are needs in the templates themselves, so does std::tuple.

Actually there are 2 features involved in the example:
  • Template declname parameter
  • Designating type
Template declname parameters allow you to have declaration names templated, e.g.

template<class T, declname N>
struct S
{
   
T N;
};

S
<int, ?data> s;
s
.data = 0;

Designating type is basically a normal type + a designator, e.g.

template<class T>
struct S;

template<class T, declname N>
struct S<T.N> // decompose the designating type
{
    T N
;
};
 
S
<int.data> s;
s
.data = 0;

S
<int> s2; // ok, N is unnamed

You can see it as a syntax sugar, but it's more than that since the designator can be optional, which is important in cases like std::tuple.

Nicol Bolas

unread,
Oct 12, 2016, 10:02:30 AM10/12/16
to ISO C++ Standard - Future Proposals

There are two kinds of uses of tuple. The first kind is for writing quick-and-dirty classes, because you don't feel like writing an actual type. The second kind is for metaprogramming uses, where you're grouping together things by position.

For the latter use, naming tuple elements makes no sense, as the elements only have meaning by their positions. For the former, it would be better to allow people to create anonymous structs in various places, thus avoiding the use of tuples for these types to begin with.

We certainly should not be entertaining any language-based mechanisms to add these kinds of things in.

Larry Evans

unread,
Oct 12, 2016, 2:56:47 PM10/12/16
to std-pr...@isocpp.org
> We /certainly/ should not be entertaining any language-based mechanisms
> to add these kinds of things in.
>
Nicolas, in another thread:

https://groups.google.com/a/isocpp.org/d/msg/std-proposals/pk4s49YJpUg/CgprxLydEgAJ

gave, IIUC, a similar argument:

From my perspective, the principle reason to use a tuple
is metaprogramming. That is, you're assembling an
aggregation of values from some some user-provided
construct. A tagged tuple has limited metaprogramming
value, but it still has some value in that regard. By
tagging types with names, you can develop interfaces
between the sender and the receiver of a tuple that allow
you to more effectively communicate. Rather than
communicating by type or index, you communicate by a name,
thus giving the sending code the freedom to pick and
choose where the element is in the tuple.

Reading that thread may give us a better understanding of Nicolas' point.

-regards,
Larry




Larry Evans

unread,
Oct 12, 2016, 3:21:26 PM10/12/16
to std-pr...@isocpp.org
What about something similar for variant?

std::variant<double.position,QColor.color>
var_pos_color=std::tuple<double>(1.2);

then:
assert(var_pos_color.position == 1.2);
assert(var_pos_color.color == undefined);
assert(var_pos_color.which == position);

Matthew Woehlke

unread,
Oct 12, 2016, 3:49:09 PM10/12/16
to std-pr...@isocpp.org
On 2016-10-12 07:58, Michał Dominiak wrote:
> You can already somewhat do this:
>
> auto get_something() {
> struct ret_type { int some_value; };
> return ret_type{ 1 };
> }

That's horrid... it requires that the function definition be inline. The
idea behind P0222+P0224 (P0224 particularly) was specifically to avoid
this requirement.

--
Matthew

Message has been deleted

Giovanni Piero Deretta

unread,
Oct 13, 2016, 5:32:02 AM10/13/16
to ISO C++ Standard - Future Proposals
On Wednesday, October 12, 2016 at 12:48:24 PM UTC+1, Johannes Schaub wrote:
Hello all,

Using type-tags to access tuple fields is a common technique to access
fields by "name"

Two things first: I believe that the current Range proposal has something similar already. Second, I think you can declare, but not define, types in template arguments.

Finally, I have been (very slowly) working on a proposal about this (which ambitiously also covers the 'overload set', 'uniform calling convention', 'named arguments', and 'operator dot overloading' design space).

What I have currently is a macro based emulation [1] that allows this:

   auto tuple = tup($(foo) = 10, $(bar) = 20);
   assert(tuple.foo == 10);
   assert(tuple.bar == 20);


A proper language implementation would use ' .<identifier>' instead of '$(identifier)'

[1] https://github.com/gpderetta/libtask/blob/master/tests/q_test.cpp

TONGARI J

unread,
Oct 13, 2016, 5:50:41 AM10/13/16
to ISO C++ Standard - Future Proposals
Seems we have some work overlapped ;) 
With my experimental language features + template arg deduction from ctor (C++17), this is possible:
std::tuple tup(.foo = 10, .bar = 20); // deduced to std::tuple<int.foo, int.bar>

Michał Dominiak

unread,
Oct 13, 2016, 5:55:25 AM10/13/16
to ISO C++ Standard - Future Proposals
How is that different from a structure? (Assuming we get type deduction for NSDMs, which seems to be much less far fetched than assuming your thing is going to even pass EWG...)

--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.

Giovanni Piero Deretta

unread,
Oct 13, 2016, 5:55:41 AM10/13/16
to ISO C++ Standard - Future Proposals

So it seems! That's exactly my end game.
Do you already have some draft proposal? I have been focusing on the emulation implementation.

 

TONGARI J

unread,
Oct 13, 2016, 6:04:47 AM10/13/16
to ISO C++ Standard - Future Proposals
They're some extensions to my last uniform designator idea that I posted months ago.
So no draft proposal yet, I only have a working implementation based on Clang (still some ABI issues to solve).

Giovanni Piero Deretta

unread,
Oct 13, 2016, 6:06:06 AM10/13/16
to std-pr...@isocpp.org

On 13 Oct 2016 10:55 am, "Michał Dominiak" <gri...@griwes.info> wrote:
>
> How is that different from a structure? (Assuming we get type deduction for NSDMs, which seems to be much less far fetched than assuming your thing is going to even pass EWG...)
>

It can be used as part of an expression; it allows, among other things for named function  arguments; doesn't require auto NSDM which has been shot down already; the generated tuple is introspection friendly (my emulation even provides a string representation of the field name); it uses the same syntax as C99 designated initilalizers and can be used to emulate them; it only requires a well contained language change.

> You received this message because you are subscribed to a topic in the Google Groups "ISO C++ Standard - Future Proposals" group.
> To unsubscribe from this topic, visit https://groups.google.com/a/isocpp.org/d/topic/std-proposals/UWc3QbxVf0c/unsubscribe.
> To unsubscribe from this group and all its topics, send an email to std-proposal...@isocpp.org.


> To post to this group, send email to std-pr...@isocpp.org.

> To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAPCFJdS7iPJumE%3DbHhChwUrrA20n35j1XJs8-DO9HVMaJa2RxA%40mail.gmail.com.

Giovanni Piero Deretta

unread,
Oct 13, 2016, 6:50:30 AM10/13/16
to ISO C++ Standard - Future Proposals

I had completely forgotten about that proposal (even though I had commented on it saying I liked it!). I ended up reinventing the idea, (even the . based opt-in for named parameters); possibly I was influenced subconsciously :).

In my design,  an '.<identifier>' expression by itself is a literal for some std::identifier<I> (where I is unspecified and there is 1:1 mapping from I to 'identifier'). std::identifier would overload operator=(T) to return an std::assignment_expression<I, T>. std::as_field(std::assignment_expression<I,T>) returns an unspecified structure with a single field named 'identifier; of type T. Similarly, std::identifier<I>::operator(T x, Rest... rest) would return the result of the first valid of these: x.<identifier>, x.<identifier>(rest...), <identifier>(x, rest...), does_not_understand(std::identifier<I>, x, rest); constexpr identifier<I>::name() would return a string_view over "<identifier>".

These can all be implemented in C++14 (with the help of the preprocessor), and by themselves already allow for a lot of functionality.

-- gpd


 

John Yates

unread,
Oct 13, 2016, 8:12:41 AM10/13/16
to std-pr...@isocpp.org

std
::tuple<double.position, QColor.color> getStop();
auto m = getStop();
double pos = m.position;
QColor c = m.color;

​Just a wild stab off the top of my head:

enum class MoreReadableTags { dimension, label, success };
std::tuple [
MoreReadableTags
] <unsigned, std::string, bool> ​
 
​getSomething();
auto x = getSomething();
if (x.success) {
    display(x.dimension, x.label);
}​

/john

TONGARI J

unread,
Oct 13, 2016, 8:21:31 AM10/13/16
to ISO C++ Standard - Future Proposals
It's quite cool as a macro approach.

The unspecified `I` in your idea maps directly to the "template declname parameter" in my design.

In my design (so is in C99), '.<identifier> = <expr>' is not a real expression, and '.<identifier>' itself does not form an expression as well.
While you can still implement the `identifier<I>` as you described, but I think it becomes unnecessary.

The ability to retrieve the name string is worth considering, could be a variable template that connects to some compiler intrinsic.

Larry Evans

unread,
Oct 13, 2016, 12:20:35 PM10/13/16
to std-pr...@isocpp.org
On 10/13/2016 07:12 AM, John Yates wrote:
> |
>
> std::tuple<double.position,QColor.color>getStop();
> autom =getStop();
> doublepos =m.position;
> QColorc =m.color;
> |
>
>
> ​Just a wild stab off the top of my head:
>
> enum class MoreReadableTags { dimension, label, success };
> std::tuple [
> MoreReadableTags
> ] <unsigned, std::string, bool> ​
>
I like this idea; however, that's probably because I tried
something like it here:

https://github.com/cppljevans/variadic_templates/blob/master/boost/composite_storage/pack/container_all_of_aligned.hpp#L25

The Index template argument corresponds to your
MoreReadableTags argument.

However, what would really be nice is some map from
enum->type. Then, such a map could be used both for
tagged tuples as well as tagged variants. IOW,

tuple<enum2type> a_tuple;
variant<enum2type> a_variant;

Thoughts?

-regards,
Larry





Vicente J. Botet Escriba

unread,
Oct 13, 2016, 4:54:15 PM10/13/16
to std-pr...@isocpp.org
Le 12/10/2016 à 13:54, 'Johannes Schaub' via ISO C++ Standard - Future
Proposals a écrit :
Hi,

See " parameter packs outside of templates" -
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0341r0.html


Vicente

Vicente J. Botet Escriba

unread,
Oct 13, 2016, 4:54:59 PM10/13/16
to std-pr...@isocpp.org

Range TS has a tagged tuple.


Vicente

Vicente J. Botet Escriba

unread,
Oct 13, 2016, 4:58:26 PM10/13/16
to std-pr...@isocpp.org
Le 12/10/2016 à 21:21, Larry Evans a écrit :


What about something similar for variant?

  std::variant<double.position,QColor.color>
var_pos_color=std::tuple<double>(1.2);

then:
  assert(var_pos_color.position == 1.2);
  assert(var_pos_color.color == undefined);
  assert(var_pos_color.which == position);

Reply all
Reply to author
Forward
0 new messages