C++ named tuple

3,826 views
Skip to first unread message

glme...@gmail.com

unread,
Mar 29, 2013, 9:03:24 AM3/29/13
to std-pr...@isocpp.org
Hi,

I propose to add a new future to C++14: "the named tuple".
In some case could be very usefull to have something like that:

std::tuple< foo@int, bar@std::string > t = std::make_tuple(5, "hello!");

than we can access to our tuple in this way:

auto n = std::get<foo>(t);
auto m = std::get<bar>(t);

What is your idea about this?
Thanks
Message has been deleted

sghe...@gmail.com

unread,
Mar 29, 2013, 10:06:03 AM3/29/13
to std-pr...@isocpp.org, glme...@gmail.com
Seems similar enough to what you can already achieve with 'tag structs' 

These are ubiquitous in libraries such as Boost Graph Library, or Boost MultiIndex.

Minimalist sample from Boost Fusion documentation:

set<int, char, bool> s(1, 'x', true);
assert(at_key<char>(s) == 'x');

Or when tag types don't correspond to the element data types: (map)

typedef map<
 pair
<int, char>
 
, pair<double, std::string> >
map_type
;

map_type m
(
 make_pair
<int>('X')
 
, make_pair<double>("Men"));

std
::cout << at_key<int>(m) << std::endl;
std
::cout << at_key<double>(m) << std::endl;

Ville Voutilainen

unread,
Mar 29, 2013, 10:19:39 AM3/29/13
to std-pr...@isocpp.org
On 29 March 2013 16:05, <sghe...@gmail.com> wrote:
> Seems similar enough to what you can already achieve with 'tag structs'
> These are ubiquitous in libraries such as Boost Graph Library, or Boost
> MultiIndex.
> Minimalist sample from Boost Fusion documentation:
> set<int, char, bool> s(1, 'x', true);
> assert(at_key<char>(s) == 'x');

That's not the same thing. That is an example of accessing tuple elements by
type, not by name. Accessing them by type is already going forward
along the lines
proposed in http://open-std.org/JTC1/SC22/WG21/docs/papers/2012/n3404.html
but there's not yet any proposal to allow accessing tuple elements by name.

Aleksandar Fabijanic

unread,
Mar 29, 2013, 10:55:19 AM3/29/13
to std-pr...@isocpp.org
On Fri, Mar 29, 2013 at 9:19 AM, Ville Voutilainen
<ville.vo...@gmail.com> wrote:
> On 29 March 2013 16:05, <sghe...@gmail.com> wrote:
>> Seems similar enough to what you can already achieve with 'tag structs'

> but there's not yet any proposal to allow accessing tuple elements by name.

If someone cares to put forth some effort it, here's an existing
implementation (not updated to C++11):

https://github.com/pocoproject/poco/blob/develop/Foundation/include/Poco/NamedTuple.h

A use example:

NamedTuple<std::string, int, bool, float, char, long>
aTuple("string", "1", "int", 1, "bool", true, "float", 1.5f, "char",
'c', "long", 999);
assert (aTuple["string"] == "1");
assert (aTuple["int"] == 1);
assert (aTuple["bool"] == true);
assert (aTuple["float"] == 1.5);
assert (aTuple["char"] == 'c');
assert (aTuple["long"] == 999);

and some shortcomings/dilemmas pointed out here:

http://pocoproject.org/blog/?p=89

Gian Lorenzo Meocci

unread,
Mar 29, 2013, 11:19:57 AM3/29/13
to std-pr...@isocpp.org
I think that we need put Named Tuple into the standard to avoid some confusion and to reduce "ugly" code



--

---
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/N-kIXNrkTUk/unsubscribe?hl=en.
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.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/?hl=en.





--
GL
http://www.meocci.it

morw...@gmail.com

unread,
Mar 29, 2013, 1:36:07 PM3/29/13
to std-pr...@isocpp.org, glme...@gmail.com
 I just don't see how this would be more useful or easy to use than:

    struct
    {
        int foo;
        string bar;
    } t = { 5, "hello" };

    auto n = t.foo;
    auto m = t.bar;
 

Aleksandar Fabijanic

unread,
Mar 29, 2013, 4:21:35 PM3/29/13
to std-pr...@isocpp.org
Such comparison would be appropriate in a context of discussion "does
C++ need std::tuple?" (or std::pair, for that matter).

Since there already exists std::tuple<int, string> and it's being
discussed here, relevant question is "which is better?":

int i = t.get<0>();

or

int i = t["foo"];

or

int i = t<"bar">();

Alex

sghe...@gmail.com

unread,
Mar 29, 2013, 7:24:02 PM3/29/13
to std-pr...@isocpp.org
> That's not the same thing. 

I didn't say it was

> That is an example of accessing tuple elements by type, not by name. 

Erm. Types /are/ ("have") names.

typedef map<
   pair<struct foo, char>
 , pair<struct bar, std::string> >
map_type;

auto n = at_key<foo>(m);
auto m = at_key<bar>(m);
Look familiar?

> Accessing them by type is already going forward [...]  

> but there's not yet any proposal to allow accessing tuple elements by name. 

My point is, it doesn't solve any additional problem. Instead it introduces more clumsy syntax to do basically the same but with inconsistent typology.

One might as well just ask for support of string literals as non-type template arguments, which would frankly stand a lot better chance of getting some sort of consistent implementation, and would also be infinitely more generally applicable (note: I'm not proposing C++ should have such a thing, though).

Ville Voutilainen

unread,
Mar 29, 2013, 7:53:21 PM3/29/13
to std-pr...@isocpp.org
On 30 March 2013 01:24, <sghe...@gmail.com> wrote:
>> That is an example of accessing tuple elements by type, not by name.
> Erm. Types /are/ ("have") names.

Elements of the same type have different names, in normal structs.
That's what's sought after for tuples.

> typedef map<
> pair<struct foo, char>
> , pair<struct bar, std::string> >
> map_type;
> auto n = at_key<foo>(m);
> auto m = at_key<bar>(m);
> Look familiar?

No. I don't see the point of this example.

>> Accessing them by type is already going forward [...]
>> but there's not yet any proposal to allow accessing tuple elements by
>> name.
> My point is, it doesn't solve any additional problem. Instead it introduces
> more clumsy syntax to do basically the same but with inconsistent typology.

Uh huh. The additional problem it would solve is the equivalent of
struct X { char jonesy; int bonesy};
and being able to name the components of a tuple<char, int> "jonesy"
and "bonesy". It
further solves the problem that by-type access doesn't solve, that is,
if you have
tuple<int, int, int>, you can't get<int>() from it because it's
ambiguous which int member
you mean.

> One might as well just ask for support of string literals as non-type
> template arguments, which would frankly stand a lot better chance of getting
> some sort of consistent implementation, and would also be infinitely more
> generally applicable (note: I'm not proposing C++ should have such a thing,
> though).

One certainly might, and that would be a useful tool for implementing
named access to tuple
elements.

sghe...@gmail.com

unread,
Mar 29, 2013, 8:27:19 PM3/29/13
to std-pr...@isocpp.org
> No. I don't see the point of this example.

Really? 

> [...] tuple<int, int, int>, you can't get<int>() from it because it's 
> ambiguous which int member you mean. 

Well, then, maybe this one helps: 

typedef map<
 pair
<struct jonesy, int>
 
, pair<struct bonesy, int> >
map_type
;

map_type m
;

auto i1 = at_key<jonesy>(m);
auto i2 = at_key<bonesy>(m);

Here's a LWS demonstruction http://liveworkspace.org/code/3WmdD1$0

Travis Gockel

unread,
Mar 29, 2013, 8:57:24 PM3/29/13
to std-pr...@isocpp.org, glme...@gmail.com, morw...@gmail.com
On Friday, March 29, 2013 11:36:07 AM UTC-6, morw...@gmail.com wrote:

 I just don't see how this would be more useful or easy to use than:

    struct
    {
        int foo;
        string bar;
    } t = { 5, "hello" };

    auto n = t.foo;
    auto m = t.bar;
 
 
The big issue here is things like get<0>(t) don't work.

I have this funny vision where you could use your own tuple-like types inside things like std::map.

struct MyThing
{
    int id;
    double score;
    std::string name;
};

std::map<MyThing> m;
m.insert({ 1, 8.3, "Bob"});
m.insert({ 2, 1.2, "James"});
auto iter = m.find(2);
std::cout << iter->name << " : " << iter->score << std::endl;

Slightly better names than first and second.

Nicol Bolas

unread,
Mar 29, 2013, 9:21:19 PM3/29/13
to std-pr...@isocpp.org

Congradulations; you just implemented `std::map<std::string, any>`. Except for potentially disallowing adding new members or changing the types of existing members, this is functionally no different than this:

std::map<std::string, any> mp {{"string", "1"}, {"int", 1}, {"bool", true}, {"float", 1.5f}, {"char", 'c'}, {"long", 999}};

So why should we prefer a new class, when all we need to get mostly equivalent is to add a std::any type? And that's already under discussion.

Nicol Bolas

unread,
Mar 29, 2013, 9:22:44 PM3/29/13
to std-pr...@isocpp.org

Any such problems will be more effectively resolved once we have some compile-time reflection. Though it should be noted that the middle one is a runtime lookup, unlike the first which is compile-time.

Aleksandar Fabijanic

unread,
Mar 29, 2013, 9:27:53 PM3/29/13
to std-pr...@isocpp.org
On Fri, Mar 29, 2013 at 8:21 PM, Nicol Bolas <jmck...@gmail.com> wrote:

> So why should we prefer a new class, when all we need to get mostly
> equivalent is to add a std::any type? And that's already under discussion.

std::any will only give you exactly the type it holds and you have to
know up front *exactly* what it is (i.e. on data extraction side,
std::any "type system" is even more rigid than C++ type system - no
implicit conversions).

But that is a topic for another discussion. Here, the topic is merits
of access of tuple members by name (as opposed to access by index).

Alex

Nicol Bolas

unread,
Mar 29, 2013, 9:36:17 PM3/29/13
to std-pr...@isocpp.org

And that implementation of that topic is what I'm talking about. The implementation returns a DynamicAny, which is just a variation of `any` that is implicitly convertible to it's original type (or throws an exception). So it's a `map<string, DynamicAny>`; why do we need a whole new class for that?

Aleksandar Fabijanic

unread,
Mar 29, 2013, 9:56:39 PM3/29/13
to std-pr...@isocpp.org
On Fri, Mar 29, 2013 at 8:36 PM, Nicol Bolas <jmck...@gmail.com> wrote:

>> But that is a topic for another discussion. Here, the topic is merits
>> of access of tuple members by name (as opposed to access by index).
>
> And that implementation of that topic is what I'm talking about. The
> implementation returns a DynamicAny, which is just a variation of `any` that
> So is implicitly convertible to it's original type (or throws an exception).

It is convertible to *any type* as long as conversion makes sense, it
prevents loss of precision or narrowing conversions, it allows user
extensions etc. But none of that is relevant here.

> it's a `map<string, DynamicAny>`; why do we need a whole new class for that?

Since someone asked about tuple members access by name, I pointed to
something related - a tuple with added vector<string> containing names
for contained values. It was not suggested that there should be
another class.

This discussion is not about any, DynamicAny or std::map. It is about
access to tuple members by name.

Alex

Gian Lorenzo Meocci

unread,
Mar 30, 2013, 5:12:21 AM3/30/13
to std-pr...@isocpp.org
Imagine a C++ for all, not only for nerd people.
Imagine that you are writing a library like this:

class MyLib
{
public:
    std::tuple<bool, int, std::string> f()
    {
           // some cool computation
            return std::make_tuple(true, 0, "All works fine");
    }

   
    std::tuple<bool, int, int> g() { .... }
};

All method return some kind of tuple: the first member is  a bool, the second is always an int (errorcode) the third depend by function. The user must remember that in case of g function the errorcode is the 2th not 3th.

So If I have the possibility to "tag" a position of element in a tuple I can do something like that

class MyLib
{
public:
    std::tuple<"result"@bool, "errorcode"@int, std::string> f()
    {
           // some cool computation
            return std::make_tuple(true, 0, "All works fine");
    }

   
    std::tuple<"result"@bool, "errorcode"@int, int> g() { .... }
};

than the user can do :

MyLib lib;

auto t = lib.g();
std::cout << std::get<"errorcode">(t) << std::endl;

In this way I create only the class that I needed without introduced any support struct or class. The code is more clear to read and to write!



--

---
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/N-kIXNrkTUk/unsubscribe?hl=en.
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.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/?hl=en.


Nicol Bolas

unread,
Mar 30, 2013, 6:51:48 AM3/30/13
to std-pr...@isocpp.org, glme...@gmail.com
On Saturday, March 30, 2013 2:12:21 AM UTC-7, Gian Lorenzo Meocci wrote:
Imagine a C++ for all, not only for nerd people.

So, you mean what we have right now, right?
 
Imagine that you are writing a library like this:

class MyLib
{
public:
    std::tuple<bool, int, std::string> f()
    {
           // some cool computation
            return std::make_tuple(true, 0, "All works fine");
    }

   
    std::tuple<bool, int, int> g() { .... }
};

All method return some kind of tuple: the first member is  a bool, the second is always an int (errorcode) the third depend by function. The user must remember that in case of g function the errorcode is the 2th not 3th.

Why would I be returning a std::tuple? I should be returning one of these:

template<typename t>
struct ErrorRet
{
 
bool didError;
 
int errorCode;
  T actualData
;
};

ErrorRet<int> g();

See? Much better. It has genuine semantics with a real name on it; people don't have to ask what the tuple means or what its elements mean. And of course this all assumes that I'm even using an error code at all instead of an exception.

Plus, I can put actual semantics on the type. I can throw an `explicit operator bool` onto it, so that I can test it without having to explicitly use the `didError` member. I could change the value representation to minimize pointless data storage, so that it uses a union between the template argument type and the error code.

Or we could just standardize a "value or errorcode" type as people are talking about ;).

To my mind, the only reason to use a tuple instead of a struct is because you want to compile-time iterate over the elements of the data structure. Which you can only do with a tuple, because C++ lacks reflection. Of course, what that means is that, once we do get compile-time reflection, the main use-case for tuple disappears.

Or said another way, we need structs to behave more like tuples, not vice-versa. And then we won't need tuples anymore.
 
So If I have the possibility to "tag" a position of element in a tuple I can do something like that

class MyLib
{
public:
    std::tuple<"result"@bool, "errorcode"@int, std::string> f()
    {
           // some cool computation
            return std::make_tuple(true, 0, "All works fine");
    }

   
    std::tuple<"result"@bool, "errorcode"@int, int> g() { .... }
};

than the user can do :

MyLib lib;

auto t = lib.g();
std::cout << std::get<"errorcode">(t) << std::endl;

In this way I create only the class that I needed without introduced any support struct or class. The code is more clear to read and to write!

Is C++ running out of typenames? Do you honestly believe that there is something onerous being added by using a real type with a real name, rather than a nameless, semanticless collection of values? I hope you're not asking for a feature just because you don't want to introduce a typename.

More importantly, how is that clear to read or write? I have no idea what that tuple means semantically. The typename is gigantic and bulky to write, and the only thing that saves you from having to see this big thing everywhere is using `auto` when you store it. Even so, your IDE is still going to show you a giant, bulky typename when you want to know what type `t` is.

Furthermore, I'd love to hear how you justify saying that `std::get<"errorcode">(t)` is "more clear to read and to write" than `t.errorCode`? Especially considering your "Imagine a C++ for all, not only for nerd people," statement at the top of your post. I don't know how many non-"nerd people" even know that much about `std::tuple` to begin with, let alone would see your `std::get` as being more obvious as to what's going on.

People seem to have these notions nowadays that tuples should be able to replace structs or that lambdas should be able to replace functors. That a tuple should be a quickie way to declare a struct, or that a lambda should be a quickie way to declare a functor. I personally despise this, for several reasons. The most important being that it starts corrupting good, localized functionality in order to turn it into something it's not.

Someone actually suggested a lambda syntax where you could define multiple functions in a single expression for the same lambda type. Really, is it that hard to just make an inline struct right there in the function? We can use those as template arguments now in C++11, so there's no excuse for not doing it.

And here you are, suggesting we take simple, clear tuple functionality and muddy it's purpose. A tuple is not a struct. A tuple should not behave like a struct. A tuple is a compile-time enumerable collection of values. The very fact that you want to access tuple elements by some name rather than an index says that you're using the wrong datastructure for your task. This is no different from wanting to random access a linked-list; that's a good indication that you picked the wrong container for your needs.

If you want a more compact struct definition syntax, ask for one. If you want a more compact functor definition syntax, ask for one. But at no time should you ask for what you're asking for now.

Nicol Bolas

unread,
Mar 30, 2013, 6:57:16 AM3/30/13
to std-pr...@isocpp.org, glme...@gmail.com
On Saturday, March 30, 2013 3:51:48 AM UTC-7, Nicol Bolas wrote:
To my mind, the only reason to use a tuple instead of a struct is because you want to compile-time iterate over the elements of the data structure. Which you can only do with a tuple, because C++ lacks reflection. Of course, what that means is that, once we do get compile-time reflection, the main use-case for tuple disappears.

Let me take that back. There is a second reason: if you're using template metaprogramming techniques to build some value. This may well require the ability to put together some type from raw materials in a way that a template struct can't do.

Of course, that's another thing that is being talked about with respect to reflection.

Vicente J. Botet Escriba

unread,
Apr 1, 2013, 11:44:26 AM4/1/13
to std-pr...@isocpp.org
Le 29/03/13 14:03, glme...@gmail.com a écrit :
Hi,


I think the first think to do is to add tagged tuples as the ones available in Boost.Fusion.
Once you have tagged tuples in the standard, the syntax you are proposing is syntactical sugar.
There are however some scope issues, what is the scope of the names foo and bar?
How the compiler could associate foo to the correct foo? E.g.

std::tuple< foo@int, bar@std::string > t1 = std::make_tuple(5, "hello!");
std::tuple< bar@std::string, foo@int > t2 = std::make_tuple( "hello!", 5);

auto n = std::get<foo>(t1);

I don't know too much about compiler design, but having to look at the type of t1 to understand which foo are we referring to is quite complex. Maybe you need something like

auto n = std::get<t1::foo>(t1);

If  opaque types are accepted they should allow to do something similar.
With the syntax proposed in http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3515.pdf

using foo = public int;
using far = public std::string;
std::tuple< foo, bar > t = std::make_tuple(5, "hello!");


auto n = std::get<foo>(t);
auto m = std::get<bar>(t);

Of course this doesn't respond completely to your need and that I recognize this could seem artificial when the user is not working already with opaque types.

Best regards,
Vicente

glme...@gmail.com

unread,
Apr 7, 2013, 12:15:52 PM4/7/13
to std-pr...@isocpp.org, glme...@gmail.com
thanks to all for clarification and comments


On Friday, March 29, 2013 2:03:24 PM UTC+1, glme...@gmail.com wrote:

masse....@gmail.com

unread,
May 9, 2013, 2:55:29 PM5/9/13
to std-pr...@isocpp.org, glme...@gmail.com
Hi,
for my part, what I'd like to do is to cast tuples into a struct.
Basically, the idea is to cast std::tuple into a struct whose fields will correspond the those of the tuple.

My goal is to be able to do things like :
typedef std::tuple<int, int, std::string> user_record_type; // Note that this definition is here for the example only. In my case, the type user_record_type will in fact be computed as a std::tuple<int, int, std::string> using metaprogramming.
user_record_type get_random_user_from_db
();
//...
struct user { int id; int gid; std::string name; }
//...
user
& u = (user)get_random_user_from_db();
std
::cout << "User Id: " << u.id << " Group Id: " << u.gid << " User Name:" << u.name;


After some tests for that, here is what I could say:
* this could probably be achieved with compile-time refelection by copying every field of the tuple into the struct, but I'd like to avoid copying datas.
* using reinterpret_cast just doesn't work. The main reason is that std::tuple<int, int, std::string> doesn't necessary have the same layout than a struct with 3 fields. (for example, on gcc the field are stored in reverse order).
* I've been able to do an (incomplete) implementation of std::tuple where an std::tuple<int, int, std::string> seems to have the same layout than the struct user as define above. Anyway, I'm unsure this is something wich is guaranted by the standart.
* By overloading the cast operator, I was able to make the above example work. Note that the cast operator also check that the struct is standart-layout and have the same size in memory than the tuple. This is done to check that they both have the same layout. Even if incomplete, this check remains usefull in most situations. (what about adding std::is_same_layout<T1, T2>?)


What do you think of it? 

germa...@gmail.com

unread,
May 17, 2013, 4:19:51 AM5/17/13
to std-pr...@isocpp.org, glme...@gmail.com
From c++ now conference: https://github.com/boostcon/cppnow_presentations_2013

This path seems to solve a broader problem: Sebastian Redl: Overloading the Member Access Operator

Including named tuples also.

snk_kid

unread,
May 18, 2013, 7:31:41 AM5/18/13
to std-pr...@isocpp.org, glme...@gmail.com
The best and most general solution which not only fixes/improves the situation but improves many other features is to make tuple types built-in types and add support for pattern matching (and I don't mean just for tuples but in general). There is should be no reason that built-in tuples and pattern matching can be as efficient if not more so than any library solution and could easily be just syntactic sugar just as much as lambda expressions in C++11 currently are. Some imaginary examples of C++ with built-in tuple types and pattern matching:

// a simple type-inferred tuple binding:
auto x = (0, 0.0f, "foo"); // the type of x is inferred to be the tuple type: (int, float, const char*)

// the same as above but without local type inference:
(int, float, const char*) x = (0, 0.0f, "foo");

// some tuple pattern binding expressions (with type inference) using 'x' from the above:

auto (a, _, _) = x; // 'a' is bound too the first element of the tuple 'x', and a can be freely used, the other elements are ignored.

auto (_, _, c) = x; // 'c' is bound too the last element of the tuple 'x', other elements ignored.

auto (_, y, z) = x; // 'y' and 'z' are bound to the second and last element of 'x', others ignored.

auto (f@(_, k, _)) = (1, foo_type(), bar_type()); // 'f' binds to the whole tuple, 'k' binds the second element.

// tuples in functions and tupple patterns in function arguments:

auto make_a_2_tuple(float x, int y) -> (float,int)
{
    return (x,y);
}

template < typename FirstType, typename SecondType >
auto first(const (FirstType,SecondType)& (x, _)) -> FirstType

   return x;
}


template < typename FirstType, typename SecondType >
auto second(const (FirstType,SecondType)& (_, x)) -> SecondType
{
   return x;
}


// using the above definitions:

auto x =
make_a_2_tuple(0.0f,0);

auto (y,z) =
make_a_2_tuple(0.0f,0);

auto (_,k) = make_a_2_tuple(0.0f,0);

auto j = (1,1);
int x = second(
j);
int y = first(
j);

Not every language abstraction should be forced in to or even make sense as a library abstraction. Why is the community making this so hard? I see no reason why we couldn't have the above any more than we have say lambda expressions and be just as efficient if not more so than a library solution without all the ugly hacks and adding more peculiar language changes to just make the library abstraction work.

Can we please at least try to pursue the possibility.

Gian Lorenzo Meocci

unread,
May 18, 2013, 7:47:43 AM5/18/13
to snk_kid, std-pr...@isocpp.org
I agree with you, this code is more cleaner and elegant!

snk_kid

unread,
May 18, 2013, 11:58:16 AM5/18/13
to std-pr...@isocpp.org, glme...@gmail.com
I forgot to mention that tuple patterns can work with nested tuples as well:

auto x = ((1,1),2);

auto ((_,y),_) = x;

auto (_, z) = x;

auto k = (x,"foo");

auto (((_,l),_), _) = k;

template < typename X, typename Y, typename Z >
auto swizzle(const (X,(Y,Z))& (x,(y,z))) -> ((Z,Y),X)
{
   
return ((z,y),x);  
}

auto (_, x) = swizzle((1,(2,3));

Jonathan Wakely

unread,
May 19, 2013, 6:36:14 AM5/19/13
to std-pr...@isocpp.org, glme...@gmail.com


On Saturday, May 18, 2013 12:31:41 PM UTC+1, snk_kid wrote:
Not every language abstraction should be forced in to or even make sense as a library abstraction. Why is the community making this so hard? I see no reason why we couldn't have the above any more than we have say lambda expressions and be just as efficient if not more so than a library solution without all the ugly hacks and adding more peculiar language changes to just make the library abstraction work.

Can we please at least try to pursue the possibility.

 Sure ... the way to get such things done is to write a proposal. 

Nicol Bolas

unread,
May 19, 2013, 7:31:51 AM5/19/13
to std-pr...@isocpp.org, glme...@gmail.com
On Saturday, May 18, 2013 4:31:41 AM UTC-7, snk_kid wrote:
Not every language abstraction should be forced in to or even make sense as a library abstraction. Why is the community making this so hard? I see no reason why we couldn't have the above any more than we have say lambda expressions and be just as efficient if not more so than a library solution without all the ugly hacks and adding more peculiar language changes to just make the library abstraction work.

Can we please at least try to pursue the possibility.

I certainly hope not.

I have very rarely been in any situation in code where I needed an aggregate of arbitrary types that was not an object of some kind. That is, where the aggregate of types was not repeated frequently enough throughout the code that it needed a name (to identify its meaning). And once that particular aggregation needs a name, it's really just a struct. It probably should also have members in it, for specific operations on that type.

Usually, this comes up in the places where we see std::pair used: some kind of basic utility object who's contents are defined by user-specified template parameters. std::tuple is really just an expanded std::pair for these situations. But it is handy for compile-time generation of types.

In any case, if you have a function that returns multiple values, odds are good that you are going to have multiple functions that all return those same values with their same meaning. Therefore, you aren't returning multiple values; you're returning a single value, which just so happens to contain multiple components.

However, if you insist on having some language support for this, you can't use parentheses. That's not going to work; it would break lots of code. The standard already clearly defines what `(0, 0.0f, "foo")` means already.

However, you could do it with curly braces, since a curly-brace-bound list of types is not defined in C++ (though Bjarne wants to define it as a parameter/definition/thing for concepts, so you'll have to fight with him for that). And the reason to use curly braces instead of parentheses (even though a parenthesized list of types is equally undefined) is this:

{int, float, std::string} a = {0, 0.0f, "foo"};

Here, you're able to combine braced-type-lists (what I call the thing on the left) with braced-init-lists (what we know is the thing on the right). And thus, you have syntax which makes sense.

You can't backtrack uniform initialization to make `{0, 0.0f, "foo"}` deduce to a tuple by itself. And I call dibs on addendum to uniform initialization syntax (such as `{: ...}`), so don't do that either. But it's not like we don't have `make_tuple` already, so if you don't want to type the names, just use that:

auto a = make_tuple(0, 0.0f, "foo");

You won't get `std::string` out of that. But then again, you wouldn't get it from making `{:0, 0.0f, "foo"}` deduce to a tuple either. I'd prefer that it's spelled out directly that you're creating a tuple.

We should not encourage people to use tuples. They're a useful tool, but they shouldn't be the default or go-to object for people's use. Actual objects, with real definitions, should be the default object of choice. Use `tuple` when you don't have a choice, when you have to compile-time generate a sequence of values.

The days of tuples are numbered anyway. Once we start getting serious reflection features (which, at a minimum, includes compile-time iteration over types, and at a maximum includes compile-time generation of types from typelists), we won't need them. I would much rather we wait until we get some reflection features, then use those in language-specific functionality.

Gabriel Dos Reis

unread,
May 18, 2013, 12:47:41 PM5/18/13
to std-pr...@isocpp.org, glme...@gmail.com
snk_kid <korcan....@googlemail.com> writes:

| I forgot to mention that tuple patterns can work with nested tuples as well:
|
| auto x = ((1,1),2);
|
| auto ((_,y),_) = x;
|
| auto (_, z) = x;
|
| auto k = (x,"foo");

in the current language, k has type 'const char*'. Are you
suggesting to break that?

It is not clear to me what the problem really is and why removing
syntactic incompatibilities between C++ and Haskell will solve it.

-- Gaby
| --
|
| ---
| 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

stackm...@hotmail.com

unread,
May 22, 2013, 4:15:06 AM5/22/13
to std-pr...@isocpp.org, glme...@gmail.com
I like this idea particularily because it makes range based for over maps finally readable:
for((k, v) : m)
   
...
The syntax using parenthesis will not work though.

Róbert Dávid

unread,
May 22, 2013, 7:44:36 PM5/22/13
to std-pr...@isocpp.org, glme...@gmail.com

What's not readable in
for(auto& kv : m)
?

Jonathan Wakely

unread,
May 23, 2013, 5:58:18 AM5/23/13
to std-pr...@isocpp.org, glme...@gmail.com

You missed out the loop body where you have to refer to kv.first and kv.second, not nice names like "key" and "value".

Or when traversing a range of tuples, std::get<0>(kv) and std::get<1>(kv), which is even worse.

James Dennett

unread,
May 23, 2013, 8:37:52 AM5/23/13
to std-pr...@isocpp.org, glme...@gmail.com
In many cases C++14 allows std::get<Type>(kv), and it's pretty common
for a map to have a key type that's distinct from the type it maps to.
It's still not perfect, but if I'm iterating over a map<int, string>
then std::get<int>(kv) and std::get<string>(kv) are pretty readable,
IMO.

-- James

Jonathan Wakely

unread,
May 23, 2013, 8:53:23 AM5/23/13
to std-pr...@isocpp.org, glme...@gmail.com
On Thursday, May 23, 2013 1:37:52 PM UTC+1, James Dennett wrote:

In many cases C++14 allows std::get<Type>(kv), and it's pretty common
for a map to have a key type that's distinct from the type it maps to.
 It's still not perfect, but if I'm iterating over a map<int, string>
then std::get<int>(kv) and std::get<string>(kv) are pretty readable,
IMO.

It's not terrible, but it's not great either.

The first one should be std::get<const int>(kv), shouldn't it?


James Dennett

unread,
May 23, 2013, 4:48:39 PM5/23/13
to std-proposals, glmeocci
Yes, it should.

Not the first time I've made that mistake, and probably not the last.
At least it'll be caught at compile time (or at e-mail time).

-- James

Róbert Dávid

unread,
May 24, 2013, 6:34:52 AM5/24/13
to std-pr...@isocpp.org, glme...@gmail.com

You missed out the loop body where you have to refer to kv.first and kv.second, not nice names like "key" and "value".

Well, I'd think it is obvious what a key_value_pair.first is.. (I generally dislike one-letter variable names anyway) Or if you use it in gazillion places inside the loop, you can add
auto& key   = key_value_pair.first;
auto& value = key_value_pair.second;
At the beginning of the loop.

The std::get<const int>(kv) won't work though if you have an int-const int map..

I still think that it would be nice to have a "typedef" for variables, so I could access the member values of a pair/tuple/struct with unmodifiable code with a different name, something the original poster in this thread asks.

For first, I thought about something like
using kv.key = kv.first;
using kv.value = kv.second;
At first it's looking like breaking encapsulation (booh!), but that's not really that horrible: you should already have access to foo.x, you are just making an alias foo.y, just like as a typedef (or if you like, variabledef, memberdef).

Alternatively, Concepts would (have) come close to rescue with the concepts map, if they support member variables, not just typenames and functions:
concept KeyValue<C> {
 typename key_type;
 typename value_type;
 key
_type key;
 
value_type value;
}

template<typename T1, typename T2>
concept_map KeyValue<std::pair<T1, T2>> {
 typedef T1 key_type;
 typedef T2 value_type;
 using key = C.first;
 using value = C.second;

}

void f(const std::map<int, std::string>& m) {
 for(HasKeyValue& kv : m)
  std::cout << "key: " << kv.key << " value: " << kv.value << std::endl;
}

Regards, Robert

Giovanni Piero Deretta

unread,
May 28, 2013, 9:32:26 AM5/28/13
to std-pr...@isocpp.org, glme...@gmail.com
On Friday, May 24, 2013 11:34:52 AM UTC+1, Róbert Dávid wrote:

You missed out the loop body where you have to refer to kv.first and kv.second, not nice names like "key" and "value".

Well, I'd think it is obvious what a key_value_pair.first is.. (I generally dislike one-letter variable names anyway) Or if you use it in gazillion places inside the loop, you can add
auto& key   = key_value_pair.first;
auto& value = key_value_pair.second;
At the beginning of the loop.

N3617 would allow this:

auto key = []first;
auto value = []second;

for(HasKeyValue& kv : m)
  std::cout << "key: " << key(kv) << " value: " << value(kv) << std::endl;

 
As proposed, I do not think N3617 would allow something like this:

   auto key = []get<0>;
   auto value = []get<1>;

but I guess it could be extended.

-- gpd

Xeo

unread,
May 28, 2013, 1:42:02 PM5/28/13
to std-pr...@isocpp.org, glme...@gmail.com
On Tuesday, May 28, 2013 3:32:26 PM UTC+2, Giovanni Piero Deretta wrote:
As proposed, I do not think N3617 would allow something like this:

   auto key = []get<0>;
   auto value = []get<1>;

but I guess it could be extended.


Actually, it would (if you add `std::`), since `std::get<N>` is just another *id-expression*. (FWIW, the paper is currently undergoing a slight rehault that would make `[]first` invalue - `[].first` would be the new proposed and *only* valid syntax, except when in the lexical context of a class, i.e., a member function).

Giovanni Piero Deretta

unread,
May 29, 2013, 4:58:39 AM5/29/13
to std-pr...@isocpp.org, glme...@gmail.com
On Tue, May 28, 2013 at 6:42 PM, Xeo <hivem...@hotmail.de> wrote:
> On Tuesday, May 28, 2013 3:32:26 PM UTC+2, Giovanni Piero Deretta wrote:
>>
>> As proposed, I do not think N3617 would allow something like this:
>>
>> auto key = []get<0>;
>> auto value = []get<1>;
>>
>> but I guess it could be extended.
>>
>
> Actually, it would (if you add `std::`), since `std::get<N>` is just another
> *id-expression*.

That's great!!

> (FWIW, the paper is currently undergoing a slight rehault
> that would make `[]first` invalue - `[].first` would be the new proposed and
> *only* valid syntax, except when in the lexical context of a class, i.e., a
> member function).

what is the reason for the change? reserving the []<name> syntax for a
future named lambda proposal?

-- gpd

Xeo

unread,
May 29, 2013, 5:28:01 AM5/29/13
to std-pr...@isocpp.org, glme...@gmail.com
FWIW, the paper is currently undergoing a slight rehault that would make `[]first` invalue
 
Oh boy, so many typos in that sentence...

what is the reason for the change? reserving the []<name> syntax for a
future named lambda proposal?

Ah, I meant to add that this is for member access. The rules are currently as follows:

auto free = []foo; // always calls free function foo, unless in a member context
auto mem = [].foo; // always INVOKE semantics for members
void X::mem_fun(){
 
auto mem = []foo; // only finds members, with implicit `this` capturing
}

The implicit `this`-capturing may sound particularly strange. The reason for this is that I want to abide by a very simple rule wrt name-lookup

// the following two should be strictly equivalent in any context
foo
(args...);
[]foo(args...);

I.e. a direct invocation of a lifting lambda should be the same as directly invoking the id-expression.

thibau...@googlemail.com

unread,
Apr 10, 2014, 6:28:07 AM4/10/14
to std-pr...@isocpp.org, glme...@gmail.com
Hi all,

This is an old thread, but it is worth a bump since I had exactly the same idea, with a slightly different motivation.

To give you a concrete example of where this syntax might be really useful is to decouple type information and data layout when using arrays of type aggregate. 

For high performance computing, a really common trick is switching between array-of-structs and struct-of-arrays depending on your architecture. Ideally, one would like to handle the aggregate exactly as you would for a struct (ie by defining a collection of fields with a type and a name), but without committing to a particular layout. This would allow to implement abstraction layers where users give a loose description of their "objects", but the layout can be adapted by a library. This would be really useful for implementing databases for example.

Here is a simple example to illustrate what I would like to achieve:

// example of named template type definition
typedef Container <char R, char G, char B, char A> ImageType;

// instantiation: creates 800x600 elements, without knowing the memory layout, could be
// struct {char R, G, B, A; } image[800*600]; or
// struct {char R, G, B, A; } image[800][600]; or
// struct {char R[800*600], G[800*600], B[800*600], A[800*600]; } image;
// etc...
ImageType image({800,600});

// Getting and setting element works regardless of the layout using the field name
image
.get<ImageType::A>({0,0}) = 0;

It is possible to achieve this using variadic template/tuples, but the fields have to be addressed directly by index, which is simply unacceptable for a large code base, since the readability and maintainability is awful (think about accessing struct members by index instead of name!). Using enums to alias the indexes is a very short term solution with more inconvenients than benefits in then end. Using named parameters would not only improve readability but also facilitate aggregation of tagged tuples, ensure validity at compile time...

The solutions proposed here don't really solve this problem, either because the scope of the name is just wrong (Boost fusion), or because they are runtime solution (expensive, string comparisons...).

I compiled here possible implementations to achieve something close to that using the current standard; and a very simple draft proposal about how we could make this much simpler.

Any comment/input would be appreciated.

Thanks

TL

David Krauss

unread,
Apr 10, 2014, 8:37:38 PM4/10/14
to std-pr...@isocpp.org
On 2014–04–10, at 6:28 PM, thibau...@googlemail.com wrote:

It is possible to achieve this using variadic template/tuples, but the fields have to be addressed directly by index, which is simply unacceptable for a large code base, since the readability and maintainability is awful (think about accessing struct members by index instead of name!). Using enums to alias the indexes is a very short term solution with more inconvenients than benefits in then end.

Enum index types with overloading are the way I’ve solved this problem. The operator->* function has high precedence and it is intended to express member access. image->*channel can be defined for various types of high-level image type (naked C array or struct containing an array, std::vector, Eigen::Matrix). In my implementation, index selects and operator->* returns a channel object, so you have (image->*channel)[ index ]. This works because I exclusively used Eigen for interleaved representation and it supports array slicing. Alternately you could have e.g. enum red_index : size_t select a different overload than enum blue_index and have syntax (image->*blue_index)[ x, y ]. (This would work well for a bounded range of channels, whereas in my application I kept adding channels.) And don’t forget the possibility of mapping indexes to members using an array of PTMs, which allows image->*pixel_index{ channel::blue, x, y }.

The nice thing about these solutions is portability to any library-defined image type. Your proposal seems to only apply to your own types.

However, I should note that the specific syntax you mention is already very close to working already. Pointer-to-members already may be template nontype parameters, so you could specialize get just the same as if R, G, B, A were enumerators. You’re only missing the & in &ImageType::A. However, I don’t see why enumerators are less convenient than PTMs.

jeanberna...@gmail.com

unread,
May 12, 2014, 11:49:45 PM5/12/14
to std-pr...@isocpp.org
Hello guys

I think you might be interested by the way I've implemented it. It still fresh but looks like a nice start : https://github.com/duckie/named_tuple

Feel free to ask questions, the code is pretty simple.

germa...@gmail.com

unread,
May 16, 2014, 5:57:52 PM5/16/14
to std-pr...@isocpp.org
VERY nice with the current features in the lang. I just wonder about collisions in hashing. How is it solved?

germa...@gmail.com

unread,
May 16, 2014, 6:06:37 PM5/16/14
to std-pr...@isocpp.org
What about adding this to standard tuples? Can be done? I mean:

- generating a uint hash from operator "" _h
- make that available through get. Should already work?
- Overload make_tuple.

Would be great!

gmis...@gmail.com

unread,
May 16, 2014, 10:12:54 PM5/16/14
to std-pr...@isocpp.org, germa...@gmail.com
Hi
Can someone explain to me why I ever want a named tuple or any tuple really? Given I never use them I find it hard to see why I'd want one "standard" and when I have come across them in other peoples code I've always found replacing them with a structure worked better for me and even the original authors haven't changed them back again.

So can someone give me a real (as in useful) example of a tuple that would help me appreciate them? I'm sure they must be useful because everyone seems pretty fascinated with them so an example might help me here.

I've sometimes wished I could alias names for existing types, e.g. for std::pair where first and second weren't named first and second, but anything that takes a pair would take my aliased pair but that's something different I guess. e.g:

std::pair<int x, int y> x;
x.x or x.first
x.y or x.second

but even that sounded like a bad idea, they just shouldn't have used pair maybe in the original class.

Anyway, that's perhaps a tangent. Tuple help please :)

Thanks

Andrew Tomazos

unread,
May 17, 2014, 1:33:40 AM5/17/14
to std-pr...@isocpp.org
The proposal to add `std::tuple` is N1382:


I guess the Motivation section is what attempts to answer your question.


--

---
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.

gmis...@gmail.com

unread,
May 17, 2014, 6:06:22 AM5/17/14
to std-pr...@isocpp.org
Hi


On Saturday, May 17, 2014 5:33:40 PM UTC+12, Andrew Tomazos wrote:
The proposal to add `std::tuple` is N1382:


I guess the Motivation section is what attempts to answer your question.

I read the motivation part as suggested. I'm embarrassed to say I don't feel any wiser.

Germán Diago

unread,
May 17, 2014, 3:29:08 PM5/17/14
to gmis...@gmail.com, std-pr...@isocpp.org

I dnt want to create a struct for.each database query or for adhoc small data structures that I will use very few times, polluting my public interface with those structs, yet I want to use names to keep fields readable.

Shachar Shemesh

unread,
May 17, 2014, 3:37:19 PM5/17/14
to std-pr...@isocpp.org
On 17/05/14 13:06, gmis...@gmail.com wrote:
Hi

On Saturday, May 17, 2014 5:33:40 PM UTC+12, Andrew Tomazos wrote:
The proposal to add `std::tuple` is N1382:


I guess the Motivation section is what attempts to answer your question.

I read the motivation part as suggested. I'm embarrassed to say I don't feel any wiser.
 
The following represents my opinion:
This is a "me too" feature. In some languages (most listed in the motivation document mentioned above), tuples are the only way to pass, well, anything around. Function parameter list? A tuple. Multiple returns from a function? A tuple. Array containing more than one piece of data? An array of tuples.

This means that some people got used to working with tuples. You might think that this is not "the C++ way of doing things", and the only answer I can give you is that I agree with you.

Now, some use cases do lend themselves to something that structs do not easily provide. If you think of a function returning more than one result, it is not easy to define such a thing as an anonymous struct and then use it effectively. In that case, a tuple does comes in handy. I'm not sure it is the best solution to this problem, but it's one that exists today.

Shachar

Bengt Gustafsson

unread,
May 17, 2014, 5:29:21 PM5/17/14
to std-pr...@isocpp.org
Couldn't this be looked on the other way around:

instead of adding names to tuple elements to make the whole thing more struct-like you could just as well add some kind of indexing possibility to structs, to make them more tuple-like. At least as far as I have understood it the main advantage that tuples provide over structs is that you can index them with compile time constant indices, which is common in template metaprogramming.

One way of achieving this would be to define the get<ix>(v) function to work for any struct value v. As this is not implementable in library code (at least not before CTTI) this generates a messy state where get is defined in the library for tuples and in the language for structs. To avoid this it could be a better choice to define a builtin operator[constexpr i] for all structs. Unfortunately this clashes with any struct defining a operator[] itself which can be resolved by letting such a definition hide the built in one,

The worse problem is however to define what indexing pertains to and in which order, considering things like private members and inheritance. Maybe operator[] should only be defined for POD structs (or a similar subset where indexes are obvious).

Even with these limitations and shortcomings I think that adding indexing to structs is less strange than adding names to tuples, which would be quite a big syntax change to achieve something that is almost the same as the struct we had since C.

gmis...@gmail.com

unread,
May 17, 2014, 5:36:37 PM5/17/14
to std-pr...@isocpp.org, gmis...@gmail.com, germa...@gmail.com
Hi


On Sunday, May 18, 2014 7:29:08 AM UTC+12, Germán Diago wrote:

I dnt want to create a struct for.each database query or for adhoc small data structures that I will use very few times, polluting my public interface with those structs, yet I want to use names to keep fields readable.

Database querying is the one place where I perhaps have thought I wanted such a thing but invariably you want to use the type more / pass it on further than initially intended, and then you wish you'd just have used a struct in the first place. The same thing happens with pair really.

Wouldn't a namespace have worked here as a place to group the types? And don't you get stuck with a bunch of get<0> constants that you start to hate so want to name so end up with a namespace to put those named constants in anyway? I see the named tuple would avoid that part but then you're back to a struct feeling simpler again. Are you not?

gmis...@gmail.com

unread,
May 17, 2014, 6:53:38 PM5/17/14
to std-pr...@isocpp.org
Hi Bengt


On Sunday, May 18, 2014 9:29:21 AM UTC+12, Bengt Gustafsson wrote:
Couldn't this be looked on the other way around:

instead of adding names to tuple elements to make the whole thing more struct-like you could just as well add some kind of indexing possibility to structs, to make them more tuple-like. At least as far as I have understood it the main advantage that tuples provide over structs is that you can index them with compile time constant indices, which is common in template metaprogramming.

Funny, I was just mentally sketching out a thought about how to "label" any member of a struct with a number when you're post arrived. I think that  means I had the same thought as you here. It does indeed seem better to be able to number any struct member than to just name any tuple item. At least at first glance.

My concern is that just being able to query a tuple class by name isn't likely to provide enough support for the problems which tuples are being used to solve. But since I haven't seen any great example problems that require a tuple, it's hard for me to say that.

I want to say tuples aren't needed, but I'm conscious that that might just mean I'm just working in a space that doesn't need them so I shouldn't deny that to others that are. But my worry is that I still can't see a strong value for them if they just gained a name and I remain sceptical that just adding a name would be enough even for people who do use tuples for their problem, that it solve their problem that much better.

It seems we should have a clear understanding of real (not toy) examples where tuples provide a solution and then see if it's not better to go all out and provide full set of tools to solve those actual problems nicely and more fully and to prove if just adding a name is enough. I suspect it isn't. In which case don't add names or add more support would be the conclusion; or don't add support at all if the whole problem space is too narrow to warrant support.

But other proposals like reflection etc. might help in the tuple space without good examples of the problems tuples are being employed to solve we can't see where those proposals can come together for a better solution either.

As soon as you make tuple "easier" by offering a name, you open up more uses that demand more support. So it seems better to assume that and start with some good examples and then really go all out to find out where people are wanting to go with the capabilities proposed and see if the language needs either no support for tuple or a lot more support than just adding a name.

Having a get<n> that would work on any type seems a start in that direction here. Other random ideas:

void f()
{
[] person; // forward declare

void print_person( person& p) -> void
   // I wish an earlier proposal had got accepted to for local functions that looked like them and not lamda's.
{
    cout << name < " " << age << endl;
}

string name;
int age;

[s,x] as person; // lambda without da lamb. aka struct. with get<> support. aka tuple.
print_person(p)
}

Thanks!

G M

unread,
May 17, 2014, 8:12:30 PM5/17/14
to std-pr...@isocpp.org
Forget the example from that last post, I hit enter way too early.

But the basic idea was to imagine get<n> worked on any type as bengt first posted, then imagine if lambda captures could exist without their function and see how useful that is. Does that start to create a toolbox that compares to a tuple or solve any problems that tuple is being used for today; or is it useful in it's own right?

Bengt's comment about accessing any type by number at compile time then begs the opposite desire of getting at any member of any object at runtime by name

Forgive me if I'm wrong, but It seems to me this whole tuple thing is not really just about accessing an element by name or number at compile time, but it will be a lead in to a very database-like feature that might benefit from being examined in that context - of libraries like linkq etc., in memory databases etc, accessing an entity by name and number not just at compile time but at runtime; dynamic vs static and querying abilities to create shapes and types.

Language constructs that work together to allow that seem to be the eventual evolution destination and for solving those kinds of problems. Just adding a compile time name isn't doing much for us (at least me), I think.

People are going to want dynamic_tuple, hybrid_tuple and "just let my object behave like a tuple" and more creative ways of creating a tuple than just make tuple, e.g. select.

Is there any truth to this point of view?

Jean-Bernard Jansen

unread,
May 18, 2014, 4:47:00 PM5/18/14
to std-pr...@isocpp.org
Hello all

@GM I think your point of view is quite valuable. I am wondering about the way to implement runtime introspection over the existing compile time introspection. That would be nice but must be an opt-in since it has a cost over single structs or tuples.

@German Diago  Collisions are not a problem if you use the second method with the types. Collisions are NOT solved if you used the _h trick. I considered that attribute names in anonymous structures are often short and common, and collisions are unlikely. I cant find a way to cope with them, unless writing a test script that loops over a dictionary to find collisions in it, and in which you pick your attribute names from time to time. Its not ideal, but if you have any better idea about that, you are welcome.

For me, adding the indexing possibility to structs is not the same than adding the access by name on tuples. The difference resides in the fact that a struct have itself a name. If you define a second struct with the same attributes (name and types), they would not be copyable are movable between each other. In the "named tuples" version, they are, because the type is defined by its very content, and not by a name.




--

---
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/N-kIXNrkTUk/unsubscribe.
To unsubscribe from this group and all its topics, send an email to std-proposal...@isocpp.org.

Jean-Bernard Jansen

unread,
Jul 13, 2014, 11:34:58 AM7/13/14
to std-pr...@isocpp.org
Hello guys

This post will sure be of interest to @German Diago. I drastically improved my named tuple prototype since the last time. Changes includes:
- More generic API, easier to use.
- New internal storage method : base on a std::stuple instead of a recursive class hierachy, allowing for nice and fast interactions with std::tuple.
- The hash collision problem has been solved : if two members happen to have the same id, name or hash, the compilation will fail.
- Smaller code, easier to read.

Enjoy code and examples here: https://github.com/duckie/named_tuple.

Regards








Gian Lorenzo Meocci

unread,
Jul 13, 2014, 1:37:19 PM7/13/14
to std-pr...@isocpp.org
Thanks for your work!

german...@personifyinc.com

unread,
Jul 14, 2014, 9:22:41 AM7/14/14
to std-pr...@isocpp.org, jeanberna...@gmail.com
Great! Will take a look.

This post will sure be of interest to @German Diago. I drastically improved my named tuple prototype since the last time. Changes includes:
- More generic API, easier to use.
- New internal storage method : base on a std::stuple instead of a recursive class hierachy, allowing for nice and fast interactions with std::tuple.
- The hash collision problem has been solved : if two members happen to have the same id, name or hash, the compilation will fail.
- Smaller code, easier to read.





ath...@gmail.com

unread,
Jul 16, 2014, 4:57:24 PM7/16/14
to std-pr...@isocpp.org, glme...@gmail.com

On Thursday, April 10, 2014 6:28:07 AM UTC-4, Thibaut Lutz wrote:
Here is a simple example to illustrate what I would like to achieve:

// example of named template type definition
typedef Container <char R, char G, char B, char A> ImageType;

// instantiation: creates 800x600 elements, without knowing the memory layout, could be
// struct {char R, G, B, A; } image[800*600]; or
// struct {char R, G, B, A; } image[800][600]; or
// struct {char R[800*600], G[800*600], B[800*600], A[800*600]; } image;
// etc...
ImageType image({800,600});

// Getting and setting element works regardless of the layout using the field name
image
.get<ImageType::A>({0,0}) = 0;



I've been searching for the same thing when I came across this thread and this syntax looks pretty nice, although a sampling of others would be worth consideration as well. Any syntax would need to fit well with the existing constraints of C++, but the current std::tuple design specifying integers to access tuple indices really does result in unmaintainable code. I tried using them more extensively on my projects and from my experience it eventually created confusion for me and other developers, particularly when there are several fields of the same type. Some solution with better syntax and names for tuple indices would be ideal. 

Perhaps this would all become moot with compile time reflection? If metaprogramming could be performed on simple structs then perhaps the problem would be solved. On the other hand named tuples may still be valuable even with compile time reflection because it would be a good mechanism for[ inline object definition with named members.

Vicente J. Botet Escriba

unread,
May 9, 2015, 12:53:02 PM5/9/15
to std-pr...@isocpp.org
Le 13/07/14 17:34, Jean-Bernard Jansen a écrit :
Hello guys

This post will sure be of interest to @German Diago. I drastically improved my named tuple prototype since the last time. Changes includes:
- More generic API, easier to use.
- New internal storage method : base on a std::stuple instead of a recursive class hierachy, allowing for nice and fast interactions with std::tuple.
- The hash collision problem has been solved : if two members happen to have the same id, name or hash, the compilation will fail.
- Smaller code, easier to read.

Enjoy code and examples here: https://github.com/duckie/named_tuple.


Hi,

even if this is  an old post, I find this library quite interesting.

I like a lot
* the function syntax to identify the attribute holder int(age),

I like, but less
* the named parameter syntax _<name> = val
* the selector syntax tuple. _<name>

Just wondering if you could add in addition to attributes_holder, attributes_key. An attribute_key instance could have a name.
 E.g.

    constexpr struct name_t : attribute_key<name_t> {} name;


This could help to replace _<name>=val by  name=val.

template <class Id, class T>
auto attribute_key<Id>::operator=(attibute_key<Id>, T val) {return attribute_helper::_<Id>=val; }

The member _ could also be overloaded with attribute_key<name_t> tpl._(name).

template <class Id>
auto named_tuple::_(attibute_key<Id>) {return attribute_helper::_<Id>(); }

or the operator() tpl(name).

Alternatively an overload for the operator% on tuple and attribute_key<name> tpl%name.

template <class NamedTuple, class Id>
auto operator%(NamedTuple const& tpl, attibute_key<Id>) {return tpl._<Id>(); }

Last the attribute_key could be seen as a key selector function name(tml).

template <class NamedTuple>
auto attibute_key<Id>::operator()(NamedTuple const& tpl) {return tpl._<Id>(); }


The following example

namespace {
struct name;
struct age;
struct taille;
struct liste;
}

int main() {
  auto test = make_named_tuple(
      _<name>() = std::string("Roger")
      , _<age>() = 47
      , _<taille>() = 1.92
      , _<liste>() = std::vector<int>({1,2,3})
      );

  std::cout
    << test._<name>() << "\n"
    << test._<age>() << "\n"
    << test._<taille>() << "\n"
    << test._<liste>().size() << std::endl;


could be transformed using the new attribute_key as follows

namespace {
    constexpr struct name_t : attribute_key<name_t> {} name;
    constexpr struct age_t : attribute_key<age_t> {} age;
    constexpr struct taille_t : attribute_key<taille_t> {} taille;
    constexpr struct liste_t : attribute_key<liste_t> {} liste;
}

int main() {

// No need to use the artificial _<name> =, name =is enough

  auto test = make_named_tuple(
        name = std::string("Roger")
      , age = 47
      , taille = 1.92
      , liste = std::vector<int>({1,2,3})
      );

// However, here we need the type, name_t :(

  std::cout
    << test._<name_t>() << "\n"
    << test._<age_t>() << "\n"
    << test._<taille_t>() << "\n"
    << test._<liste_t>().size() << std::endl;

// making use of overloaded member _
  std::cout
    << test._(name) << "\n"
    << test._(age) << "\n"
    << test._(taille) << "\n"
    << test._(liste_t).size() << std::endl;

// making use of overloaded member operator()
  std::cout
    << test(name) << "\n"
    << test(age) << "\n"
    << test(taille) << "\n"
    << test(liste_t).size() << std::endl;

// Using the operator tuple % attribute_key

  std::cout
    << test%name << "\n"
    << test%age << "\n"
    << test%taille << "\n"
    << (test%liste_t).size() << std::endl;

// Using the attribute_key operator ()

  std::cout
    << name(test) << "\n"
    << age(test) << "\n"
    << taille(test) << "\n"
    << liste(test).size() << std::endl;

}

Vicente

Jean-Bernard Jansen

unread,
May 11, 2015, 6:47:53 AM5/11/15
to std-pr...@isocpp.org
Hello Vicente

Thank you very much for your interest about this work.

What you propose is actually doable, and is quite similar to what we can found in this project, which basically does the same thing. I am a little bit reluctant to add this feature for a very particular reason. Using static instances as you propose adds a problem : you have static symbols to manage, thus leading to potential conflicts between different compilation units. Those problems can be solved but extra care about them is needed. For instance, you used an anonymous namespace. It works, but duplicates every static instance in every compilation unit using them. This adds unused data in the resulting compiled code. When using declared (but NOT defined) types, no extra data is added.

The main point of my particular implementation is to avoid static instances as much as possible (cannot be avoided when you want to use runtime introspection, obviously). This allows to use a syntax with wich you dont even have to declare attribute names before using them.


auto test = make_named_tuple(
      _<"nom"_h>() = std::string("Roger")
      , _<"age"_h>() = 47
      , _<"taille"_h>() = 1.92
      , _<"liste"_h>() = std::vector<int>({1,2,3})
      );

What is your opinion on this point ? My mind is always open to changes, and I do like the proposal about using key selector function to access members. This really is elegant, I'll think about it.

Best regards
JB




--

Vicente J. Botet Escriba

unread,
May 11, 2015, 6:09:15 PM5/11/15
to std-pr...@isocpp.org
Le 11/05/15 12:47, Jean-Bernard Jansen a écrit :
Hello Vicente

Salut Jean-Bernard,

Thank you very much for your interest about this work.

What you propose is actually doable, and is quite similar to what we can found in this project, which basically does the same thing.
A quick look let me think that there are a lot of macros there :(

I am a little bit reluctant to add this feature for a very particular reason. Using static instances as you propose adds a problem : you have static symbols to manage, thus leading to potential conflicts between different compilation units. Those problems can be solved but extra care about them is needed. For instance, you used an anonymous namespace. It works, but duplicates every static instance in every compilation unit using them. This adds unused data in the resulting compiled code. When using declared (but NOT defined) types, no extra data is added.
The attribute_key class template has no data, so it has the same issues that we have with nullopt, in_place, ..... There is a ODR issue, but we can live with that, as soon as we don't use the address of these objects.
Note that I included them in an anonymous namespace as it was just an example. I believe the users have other possibilities and the standard doesn't need to solve this problem for them.


The main point of my particular implementation is to avoid static instances as much as possible (cannot be avoided when you want to use runtime introspection, obviously). This allows to use a syntax with wich you dont even have to declare attribute names before using them.


auto test = make_named_tuple(
      _<"nom"_h>() = std::string("Roger")
      , _<"age"_h>() = 47
      , _<"taille"_h>() = 1.92
      , _<"liste"_h>() = std::vector<int>({1,2,3})
      );

What is your opinion on this point ?
I see the advantage but I would prefer to have less noise

_<"nom"_h>()
   nom

even if this would require a declaration. Anyway the attribute key is no more than syntactic sugar, and with the exception of the syntax test._(name) and test(name) all the others could be added in a non-intrusive way by the user on top of your proposal. If the fuction call unification is adopted the function call name(test) could be used also as test.name().
My mind is always open to changes, and I do like the proposal about using key selector function to access members. This really is elegant, I'll think about it.


BTW, if tagged types were adopted in the standard, it would be nice to use them also with std::experimental::any and std::experimental::variant. What do you think of this possibility?

Vicente

P.S. What prevents to extend std::tuple with tagged types? Have you found any backward compatibility issues or any lost of efficiency?

P.S..S Do you plan a have a first proposal for the next meeting?

Jean-Bernard Jansen

unread,
May 11, 2015, 7:27:36 PM5/11/15
to std-pr...@isocpp.org
Hi again :)

Yes, the IOD project uses macros, and I dont like it either.

And yes, we could add what you propose just on top of what already exists : this convinces me to give it a try very soon.

As for tagged types into the standard, I am convinced they will make their way through one day or antoher. Tuple addressing via type was the first baby step toward it. Yes I would gladly see them well integrated with std::exprimental::any and std::experimental::variant. And to be fully honest, I just see them as a step toward anonymous structures : a type described by its members' names and types, but not by its own name. It would spare the developpers a lot of conversion code to make libraries from different sources interoperable. Thats what I had in mind while working on the named tuple.

I dont think anything prevents from extending std::tuple with tagged types. I did not use inheritance because the first implementation was not based on std::tuple.. But now that you state it, it makes sense.

I did not plan to write a proposal for the next meeting, but I can spare time to. I am not used to the exercise though, any advice on the matter will be appreciated.

Thank you again for your toughts.

JB


--

Vicente J. Botet Escriba

unread,
May 13, 2015, 1:00:19 PM5/13/15
to std-pr...@isocpp.org
Le 12/05/15 01:27, Jean-Bernard Jansen a écrit :
Hi again :)

Yes, the IOD project uses macros, and I dont like it either.

And yes, we could add what you propose just on top of what already exists : this convinces me to give it a try very soon.

As for tagged types into the standard, I am convinced they will make their way through one day or antoher. Tuple addressing via type was the first baby step toward it. Yes I would gladly see them well integrated with std::exprimental::any and std::experimental::variant. And to be fully honest, I just see them as a step toward anonymous structures : a type described by its members' names and types, but not by its own name.
This exist already.
struct {
    T1 n1;
    T2 n2;
}

I believe that what your are talking off is the ability to built them at compile time.
It would spare the developpers a lot of conversion code to make libraries from different sources interoperable. Thats what I had in mind while working on the named tuple.
I agree that this will be really a good thing, but I'm not a compiler guy, so I couldn't prototype anything. Recently there were some that were talking about introducing discriminated unions in C++. It would be great if we were also able to built these discriminated unions at compile time.

Waiting for that, we can take see what can be done in C++14/17 with a library solution, even if the solution couldn't be as elegant as if it was integrated on the language.


I dont think anything prevents from extending std::tuple with tagged types. I did not use inheritance because the first implementation was not based on std::tuple.. But now that you state it, it makes sense.

I was not thinking about using inheritance, but to re-define std::tuple in std::experimental::tuple extending it to take in account named/tag types.

I did not plan to write a proposal for the next meeting, but I can spare time to. I am not used to the exercise though, any advice on the matter will be appreciated.

Let see this point off line then.

Vicente
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.
Reply all
Reply to author
Forward
0 new messages