Reasoning about names

555 views
Skip to first unread message

Roland Bock

unread,
Mar 23, 2015, 1:09:41 PM3/23/15
to std-pr...@isocpp.org
Hi,

here is an idea I first talked about in a workshop at CppCon 2014 and
recently at the Munich C++ Meetup:


In templates we can use types and values as parameters. We cannot
specify names though.

But wouldn't it be cool to be able to parametrize templates with names? E.g.

// ------------------------
template<typename Type,
name Name, // New template parameter type
Type Value>
struct Foo
{
Type Name = Value;
};
// ------------------------


Ok, this may look boring, so how about a tuple with named members?


// ------------------------
template<typename... T>
struct named_tuple
{
typename T::type T::name;... // New expansion style
};
// ------------------------

It could be used with

// ------------------------
template<typename T, name X>
struct type_name
{
using type = T;
using name = X;
};

using my_struct =
named_tuple
<
type_name<int, `foo`>,
type_name<char, `bar`>
>;

auto ms = my_struct{7, 'c'};
ms.foo = 9;
// ------------------------


This is a much nicer user interface, when you are not targeting TMP. But
such a named tuple could (of course) also be used like a std::tuple,
too. For instance, std::get wouldn't be hard to specialize for a named
tuple.


With these mechanisms, it would also be possible to replace CRTP with
mixins in some scenarios.



I posted my ideas/use-cases in more detail here:

http://cpp.eudoxos.de/dreaming-of-names/


Please let me know what you think!


Regards,

Roland

gmis...@gmail.com

unread,
Mar 23, 2015, 3:35:57 PM3/23/15
to std-pr...@isocpp.org
Hi

I think a feature like this would be great.
I don't know if the feature should take that form though, but that's to be explored.
The solution may be related to work meta data task group are doing I wonder.

I'm sure this has come up before somewhere, but I would like to be able to make existing code more readable.
I would like to provide the ability for a class like pair, to be able to alias first_name with first and last_name with second.
If no name is given, pair should work as normal.

std::pair<string first_name, string last_name> name{"bob", "jones"};
printf("Hello %s %s\n", name.first_name.c_str(), name.second_name.c_str());

But mostly I wish for a feature that would make people use pair and tuple a lot less, that's what I really want!

Douglas Boffey

unread,
Mar 23, 2015, 4:04:47 PM3/23/15
to std-pr...@isocpp.org
I, personally, don't like the idea of quoted strings used for
identifiers, but, I suppose, that's just a bikeshed.
> --
>
> ---
> 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.
> Visit this group at
> http://groups.google.com/a/isocpp.org/group/std-proposals/.
>

gmis...@gmail.com

unread,
Mar 23, 2015, 4:57:55 PM3/23/15
to std-pr...@isocpp.org

On Tuesday, March 24, 2015 at 9:04:47 AM UTC+13, Douglas Boffey wrote:
I, personally, don't like the idea of quoted strings used for
identifiers, but, I suppose, that's just a bikeshed.

I don't know if you were referring to the OP or me with that comment but I agree with you and am not suggesting quoted strings.

Douglas Boffey

unread,
Mar 23, 2015, 5:43:30 PM3/23/15
to std-pr...@isocpp.org
It was in reference to the OP.

On 3/23/15, gmis...@gmail.com <gmis...@gmail.com> wrote:
>

Roland Bock

unread,
Mar 23, 2015, 6:02:49 PM3/23/15
to std-pr...@isocpp.org
You're referring to something like

type_name<int, `foo`>

?

I haven't set my mind on the syntax yet. I used forward ticks since they
aren't used in C++ yet. If that's the only question you have, go ahead
bikeshedding :-)

Here are some options I thought of

type_name<int, `foo`>

type_name<int, $foo> // $ is already used by MSVC

type_name<int, #foo> // Might confuse preprocessor

type_name<int, @foo> // OK?


Personally, I like the forward ticks best, but @foo looks good to me,
too, and I am sure there will be more suggestions :-)

Vicente J. Botet Escriba

unread,
Mar 24, 2015, 1:34:14 AM3/24/15
to std-pr...@isocpp.org
Le 23/03/15 18:09, Roland Bock a écrit :
Hi,

here is an idea I first talked about in a workshop at CppCon 2014 and
recently at the Munich C++ Meetup:


In templates we can use types and values as parameters. We cannot
specify names though.

But wouldn't it be cool to be able to parametrize templates with names? E.g.

// ------------------------
template<typename Type, 
         name Name, // New template parameter type
         Type Value>
struct Foo
{
  Type Name = Value;
};
// ------------------------


Ok, this may look boring, so how about a tuple with named members?

I'm all for been able to do something like that.
An alternative could be to be able to define some kind of data members indexed by a type (something related to template variables instances).

template<typename Type, 
         typename Name, // New template parameter type
         Type Value>
struct Foo
{
  Type _<Name> = Value;
};

The use of _ is intended, meaning the data member name is not significant.




// ------------------------
template<typename... T>
struct named_tuple
{
  typename T::type T::name;... // New expansion style
};

template<typename... T>
struct named_tuple
{
  typename T::type _<T::name>;... // New expansion style
};
// ------------------------

It could be used with

// ------------------------
template<typename T, name X>
struct type_name
{
  using type = T;
  using name = X;
};

using my_struct = 
  named_tuple
  <
    type_name<int, `foo`>,
    type_name<char, `bar`>
  >;

auto ms = my_struct{7, 'c'};
ms.foo = 9;
// ------------------------

// ------------------------
template<typename T, typename X>
struct type_name
{
  using type = T;
  using name = X;
};

struct foo {};
struct bar {};

using my_struct = 
  named_tuple
  <
    type_name<int, foo>,
    type_name<char, bar>
  >;

auto ms = my_struct{7, 'c'};
ms._<foo> = 9;

The interface is not so nice, it needs to declare the structs foo and bar and use ms._<foo> instead of ms.foo.

However it doesn't need to introduce a new name keyword and a new kind of symbols.

Another limitation of my alternative is that it doesn't work well with struct. Your example for StructOfArrays would need a struct S indexed by types

struct S {
    int _<a>;
    int _<b>;
    int _<c>;
};

template<typename S,
         typename... Ms>
struct StructOfArrays
{
   std::vector<Ms::type> _<Ms::name>;... // the vectors
 
   // example part of the SOA interface
   void push_back(const S& _soa_arg)
   {
     // Assuming N4191, Fold expressions
     (_<Ms::name>.push_back(_soa_arg._<Ms::name>,...);
   }
};


In some way we can say that your new name is some kind of syntactic sugar of my alternative

`foo`

will expand to

__name::foo

where

namespapce __name {
    struct foo {};   
}


template < typename Type, name Name>
struct X {
    Type Name;
};

to

template < typename Type, typename Name>
struct X {
    Type _<Name>;
};

and
ms.foo

to

ms._<__name::foo>

We will need also that the struct

struct S {
    int a;
    int b;
    int c;
};

should be equivalent to

struct S {
    int _<__name::a>;
    int _<__name::b>;
    int _<__name::c>;
};


and that

struct S {
    int _<__name::a>;
    int a;
};

would report a compile error.

I would try to translate the mixin example:

template <typename Derived>
struct mixin
{
  using name = __name::check; // name of the mixin // [1]
  Derived* _derived;

  template <typename T>
  bool operator()(const T& t) const // mixin is callable
  {
    return _derived->_data == t;
  }
};

template<template< class> class ... Mixin> // Made this variadic for fun
struct MyClass
{
  Mixin<MyClass> _<Mixin<MyClass>::name> = {this};... \\ [2]
  int _data = 7;
};

In [1] declare name as been just a tag type.
In [2] we use the Mixin<D>::name type as template of the data member indexed variable _. 


Resuming the two approaches could be seen as two sides of the same coin. Your side gives a more friendly syntax, mine is closer to the current language semantic once extended with member data indexed by types, of course.

Vicente

Roland Bock

unread,
Mar 24, 2015, 1:54:15 AM3/24/15
to std-pr...@isocpp.org
On 2015-03-23 20:35, gmis...@gmail.com wrote:
> Hi
>
> I think a feature like this would be great.

:-)

> I don't know if the feature should take that form though, but that's
> to be explored.
> The solution may be related to work meta data task group are doing I
> wonder.
Can you give me a link?

>
> I'm sure this has come up before somewhere, but I would like to be
> able to make existing code more readable.
> I would like to provide the ability for a class like pair, to be able
> to alias first_name with first and last_name with second.
> If no name is given, pair should work as normal.
>
> std::pair<string first_name, string last_name> name{"bob", "jones"};
> printf("Hello %s %s\n", name.first_name.c_str(),
> name.second_name.c_str());
>

The pair example is classic, of course. The syntax you are suggesting is
quite nice for pair and tuple, but how would that pan out with the
CRTP/mixin example?

> But mostly I wish for a feature that would make people use pair and
> tuple a lot less, that's what I really want!

We're almost on the same page here. Tuples are great for generic
programming but not necessarily for library users. Tuples with named
members can serve both worlds, though. Ease of use in the generic world
and ease of use for library users due to appropriately named members.

Best,

Roland

Roland Bock

unread,
Mar 24, 2015, 5:53:32 AM3/24/15
to std-pr...@isocpp.org
On 2015-03-24 06:34, Vicente J. Botet Escriba wrote:
Le 23/03/15 18:09, Roland Bock a écrit :
Hi,

here is an idea I first talked about in a workshop at CppCon 2014 and
recently at the Munich C++ Meetup:


In templates we can use types and values as parameters. We cannot
specify names though.

But wouldn't it be cool to be able to parametrize templates with names? E.g.

// ------------------------
template<typename Type, 
         name Name, // New template parameter type
         Type Value>
struct Foo
{
  Type Name = Value;
};
// ------------------------


Ok, this may look boring, so how about a tuple with named members?

I'm all for been able to do something like that.

:-)


An alternative could be to be able to define some kind of data members indexed by a type (something related to template variables instances).

template<typename Type, 
         typename Name, // New template parameter type
         Type Value>
struct Foo
{
  Type _<Name> = Value;
};

The use of _ is intended, meaning the data member name is not significant.

Ah, ok, it took a while for me to grok that. So _<some_type> would be a non-static template variable, right?

I can't tell what would be a greater change to the language, but as you mention yourself, ease of use would be better if we really had names.


// ------------------------
template<typename T, typename X>
struct type_name
{
  using type = T;
  using name = X;
};

struct foo {};
struct bar {};

using my_struct = 
  named_tuple
  <
    type_name<int, foo>,
    type_name<char, bar>
  >;

auto ms = my_struct{7, 'c'};
ms._<foo> = 9;

The interface is not so nice, it needs to declare the structs foo and bar and use ms._<foo> instead of ms.foo.

Here's how I would like to use the feature in sqlpp one day:

a) defining tables:

using TabFoo = sqlpp::table<`tab_foo`,
                            int, `id`,
                            string, `name`
                            bool, `likesCpp`>;

(slightly simplified, in reality there would be traits like can_be_null or has_default to be set, too)

b) using tables in code:

auto left = TabFoo{}.as<`left`>;
auto right = TabFoo{}.as<`right`>

for (row : db(select(left.id, right.id.as<`partnerId`>)
              .from(left, right)
              .where(left.id > right.id)))
{
   std::cout << row.id << "," << row.partnerId << '\n';
}

Much of the elegance would be lost if I had to create an extra type for each name.


Another really important feature is the ability to compare names.

template<name X>
struct N{};

struct Foo

{
    using name = `dilbert`;
};
struct Bar
{
    using name = `dilbert`;
};

static_assert(std::is_same<N<Foo::name>,
                           N<Bar::name>>::value, "");

If you wanted to achieve the same with types, then you would have some project wide namespace for all the name-representing types which might be quite awkward, IMHO.



However it doesn't need to introduce a new name keyword and a new kind of symbols.

Another limitation of my alternative is that it doesn't work well with struct. Your example for StructOfArrays would need a struct S indexed by types

[...]


In some way we can say that your new name is some kind of syntactic sugar of my alternative

`foo`

will expand to

__name::foo

where

namespapce __name {
    struct foo {};   
}


template < typename Type, name Name>
struct X {
    Type Name;
};

to

template < typename Type, typename Name>
struct X {
    Type _<Name>;
};

and
ms.foo

to

ms._<__name::foo>

Comparing ms.foo to ms._<__name::foo> I know what I'd prefer :-)



We will need also that the struct

struct S {
    int a;
    int b;
    int c;
};

should be equivalent to

struct S {
    int _<__name::a>;
    int _<__name::b>;
    int _<__name::c>;
};

This seems to be a tough requirement. Wouldn't that mean that all structs with members of the same type have to be equivalent? Or would just these non-static template variables have some special ability to make structs equivalent?

How about

struct S
{
   int a;
   int b;
};

struct T
{
  
int _<__name::a>;
   int b;
};

Wouldd they also be equivalent?


and that

struct S {
    int _<__name::a>;
    int a;
};

would report a compile error.

How about

struct S {
  int
_<__global_names::a>;
  int
_<__local_names::a>;
};

I would have assumed from your earlier examples, that this would work fine. But if the above should give a compile error, then this should also not compile.



I would try to translate the mixin example:

template <typename Derived>
struct mixin
{
  using name = __name::check; // name of the mixin // [1]
  Derived* _derived;

  template <typename T>
  bool operator()(const T& t) const // mixin is callable
  {
    return _derived->_data == t;
  }
};

template<template< class> class ... Mixin> // Made this variadic for fun
struct MyClass
{
  Mixin<MyClass> _<Mixin<MyClass>::name> = {this};... \\ [2]
  int _data = 7;
};

In [1] declare name as been just a tag type.
In [2] we use the Mixin<D>::name type as template of the data member indexed variable _. 


Resuming the two approaches could be seen as two sides of the same coin. Your side gives a more friendly syntax, mine is closer to the current language semantic once extended with member data indexed by types, of course.

I really appreciate the effort to mimic the names with the non-static template member variables. It is a neat idea on its own. I do believe though that the idea of equivalence of structs as described above adds quite a bit of complexity.

My "names" idea obviously introduces
  • a new keyword, although I guess that could be circumvented since it is "only" used in the template list. We could probably reuse some existing keyword.
  • some way of specifying names

I understand that especially the latter part is a major change. Ease of use for library-developers and their users on the other hand is one of the major goals of the "names" idea.


Cheers,

Roland

Vicente J. Botet Escriba

unread,
Mar 24, 2015, 2:47:58 PM3/24/15
to std-pr...@isocpp.org
Le 24/03/15 10:53, Roland Bock a écrit :
On 2015-03-24 06:34, Vicente J. Botet Escriba wrote:
Le 23/03/15 18:09, Roland Bock a écrit :
In templates we can use types and values as parameters. We cannot
specify names though.

But wouldn't it be cool to be able to parametrize templates with names? E.g.

// ------------------------
template<typename Type, 
         name Name, // New template parameter type
         Type Value>
struct Foo
{
  Type Name = Value;
};
// ------------------------
   
An alternative could be to be able to define some kind of data members indexed by a type (something related to template variables instances).


template<typename Type, 
         typename Name, // New template parameter type
         Type Value>
struct Foo
{
  Type _<Name> = Value;
};

The use of _ is intended, meaning the data member name is not significant.

Ah, ok, it took a while for me to grok that. So _<some_type> would be a non-static template variable, right?

I can't tell what would be a greater change to the language, but as you mention yourself, ease of use would be better if we really had names.
I'm not a compiler implementer, but I suspect that easy to implement would be an important factor.
Agreed, however I have not said that you must define them. These types could be generated by the compiler, that is be just like your names `foo`.



Another really important feature is the ability to compare names.

template<name X>
struct N{};

struct Foo

{
    using name = `dilbert`;
};
struct Bar
{
    using name = `dilbert`;
};

static_assert(std::is_same<N<Foo::name>,
                           N<Bar::name>>::value, "");

If you wanted to achieve the same with types, then you would have some project wide namespace for all the name-representing types which might be quite awkward, IMHO.
Right, it is the namespace __name. This IMO is an implementation detail.

However it doesn't need to introduce a new name keyword and a new kind of symbols.

Another limitation of my alternative is that it doesn't work well with struct. Your example for StructOfArrays would need a struct S indexed by types

[...]

In some way we can say that your new name is some kind of syntactic sugar of my alternative


and
ms.foo

to

ms._<__name::foo>

Comparing ms.foo to ms._<__name::foo> I know what I'd prefer :-)
Me too. I want just to remove the need for a new kind of "Thing". What I mean is that both could be made equivalent.



We will need also that the struct

struct S {
    int a;
    int b;
    int c;
};

should be equivalent to

struct S {
    int _<__name::a>;
    int _<__name::b>;
    int _<__name::c>;
};

This seems to be a tough requirement. Wouldn't that mean that all structs with members of the same type have to be equivalent? Or would just these non-static template variables have some special ability to make structs equivalent?
The last.


How about

struct S
{
   int a;
   int b;
};

struct T
{
  
int _<__name::a>;
   int b;
};

Wouldd they also be equivalent?
I would say yes, but I have not think enough about the implications. Do you see a problem?


and that

struct S {
    int _<__name::a>;
    int a;
};

would report a compile error.

How about

struct S {
  int
_<__global_names::a>;
  int
_<__local_names::a>;
};

I don't see the need for global or local namespace for the types representing symbols (names). All of them could/should be on the same __name namespace.
I would have assumed from your earlier examples, that this would work fine. But if the above should give a compile error, then this should also not compile.
Resuming the two approaches could be seen as two sides of the same coin. Your side gives a more friendly syntax, mine is closer to the current language semantic once extended with member data indexed by types, of course.

I really appreciate the effort to mimic the names with the non-static template member variables. It is a neat idea on its own. I do believe though that the idea of equivalence of structs as described above adds quite a bit of complexity.
Can you elaborate?


My "names" idea obviously introduces
  • a new keyword, although I guess that could be circumvented since it is "only" used in the template list. We could probably reuse some existing keyword.
  • some way of specifying names

I understand that especially the latter part is a major change. Ease of use for library-developers and their users on the other hand is one of the major goals of the "names" idea.

Hopping the transformation I have described could make it more probable.
Vicente

Roland Bock

unread,
Mar 24, 2015, 5:23:49 PM3/24/15
to std-pr...@isocpp.org
On 2015-03-24 19:47, Vicente J. Botet Escriba wrote:
Le 24/03/15 10:53, Roland Bock a écrit :
On 2015-03-24 06:34, Vicente J. Botet Escriba wrote:
Le 23/03/15 18:09, Roland Bock a écrit :
In templates we can use types and values as parameters. We cannot
specify names though.

But wouldn't it be cool to be able to parametrize templates with names? E.g.

// ------------------------
template<typename Type, 
         name Name, // New template parameter type
         Type Value>
struct Foo
{
  Type Name = Value;
};
// ------------------------
   
An alternative could be to be able to define some kind of data members indexed by a type (something related to template variables instances).

template<typename Type, 
         typename Name, // New template parameter type
         Type Value>
struct Foo
{
  Type _<Name> = Value;
};

The use of _ is intended, meaning the data member name is not significant.

Ah, ok, it took a while for me to grok that. So _<some_type> would be a non-static template variable, right?

I can't tell what would be a greater change to the language, but as you mention yourself, ease of use would be better if we really had names.
I'm not a compiler implementer, but I suspect that easy to implement would be an important factor.

You are certainly right that the effort of implementing this would be a factor. Since I am also not a compiler developer, I have no idea about the effort of either approach :-)



using TabFoo = sqlpp::table<`tab_foo`,
                            int, `id`,
                            string, `name`
                            bool, `likesCpp`>;

(slightly simplified, in reality there would be traits like can_be_null or has_default to be set, too)

b) using tables in code:

auto left = TabFoo{}.as<`left`>;
auto right = TabFoo{}.as<`right`>

for (row : db(select(left.id, right.id.as<`partnerId`>)
              .from(left, right)
              .where(left.id > right.id)))
{
   std::cout << row.id << "," << row.partnerId << '\n';
}

Much of the elegance would be lost if I had to create an extra type for each name.
Agreed, however I have not said that you must define them. These types could be generated by the compiler, that is be just like your names `foo`.
Then I missed something. I thought you're appoach would require something like


using TabFoo = sqlpp::table<`tab_foo`,
                            int, __name::id,
                            string, __name::name
                            bool, __name::likesCpp>;

If the compiler can generate these, then __name would be a special namespace extra for this purpose?






Another really important feature is the ability to compare names.

template<name X>
struct N{};

struct Foo

{
    using name = `dilbert`;
};
struct Bar
{
    using name = `dilbert`;
};

static_assert(std::is_same<N<Foo::name>,
                           N<Bar::name>>::value, "");

If you wanted to achieve the same with types, then you would have some project wide namespace for all the name-representing types which might be quite awkward, IMHO.
Right, it is the namespace __name. This IMO is an implementation detail.

Ok, I think I got your idea now.



However it doesn't need to introduce a new name keyword and a new kind of symbols.

Another limitation of my alternative is that it doesn't work well with struct. Your example for StructOfArrays would need a struct S indexed by types

[...]

In some way we can say that your new name is some kind of syntactic sugar of my alternative


and
ms.foo

to

ms._<__name::foo>

Comparing ms.foo to ms._<__name::foo> I know what I'd prefer :-)
Me too. I want just to remove the need for a new kind of "Thing". What I mean is that both could be made equivalent.
Well, you have two new things if I am not mistaken:

a) a special namespace that lets the compiler generate types automtically just by mentioning them
b) a non-static template member variable




We will need also that the struct

struct S {
    int a;
    int b;
    int c;
};

should be equivalent to

struct S {
    int _<__name::a>;
    int _<__name::b>;
    int _<__name::c>;
};

This seems to be a tough requirement. Wouldn't that mean that all structs with members of the same type have to be equivalent? Or would just these non-static template variables have some special ability to make structs equivalent?
The last.
This is the third new thing: The non-static template member variables have an ability unlike anything else. They can turn unrelated structs into equivalents.

This is the fourth new thing: Equivalence. Obviously the two structs above are not the same. Their members have totally different names. But maybe they can be converted to each other with an automatically generated conversion operator as soon as there are these special members?



How about

struct S
{
   int a;
   int b;
};

struct T
{
  
int _<__name::a>;
   int b;
};

Wouldd they also be equivalent?
I would say yes, but I have not think enough about the implications. Do you see a problem?
Seems to me that this would imply that

struct S { int b };
struct T { int b };

would be equivalent then too. But that is probably wrong. Please ignore.



and that

struct S {
    int _<__name::a>;
    int a;
};

would report a compile error.

How about

struct S {
  int
_<__global_names::a>;
  int
_<__local_names::a>;
};

I don't see the need for global or local namespace for the types representing symbols (names). All of them could/should be on the same __name namespace.

Got that.


I would have assumed from your earlier examples, that this would work fine. But if the above should give a compile error, then this should also not compile.

Resuming the two approaches could be seen as two sides of the same coin. Your side gives a more friendly syntax, mine is closer to the current language semantic once extended with member data indexed by types, of course.

I really appreciate the effort to mimic the names with the non-static template member variables. It is a neat idea on its own. I do believe though that the idea of equivalence of structs as described above adds quite a bit of complexity.
Can you elaborate?
As described above, equivalence seems to be kind of a new concept. I might very well still be misunderstanding something, but I see the following new things in total:

a) a special namespace that lets the compiler generate types automtically just by mentioning them
b) a non-static template member variable
c) The non-static template member variables can turn unrelated structs into equivalents
d) equivalent structs can be converted into each by default (not need to write a conversion operator)

These do not seem like small changes to me either.
Sorry, if I misinterpreted something.



My "names" idea obviously introduces
  • a new keyword, although I guess that could be circumvented since it is "only" used in the template list. We could probably reuse some existing keyword.
  • some way of specifying names

I understand that especially the latter part is a major change. Ease of use for library-developers and their users on the other hand is one of the major goals of the "names" idea.

Hopping the transformation I have described could make it more probable.

Yes, I got that and I am thankful for the discussion!

On the other hand, reflection has to deal with names, too. So maybe names aren't such a huge thing to begin with.

Regards,

Roland

Vicente J. Botet Escriba

unread,
Mar 24, 2015, 11:39:38 PM3/24/15
to std-pr...@isocpp.org
Le 24/03/15 22:23, Roland Bock a écrit :
My bad. I didn't thought too much about that. I don't want to introduce any equivalence. Let say that _<_name::a> is a way to name the data member a.

Declaring

struct S {
    int a;
};
struct S {
    int _<__name::a>;
};

would result on a compile error as S is declared twice.


I would have assumed from your earlier examples, that this would work fine. But if the above should give a compile error, then this should also not compile.

Resuming the two approaches could be seen as two sides of the same coin. Your side gives a more friendly syntax, mine is closer to the current language semantic once extended with member data indexed by types, of course.

I really appreciate the effort to mimic the names with the non-static template member variables. It is a neat idea on its own. I do believe though that the idea of equivalence of structs as described above adds quite a bit of complexity.
Can you elaborate?
As described above, equivalence seems to be kind of a new concept. I might very well still be misunderstanding something, but I see the following new things in total:

a) a special namespace that lets the compiler generate types automtically just by mentioning them
granted

b) a non-static template member variable
A generator for a member variable names from a special kind of type would define it better. This is already a major change.

c) The non-static template member variables can turn unrelated structs into equivalents
I don't think we need the equivalence relation.

d) equivalent structs can be converted into each by default (not need to write a conversion operator)
I don't think this is needed.


These do not seem like small changes to me either.
Granted, even if the equivalence relation is not needed.

Sorry, if I misinterpreted something.



My "names" idea obviously introduces
  • a new keyword, although I guess that could be circumvented since it is "only" used in the template list. We could probably reuse some existing keyword.
  • some way of specifying names

I understand that especially the latter part is a major change. Ease of use for library-developers and their users on the other hand is one of the major goals of the "names" idea.

Hopping the transformation I have described could make it more probable.

Yes, I got that and I am thankful for the discussion!

Just I want to say that the alternative I've proposed is not better than yours. It was in some way another way to say the same thing and it is not simpler. At the semantic level there is not a big difference and yours is much user friendly. So go ahead
Sorry for the distraction.


On the other hand, reflection has to deal with names, too. So maybe names aren't such a huge thing to begin with.


Sure, having a way to generate/refer to data member names would help any reflection library.

Vicente


Roland Bock

unread,
Mar 25, 2015, 2:28:57 AM3/25/15
to std-pr...@isocpp.org
On 2015-03-25 04:39, Vicente J. Botet Escriba wrote:
My bad. I didn't thought too much about that. I don't want to introduce any equivalence. Let say that _<_name::a> is a way to name the data member a.

Declaring

struct S {
    int a;
};
struct S {
    int _<__name::a>;
};

would result on a compile error as S is declared twice.

Got it. So basically it would be difference in notation:

My proposal is

template<name X>
struct T
{
   int X;
};
auto t = T<`sample`>{7};
t.sample = 42;

This would require
  • name literals
  • name keyword in templates (or re-using an existing one)


You're suggesting an alternative way by saying


template<typename X>
struct T
{
    int _<X>;
};
auto t = T<__name::sample>{};
assert(&t._<__name::sample> == &t.sample);

This would require
  • A special namespace, e.g. __name, which has the feature that typenames inside this namespace can be used in
  • a special construct that is interpreted like a member name


So the approaches are almost the same:

  • a name keyword in the template parameter list vs. a special __name namespace
  • a name literal vs a special construct that iterprets types from the special namespace

They would even work the same for something I haven't mentioned yet, but came up quickly in the discussion at CppCon: concatenation of names

In order to avoid name clashes in the constructor, concatenation might be helpful:

template<name X>
struct T
{
   int X;
   T(int X`arg`):X(X`arg`){}
};

This ensures that the argument name in the constructor can never conflict with X.

Same would work for your approach if the naming construct were variadic:

template<typename X>
struct T
{
   int _<X>;
   T(int _<X, __name::_arg>):X(
_<X, __name::_arg>){}
};


Hopping the transformation I have described could make it more probable.

Yes, I got that and I am thankful for the discussion!

Just I want to say that the alternative I've proposed is not better than yours. It was in some way another way to say the same thing and it is not simpler. At the semantic level there is not a big difference and yours is much user friendly. So go ahead
Sorry for the distraction.

No worries, it was quite interesting to see the type idea develop :-)

Best,

Roland

Matthew Woehlke

unread,
Mar 25, 2015, 11:06:05 AM3/25/15
to std-pr...@isocpp.org
On 2015-03-24 17:23, Roland Bock wrote:
> This is the third new thing: The non-static template member variables
> have an ability unlike anything else. They can turn unrelated structs
> into equivalents.
>
> This is the fourth new thing: Equivalence. Obviously the two structs
> above are not the same. Their members have totally different names. But
> maybe they can be converted to each other with an automatically
> generated conversion operator as soon as there are these special members?

I was thinking about this some, and how my "optimal outcome" of such a
feature would be for the std::map::iterator::operator->() type to
*finally* have members "key" and "value" instead of the obnoxious
"first" and "second". (Well, "in addition to", technically, since of
course we need to be backwards compatible.)

For compatibility, this would also need to implicitly convert to - or
more likely, be polymorphic with - a std::pair with the "usual" names.
(I think this is possible by subclassing the 'normal' pair and using
expression aliases to provide the 'renamed' members. Actually, come to
think of it, that doesn't even need this proposal, although it would be
interesting for std::map to use a flavor of std::pair using this idea,
if only for the sake of having such a std::pair that is implicitly
compatible with the generic version.)

Point being... I think the "right" implementation for equivalence is to
have a generic version and a version with user-defined names, with the
latter subclassing the former. This allows implicitly using the latter
as the former, but requires a static_cast to go the other direction,
which seems like TRRTD. Mind, a static_cast from an A to a B, where A
and B are different (both conceptually and in the typesystem), but have
the same structure (e.g. both are pair<int, double>), would be allowable
and permit "conversion" from A to B, whether or not such a conversion
makes conceptual sense.

Additionally...

> Seems to me that this would imply that
>
> struct S { int b };
> struct T { int b };
>
> would be equivalent then too. But that is probably wrong. Please ignore.

...it means this doesn't happen, since the equivalence is through
polymorphism (which isn't present above... although, similarly, you can
already "meaningfully" reinterpret_cast between T* and S* today).

--
Matthew

Roland Bock

unread,
Mar 25, 2015, 1:36:04 PM3/25/15
to std-pr...@isocpp.org
On 2015-03-25 16:04, Matthew Woehlke wrote:
> On 2015-03-24 17:23, Roland Bock wrote:
>> This is the third new thing: The non-static template member variables
>> have an ability unlike anything else. They can turn unrelated structs
>> into equivalents.
>>
>> This is the fourth new thing: Equivalence. Obviously the two structs
>> above are not the same. Their members have totally different names. But
>> maybe they can be converted to each other with an automatically
>> generated conversion operator as soon as there are these special members?
> I was thinking about this some, and how my "optimal outcome" of such a
> feature would be for the std::map::iterator::operator->() type to
> *finally* have members "key" and "value" instead of the obnoxious
> "first" and "second". (Well, "in addition to", technically, since of
> course we need to be backwards compatible.)
"In addition" is a nice idea. Do we (plan to) have aliases for members?
We have aliases for types and templates...

If we had aliases for members, then pair could get two new optional
template parameters:

template<typename T1, typename T2,
name N1 = `first`, name N2 = `second`>
struct pair
{
T1 first|N1; // Inventing syntax here...
T2 second|N2; // duplicates to be ignored
//...
};

And the value_type of a map could then be defined to

using value_type = std::pair<Key, T, `key`, `value`>;

This would give you backwards compatibility (you could still call the
members `first` and `second`) and you could also call the members `key`
and `value`.

Yeah, I'd love that :-)


> For compatibility, this would also need to implicitly convert to - or
> more likely, be polymorphic with - a std::pair with the "usual" names.
> (I think this is possible by subclassing the 'normal' pair and using
> expression aliases to provide the 'renamed' members.
Could you show how you would do that?

> Actually, come to
> think of it, that doesn't even need this proposal, although it would be
> interesting for std::map to use a flavor of std::pair using this idea,
> if only for the sake of having such a std::pair that is implicitly
> compatible with the generic version.)

See above :-)


Cheers,

Roland

Matthew Woehlke

unread,
Mar 25, 2015, 5:13:41 PM3/25/15
to std-pr...@isocpp.org
On 2015-03-25 13:36, Roland Bock wrote:
> On 2015-03-25 16:04, Matthew Woehlke wrote:
>> On 2015-03-24 17:23, Roland Bock wrote:
>>> This is the third new thing: The non-static template member variables
>>> have an ability unlike anything else. They can turn unrelated structs
>>> into equivalents.
>>>
>>> This is the fourth new thing: Equivalence. Obviously the two structs
>>> above are not the same. Their members have totally different names. But
>>> maybe they can be converted to each other with an automatically
>>> generated conversion operator as soon as there are these special members?
>> I was thinking about this some, and how my "optimal outcome" of such a
>> feature would be for the std::map::iterator::operator->() type to
>> *finally* have members "key" and "value" instead of the obnoxious
>> "first" and "second". (Well, "in addition to", technically, since of
>> course we need to be backwards compatible.)
>
> "In addition" is a nice idea. Do we (plan to) have aliases for members?
> We have aliases for types and templates...

This would probably need to use the 'expression aliases' feature that
keeps popping up. (Another reason to actually get that into the language...)

> If we had aliases for members, then pair could get two new optional
> template parameters:
>
> template<typename T1, typename T2,
> name N1 = `first`, name N2 = `second`>
> struct pair
> {
> T1 first|N1; // Inventing syntax here...
> T2 second|N2; // duplicates to be ignored
> //...
> };
>
> And the value_type of a map could then be defined to
>
> using value_type = std::pair<Key, T, `key`, `value`>;
>
> This would give you backwards compatibility (you could still call the
> members `first` and `second`) and you could also call the members `key`
> and `value`.

The problem here is with what I was talking about in the last mail; it
*must* (for compatibility) implicitly convert to a std::pair with the
default names. That's why I was thinking subclassing. (Also because I'm
not sure how to write the members / aliases so that they are omitted if
the default names are used. Subclassing would solve that problem by
simply declaring that you only use the subclass if you use other names,
and since you can implicitly convert to the base class, you get the
required source compatibility.)

Otherwise, yes, that's the idea :-).

>> For compatibility, this would also need to implicitly convert to - or
>> more likely, be polymorphic with - a std::pair with the "usual" names.
>> (I think this is possible by subclassing the 'normal' pair and using
>> expression aliases to provide the 'renamed' members.
>
> Could you show how you would do that?

template <typename T1, typename T2, name N1, name N2>
struct named_pair : pair<T1, T2>
{
using constexpr N1 = first; // note: this is an expression alias
using constexpr N2 = second;
};

Now, a named_pair<A, B, `key`, `value`> is implicitly convertible (both
copy and reference) to a pair<A, B>, since the latter is a base class of
the former, but given such a critter 'i', you can write 'i.value' to
access the member 'pair<A, B>::second'.

>> Actually, come to think of it, that doesn't even need this
>> proposal, although it would be interesting for std::map to use a
>> flavor of std::pair using this idea, if only for the sake of having
>> such a std::pair that is implicitly compatible with the generic
>> version.)
>
> See above :-)

Without this proposal (but still requiring expression aliases), you
could write:

// within map<KeyType, ValueType>
struct node : pair<KeyType, ValueType>
{
using constexpr key = first;
using constexpr value = second;
};


However, being able to express the map's node type like:

// within map<KeyType, ValueType>
using node = named_pair<KeyType, ValueType, `key`, `value`>

...would be a good use case for your feature, both to ensure that it
works, and to avoid a completely local structure in std::map.

--
Matthew

Roland Bock

unread,
Mar 26, 2015, 2:28:40 AM3/26/15
to std-pr...@isocpp.org
On 2015-03-25 22:13, Matthew Woehlke wrote:
> On 2015-03-25 13:36, Roland Bock wrote:
>> On 2015-03-25 16:04, Matthew Woehlke wrote:
>>> On 2015-03-24 17:23, Roland Bock wrote:
>>>> This is the third new thing: The non-static template member variables
>>>> have an ability unlike anything else. They can turn unrelated structs
>>>> into equivalents.
>>>>
>>>> This is the fourth new thing: Equivalence. Obviously the two structs
>>>> above are not the same. Their members have totally different names. But
>>>> maybe they can be converted to each other with an automatically
>>>> generated conversion operator as soon as there are these special members?
>>> I was thinking about this some, and how my "optimal outcome" of such a
>>> feature would be for the std::map::iterator::operator->() type to
>>> *finally* have members "key" and "value" instead of the obnoxious
>>> "first" and "second". (Well, "in addition to", technically, since of
>>> course we need to be backwards compatible.)
>> "In addition" is a nice idea. Do we (plan to) have aliases for members?
>> We have aliases for types and templates...
> This would probably need to use the 'expression aliases' feature that
> keeps popping up. (Another reason to actually get that into the language...)

Missed those, but yes, it would be a very nice feature :-)

>
>> If we had aliases for members, then pair could get two new optional
>> template parameters:
>>
>> template<typename T1, typename T2,
>> name N1 = `first`, name N2 = `second`>
>> struct pair
>> {
>> T1 first|N1; // Inventing syntax here...
>> T2 second|N2; // duplicates to be ignored
>> //...
>> };
>>
>> And the value_type of a map could then be defined to
>>
>> using value_type = std::pair<Key, T, `key`, `value`>;
>>
>> This would give you backwards compatibility (you could still call the
>> members `first` and `second`) and you could also call the members `key`
>> and `value`.
> The problem here is with what I was talking about in the last mail; it
> *must* (for compatibility) implicitly convert to a std::pair with the
> default names.
Rules for implicit conversion tend to take me by surprise, so I am
probably missing something, but wouldn't it be sufficient if named_pair
had conversion operators in both directions?

template<typename T1, typename T2, name N1, name N2>
struct named_pair
{
T1 N1;
T2 N2;

named_pair(const pair<T1,T2>&);
named_pair(pair<T1,T2>&&);

operator pair<T1, T2>&()
{
return reinterpret_cast<pair<T1,T2>&>(*this);
}
operator const pair<T1, T2>&() const;
};

std::pair could also have templated conversion operators in both directions.

template<typename T1, typename T2>
struct pair
{
T1 first;
T2 second;

template<name N1, name N2>
named_pair(const named_pair<T1,T2,N1,N2>&);

template<name N1, name N2>
named_pair(pair<T1,T2,N1,N2>&&);

template<name N1, name N2>
operator pair<T1, T2,N1,N2>&()
{
return reinterpret_cast<pair<T1,T2,N1,N2>&>(*this);
}

template<name N1, name N2>
operator const pair<T1,T2,N1,N2>&() const;
};
It would be a nice use case if we had expression aliases, too :-)

Matthew Woehlke

unread,
Mar 26, 2015, 11:16:59 AM3/26/15
to std-pr...@isocpp.org
On 2015-03-26 02:28, Roland Bock wrote:
> On 2015-03-25 22:13, Matthew Woehlke wrote:
>> it *must* (for compatibility) implicitly convert to a std::pair
>> with the default names.
>
> Rules for implicit conversion tend to take me by surprise, so I am
> probably missing something, but wouldn't it be sufficient if named_pair
> had conversion operators in both directions?
>
> template<typename T1, typename T2, name N1, name N2>
> struct named_pair
> {
> T1 N1;
> T2 N2;
>
> named_pair(const pair<T1,T2>&);
> named_pair(pair<T1,T2>&&);
>
> operator pair<T1, T2>&()
> {
> return reinterpret_cast<pair<T1,T2>&>(*this);
> }
> operator const pair<T1, T2>&() const;
> };

Hmm... yes, I wasn't thinking of a conversion-to-reference that did a
reinterpret_cast. To me that's rather ugly, though :-). However, you
still need to have both the specific and default names, at least for a
flavor that std::map would use. (Maybe this is an argument for std::map
to *not* use the feature, so that the explicitly named version has
*only* the specified names. On the plus side, this means it doesn't need
expression aliases. On the down side, it means you can't compatibly
replace an existing pair with a named_pair.)

>> However, being able to express the map's node type like:
>>
>> // within map<KeyType, ValueType>
>> using node = named_pair<KeyType, ValueType, `key`, `value`>
>>
>> ...would be a good use case for your feature, both to ensure that it
>> works, and to avoid a completely local structure in std::map.
>>
> It would be a nice use case if we had expression aliases, too :-)

True enough :-).

Hmm, I wonder if anyone has thought before about starting a wiki of
features people have requested. It would be a good place to keep track
of things that have come up before, and what general reaction they got.
(I think the reaction to expression aliases has been neutral to
positive, but I'm also thinking about things like properties and
especially explicit non-initialization that have been shot down *hard*
before, but keep coming back anyway.) Bonus points for tracking pending
proposals...

--
Matthew

Roland Bock

unread,
Mar 26, 2015, 11:54:33 AM3/26/15
to std-pr...@isocpp.org
On 2015-03-26 16:16, Matthew Woehlke wrote:
> On 2015-03-26 02:28, Roland Bock wrote:
>> On 2015-03-25 22:13, Matthew Woehlke wrote:
>>> it *must* (for compatibility) implicitly convert to a std::pair
>>> with the default names.
>> Rules for implicit conversion tend to take me by surprise, so I am
>> probably missing something, but wouldn't it be sufficient if named_pair
>> had conversion operators in both directions?
>>
>> template<typename T1, typename T2, name N1, name N2>
>> struct named_pair
>> {
>> T1 N1;
>> T2 N2;
>>
>> named_pair(const pair<T1,T2>&);
>> named_pair(pair<T1,T2>&&);
>>
>> operator pair<T1, T2>&()
>> {
>> return reinterpret_cast<pair<T1,T2>&>(*this);
>> }
>> operator const pair<T1, T2>&() const;
>> };
> Hmm... yes, I wasn't thinking of a conversion-to-reference that did a
> reinterpret_cast. To me that's rather ugly, though :-).
reinterpret_cast was your idea ;-)

And yes, it would be code that would be marked as
"don't try this at home, kids!"

> However, you
> still need to have both the specific and default names, at least for a
> flavor that std::map would use. (Maybe this is an argument for std::map
> to *not* use the feature, so that the explicitly named version has
> *only* the specified names. On the plus side, this means it doesn't need
> expression aliases. On the down side, it means you can't compatibly
> replace an existing pair with a named_pair.)
Hmm. I thought that named pair would be using expression aliases to
allow access to each element via both names:

template<typename T1, typename T2,
name N1 = `first`, name N2 = `second`>
struct named_pair
{
T1 first|N1; // This member would be named both first/N1
T2 second|N2; // duplicates are to be ignored
//...
};

Thus, if N1 == `first`, then that member would just be called first. If
N1 == `key`, then you could use both `first` and `key` to address the
first member.

If we had expression aliases, that is, of course :-)

>
>>> However, being able to express the map's node type like:
>>>
>>> // within map<KeyType, ValueType>
>>> using node = named_pair<KeyType, ValueType, `key`, `value`>
>>>
>>> ...would be a good use case for your feature, both to ensure that it
>>> works, and to avoid a completely local structure in std::map.
>>>
>> It would be a nice use case if we had expression aliases, too :-)
> True enough :-).
>
> Hmm, I wonder if anyone has thought before about starting a wiki of
> features people have requested. It would be a good place to keep track
> of things that have come up before, and what general reaction they got.
> (I think the reaction to expression aliases has been neutral to
> positive, but I'm also thinking about things like properties and
> especially explicit non-initialization that have been shot down *hard*
> before, but keep coming back anyway.) Bonus points for tracking pending
> proposals...
>
That is a wonderful idea. It would also make for lovely talks at
conferences: "An overview of pending proposals", "CRRSP: Curiously
recurring and rejected standard proposals", ...

I would vote for wiki and talks without hesitation.

Cheers,

Roland

Matthew Woehlke

unread,
Mar 26, 2015, 12:25:31 PM3/26/15
to std-pr...@isocpp.org
On 2015-03-26 11:54, Roland Bock wrote:
> On 2015-03-26 16:16, Matthew Woehlke wrote:
>> On 2015-03-26 02:28, Roland Bock wrote:
>>> On 2015-03-25 22:13, Matthew Woehlke wrote:
>>>> it *must* (for compatibility) implicitly convert to a std::pair
>>>> with the default names.
>>> Rules for implicit conversion tend to take me by surprise, so I am
>>> probably missing something, but wouldn't it be sufficient if named_pair
>>> had conversion operators in both directions?
>>>
>>> template<typename T1, typename T2, name N1, name N2>
>>> struct named_pair
>>> {
>>> T1 N1;
>>> T2 N2;
>>>
>>> named_pair(const pair<T1,T2>&);
>>> named_pair(pair<T1,T2>&&);
>>>
>>> operator pair<T1, T2>&()
>>> {
>>> return reinterpret_cast<pair<T1,T2>&>(*this);
>>> }
>>> operator const pair<T1, T2>&() const;
>>> };
>> Hmm... yes, I wasn't thinking of a conversion-to-reference that did a
>> reinterpret_cast. To me that's rather ugly, though :-).
> reinterpret_cast was your idea ;-)
>
> And yes, it would be code that would be marked as
> "don't try this at home, kids!"

Heh... touché :-). That *was* rather for the case of coercing one
named_pair into an "unrelated" one with the same structure, but I
suppose you can argue this qualifies :-).

> Hmm. I thought that named pair would be using expression aliases to
> allow access to each element via both names:
>
> template<typename T1, typename T2,
> name N1 = `first`, name N2 = `second`>
> struct named_pair
> {
> T1 first|N1; // This member would be named both first/N1
> T2 second|N2; // duplicates are to be ignored
> //...
> };
>
> Thus, if N1 == `first`, then that member would just be called first. If
> N1 == `key`, then you could use both `first` and `key` to address the
> first member.

The problem with that is that you need to specialize for the case where
the default names are used, or you'll have identifier collisions. That's
why I like the subclass approach; you can use the base class for the
case that you only care about the default names, and just require that
the subclass must provide different names for all templated names. And
you get the convenience that using the default names actually *is* just
the old, non-template-names version of the class, and the implicit
conversion to the same is free because it's just an upcast.

>> Hmm, I wonder if anyone has thought before about starting a wiki of
>> features people have requested [...] that have been shot down
>> *hard* before, but keep coming back anyway.
>
> "CRRSP: Curiously recurring and rejected standard proposals", ...

:-D

--
Matthew

Roland Bock

unread,
Mar 26, 2015, 12:49:54 PM3/26/15
to std-pr...@isocpp.org
On 2015-03-26 17:25, Matthew Woehlke wrote:
>>>> Rules for implicit conversion tend to take me by surprise, so I am
>>>> probably missing something, but wouldn't it be sufficient if named_pair
>>>> had conversion operators in both directions?
>>>>
>>>> template<typename T1, typename T2, name N1, name N2>
>>>> struct named_pair
>>>> {
>>>> T1 N1;
>>>> T2 N2;
>>>>
>>>> named_pair(const pair<T1,T2>&);
>>>> named_pair(pair<T1,T2>&&);
>>>>
>>>> operator pair<T1, T2>&()
>>>> {
>>>> return reinterpret_cast<pair<T1,T2>&>(*this);
>>>> }
>>>> operator const pair<T1, T2>&() const;
>>>> };
>>> Hmm... yes, I wasn't thinking of a conversion-to-reference that did a
>>> reinterpret_cast. To me that's rather ugly, though :-).
>> reinterpret_cast was your idea ;-)
>>
>> And yes, it would be code that would be marked as
>> "don't try this at home, kids!"
> Heh... touché :-). That *was* rather for the case of coercing one
> named_pair into an "unrelated" one with the same structure, but I
> suppose you can argue this qualifies :-).
:-)

>> Hmm. I thought that named pair would be using expression aliases to
>> allow access to each element via both names:
>>
>> template<typename T1, typename T2,
>> name N1 = `first`, name N2 = `second`>
>> struct named_pair
>> {
>> T1 first|N1; // This member would be named both first/N1
>> T2 second|N2; // duplicates are to be ignored
>> //...
>> };
>>
>> Thus, if N1 == `first`, then that member would just be called first. If
>> N1 == `key`, then you could use both `first` and `key` to address the
>> first member.
> The problem with that is that you need to specialize for the case where
> the default names are used, or you'll have identifier collisions.
It depends on how expression aliases work. I would argue that it should
handle collisions gracefully. If you insist on calling something `first`
which already is called `first`, then that's ok. It does not have to
create a compiler error.
> That's
> why I like the subclass approach; you can use the base class for the
> case that you only care about the default names, and just require that
> the subclass must provide different names for all templated names. And
> you get the convenience that using the default names actually *is* just
> the old, non-template-names version of the class, and the implicit
> conversion to the same is free because it's just an upcast.
The only thing I could currently say against that is: I am not a big fan
of inheritance. That's why I would try to avoid it.

On the other hand, I am not a big fan of reinterpret_cast either...
>>> Hmm, I wonder if anyone has thought before about starting a wiki of
>>> features people have requested [...] that have been shot down
>>> *hard* before, but keep coming back anyway.
>> "CRRSP: Curiously recurring and rejected standard proposals", ...
> :-D
I was quite sincere, btw, it could be a lovely talk! I would very much
like to hear it.

Roland


Dejan Milosavljevic

unread,
Mar 27, 2015, 7:23:32 PM3/27/15
to std-pr...@isocpp.org
What about this model:

#define Foo(Type,Name)      \
 struct                     \
  {                         \
    Type Name;              \
  }

typedef Foo(int,test1) First;
typedef Foo(float,test2) Second;




Message has been deleted

Dejan Milosavljevic

unread,
Mar 28, 2015, 5:10:55 AM3/28/15
to std-pr...@isocpp.org
Roland Bock > Do we (plan to) have aliases for members?
Thumbs up for that!!!

And member functions too.


Here is another syntax approach for that.
As consequence we have injection of alias in class.

struct A
{
    int a_public;
protected:
    int a_protected;
private:
    int a_private;
};
struct B : A
{
    using b_public     = a_public;    //!< OK: a_public is public
    using b_protected  = a_protected; //!< OK: a_protected is protected and this b_protected remain protected
    using b_private    = a_private;   //!< error: visibility rules
};
using B::x_public     = B::a_public   ;   //!< OK: a_public is public
using B::x_protected  = B::b_protected;   //!< error: visibility rules
using B::x_private    = B::b_private  ;   //!< error: visibility rules

using y = B::b_public;   //!< error: scope change.

int main( int argc, char *argv[] )
 {
   B b;
 b.x_protected; //!< error: it is protected even if declared in public scope

  using my_map = std::map<int,int>;
  my_map m;
  // may_pair is injected in std::map<int,int>.
  using my_map::my_pair = my_pair::my_map::value_type;
  using my_map::my_pair::key  = my_pair::my_map::value_type::first;
  using my_map::my_pair::data = my_pair::my_map::value_type::second;
  for( auto const& i : m )
  {
      i.data = 10;
      std::cout << i.key; 
  }
  return EXIT_SUCCESS;
 }

Roland Bock

unread,
Mar 28, 2015, 5:40:29 AM3/28/15
to std-pr...@isocpp.org
For which aspect of the discussion would you use that?

To give you a (simplified) example of one of the use cases I have in mind for names:

template<typename... Columns>
auto select(Columns&&... cs)
{
   return result_row<
           result_field<typename Column::type, Column::name>...>
       {cs...}
}

result_field then is a struct with members of appropriate types and names.

This cannot be done with the macro you're suggesting, because

a) the names depend on the argument types
b) the parameter pack is expanded after the preprocessor has done its work.

Best,

Roland

Roland Bock

unread,
Mar 28, 2015, 5:50:40 AM3/28/15
to std-pr...@isocpp.org
On 2015-03-28 10:08, Dejan Milosavljevic wrote:

Rp;and Bock >Do we (plan to) have aliases for members?
Thumbs up for that!!!

And member functions too.

It would be a nice use case for names, indeed :-)
IF we had expression aliases, then something like this would certainly work. The question would then be: How could map use this feature so that

a) you could use key/data or key/value in new code, while old code still works
b) you do not have to declare those aliases for every loop you write

Regards,

Roland

Dejan Milosavljevic

unread,
Mar 28, 2015, 6:19:46 AM3/28/15
to std-pr...@isocpp.org
> This cannot be done with the macro you're suggesting, ...
Agree. Using macro is not elegant solution. That was idea on first thought.


> a) you could use key/data or key/value in new code, while old code still works

Injection of member/expression alias to class.

> b) you do not have to declare those aliases for every loop you write

Unfortunately this is must. Access control and scope control will apply anyway.
Question is how broad visibility we want.
Class A and B demonstrate wide visibility.
 std::map<...> demonstrate loop-only visibility.

Roland Bock

unread,
Mar 28, 2015, 8:05:55 AM3/28/15
to std-pr...@isocpp.org
With the sub-class approach (named_pair: public pair) suggested by Matthew or the implicit conversion operators (named_pair<->pair) suggested by me, these aliases could be introduced by the map already.

David Krauss

unread,
Mar 28, 2015, 10:57:51 PM3/28/15
to std-pr...@isocpp.org
There is an existing workaround, just for the sake of argument.

extern struct key_tag {} key;
extern struct value_tag {} value;

template< typename first_t, typename second_t >
first_t & operator ->* ( std::pair< first_t, second_t > & p, key_tag )
   { return p.first; }

template< typename first_t, typename second_t >
second_t & operator ->* ( std::pair< first_t, second_t > & p, value_tag )
   { return p.second; }

std::map< int, std::string > m { { 1, "hello" }, { 2, "world" } };
int k = (*m.begin())->*key;
std::string v = (*m.begin())->*value;

Add overloads for const.

I wonder, would it do any real harm to add drill-down behavior to operator->* to match operator->? It should use operator-> to drill down until either a pointer type LHS or an operator->* overload is found.

Roland Bock

unread,
Mar 29, 2015, 6:52:57 AM3/29/15
to std-pr...@isocpp.org
On 2015-03-29 04:57, David Krauss wrote:
There is an existing workaround, just for the sake of argument.

extern struct key_tag {} key;
extern struct value_tag {} value;

template< typename first_t, typename second_t >
first_t & operator ->* ( std::pair< first_t, second_t > & p, key_tag )
   { return p.first; }

template< typename first_t, typename second_t >
second_t & operator ->* ( std::pair< first_t, second_t > & p, value_tag )
   { return p.second; }

std::map< int, std::string > m { { 1, "hello" }, { 2, "world" } };
int k = (*m.begin())->*key;
std::string v = (*m.begin())->*value;

Add overloads for const.

Nice! Overloading pointer to member always takes me by surprise. This reminds me of a proposal to overload operator.()...

Looks a bit weird though, when key/value are living in a separate namespace:

  for (entry : m)
  {
    int k = entry->*project::key;
    std::string v = entry->*project::value;

  }



I wonder, would it do any real harm to add drill-down behavior to operator->* to match operator->? It should use operator-> to drill down until either a pointer type LHS or an operator->* overload is found.


Can't think of any harm it might do, but I am certainly not the most qualified person to reason about this ;-)

gmis...@gmail.com

unread,
Mar 29, 2015, 5:06:02 PM3/29/15
to std-pr...@isocpp.org

Way back at the start of this thread I suggested this syntax:

std::pair<string first_name, string last_name> name{"bob", "jones"};
printf("Hello %s %s\n", name.first_name.c_str(), name.second_name.c_str());

This syntax provides a local alias that gets replaced with whatever names the template actually uses.
The idea didn't seem to get much traction but it seems to be a simple solution at least for the example problem I provided with it, to avoid first and second.
Does it not solve that problem and what the OP wanted too? What's the problem with it?

To elaborate: In my example, the problem is that first and second create unreadable code. 90% of the time when I see pair, I can and do replace it with a simple struct. I sometimes can do that with tuple too, but I do risk introducing incompatibility when I do this.

But if we could provide the alias syntax I suggest,  the code would expands to exactly the original pair code. Pair continues to use the names it wants to use and I could continue using pair and not introduce new types and allow quick refactoring with no risk of breakage. (I think.)

One potential issue I see is that the underlying type (pair in this case) could later introduce new names matching any aliases given. i.e. in the example, imagine the issues If pair introduce a real first_name and last_name later. If this happens, the new real first_name and last_name names are inaccessible because the aliases would hide the new fields by mapping to the original first and last fields not the new ones.

Existing code would still work which is good, but the situation wouldn't be tenable going forward as the reader wouldn't be easily sure without checking what intent the was and so the code would appear a little ambiguous but the real problem would be the new code might want the access the hidden fields. But if the compiler would emit a warning it would encourage people to fix those issues as they arrive so code clarity is maintained. But this only happens in the assumedly rare event of a clash and a compatible fix is always possible i.e. go back to referring to the original names if you need them and refer to the new names if that's what you need. Hopefully people cam following this, but I think it makes sense.

People may have other suggestions for tweaking the concept here. But what's wrong with the basic idea?

If this is useful, but not solving the OP's problem, I can start this idea in a new thread. But it appears to solve the OP's problem to my mind and at least the real example of a problem I gave. I am trying to follow the OP's solution path and that seems more complicated but it may solve their problem better and other different ones. I'm not sure if it solves this example?

Roland Bock

unread,
Mar 30, 2015, 2:20:48 AM3/30/15
to std-pr...@isocpp.org
On 2015-03-29 23:06, gmis...@gmail.com wrote:
>
> Way back at the start of this thread I suggested this syntax:
>
> std::pair<string first_name, string last_name> name{"bob", "jones"};
> printf("Hello %s %s\n", name.first_name.c_str(),
> name.second_name.c_str());
>
> This syntax provides a local alias that gets replaced with whatever
> names the template actually uses.
> The idea didn't seem to get much traction but it seems to be a simple
> solution at least for the example problem I provided with it, to avoid
> first and second.
> Does it not solve that problem and what the OP wanted too? What's the
> problem with it?
Hi,

Here are some problems I see with the syntax you provided:

It seems like types and names in your syntax would have to come in pairs
if a name is specified at all. And it seems kind of obvious where to
apply the names in case of std::pair. But what if there were templates
like these:

template<typename A>
struct foo
{
A first;
A second;
};

auto x = foo<int hello>{7,8};
x.hello;// is that first or second or both?

template<typename A>
struct bar
{
const std::shared_ptr<A> first;
};

auto y = bar<int, hello>{};
y.hello; is that even available? decltype(first) != A

Or take a look at a tuple implementation of your choice and then try to
apply the type-name pairs to the tuple elements.

I therefore believe that we need to be able to specify types and names
independently.


Another question would be:
How do you transport names that you cannot specify in the way your
example suggests?

See for instance the CRTP example in my blog post.
http://cpp.eudoxos.de/dreaming-of-names/

The name is specified in the "mixin" and it is not used as a template
parameter.


Or try to create a named_tuple (see blog post) with your syntax,
including the comparison operators.


Regards,

Roland

Matthew Woehlke

unread,
Mar 30, 2015, 11:25:25 AM3/30/15
to std-pr...@isocpp.org
On 2015-03-28 05:08, Dejan Milosavljevic wrote:
> Rp;and Bock >Do we (plan to) have aliases for members?
> Thumbs up for that!!!
>
> And member functions too.

You can "already" alias member functions using an inline trampoline. I
*think* expression aliases would allow a slightly less verbose syntax if
you can have a "member" of "unbound member function" type, although
these might still be ugly. (Not a bad idea to call these out in an
expression alias proposal.)

> struct A
> {
> int a_public;
> protected:
> int a_protected;
> private:
> int a_private;
> };
> struct B : A
> {
> using b_public = a_public; //!< OK: a_public is public
> using b_protected = a_protected; //!< OK: a_protected is protected and
> this b_protected remain protected

Er... you declared b_protected as public; I would expect it to be
public... (Fiddling with access protections, especially public-const,
protected-mutable, is one of the intended use cases of expression
aliases. Also, what you suggest is contrary to 'using a_protected;'.)

> using b_private = a_private; //!< error: visibility rules
> };


--
Matthew

Matthew Woehlke

unread,
Mar 30, 2015, 12:26:51 PM3/30/15
to std-pr...@isocpp.org
On 2015-03-29 17:06, gmis...@gmail.com wrote:
> One potential issue I see is that the underlying type (pair in this case)
> could later introduce new names matching any aliases given. i.e. in the
> example, imagine the issues If pair introduce a real first_name and
> last_name later. If this happens, the new real first_name and
> last_name names are inaccessible because the aliases would hide the new
> fields by mapping to the original first and last fields not the new ones.

(Oh, look; another reason for the subclass approach :-).)

// as in your example, syntax bikeshed elided
auto p = named_pair<...>;

p.first_name; // really std::pair::first
p.std::pair::first_name;

No problem there :-). (I can still invent problematic scenarios, but
let's be realistic: this is *std::pair* we're talking about. The chances
of its layout changing are effectively nil.)

> People may have other suggestions for tweaking the concept here. But what's
> wrong with the basic idea?

Roland pointed out the obvious issue already, so I won't rehash (except
to say I agree with him that we "need" to be able to support use cases
other than the trivial one).

That said, as much as std::pair is an obvious use case, let's keep in
mind that what we're trying to address is ways to inject identifiers
into a template. Ideally that would not be restricted to member
variables, but to any identifier (certainly also member *functions*,
maybe even other things like enum values?).

--
Matthew

Roland Bock

unread,
Mar 30, 2015, 3:16:54 PM3/30/15
to std-pr...@isocpp.org
On 2015-03-30 18:26, Matthew Woehlke wrote:
> On 2015-03-29 17:06, gmis...@gmail.com wrote:
>> One potential issue I see is that the underlying type (pair in this case)
>> could later introduce new names matching any aliases given. i.e. in the
>> example, imagine the issues If pair introduce a real first_name and
>> last_name later. If this happens, the new real first_name and
>> last_name names are inaccessible because the aliases would hide the new
>> fields by mapping to the original first and last fields not the new ones.
> (Oh, look; another reason for the subclass approach :-).)

:-)

>
> That said, as much as std::pair is an obvious use case, let's keep in
> mind that what we're trying to address is ways to inject identifiers
> into a template. Ideally that would not be restricted to member
> variables, but to any identifier (certainly also member *functions*,
> maybe even other things like enum values?).
>
My thoughts exactly. Any identifier, really. Although I must admit that
I haven't come up with a compelling /simple/ use case for functions or
enum values or type names.

Maybe I can simplify examples from sqlpp11: WHERE and HAVING clauses,
for instance, are almost identical except for the names of data members
and functions.

I'd be interested in other use cases, of course.

Regards,

Roland

Matthew Woehlke

unread,
Mar 30, 2015, 4:46:01 PM3/30/15
to std-pr...@isocpp.org
On 2015-03-30 15:16, Roland Bock wrote:
> On 2015-03-30 18:26, Matthew Woehlke wrote:
>> That said, as much as std::pair is an obvious use case, let's keep in
>> mind that what we're trying to address is ways to inject identifiers
>> into a template. Ideally that would not be restricted to member
>> variables, but to any identifier (certainly also member *functions*,
>> maybe even other things like enum values?).
>
> My thoughts exactly. Any identifier, really. Although I must admit that
> I haven't come up with a compelling /simple/ use case for functions or
> enum values or type names.

Likewise :-). I was going to say something along those lines, but after
a non-trivial time thinking about it, was unable to come up with any
practical example where giving e.g. the *name* of a function to be used
in the implementation of some method would be useful but giving the
function itself would not be possible.

So, naturally, the moment I started writing this reply, I came up with
something like:

// FUNC is an identifier template argument
void templateMethod()
{
if (...) FUNC(x);
else if (...) FUNC(x, y);
else FUNC(a, b);
}

That is, where the template wants to call several overloads of something
with the same name, where 'something' is not (necessarily) a functor.
(If the method in question were itself templated, such that the function
type is non known to the outer template, that would be another use case.)

Note: I don't think we should allow type names unless you can explain
how that would differ from what we already have :-).

--
Matthew

Roland Bock

unread,
Mar 31, 2015, 3:09:24 AM3/31/15
to std-pr...@isocpp.org
Good thinking! I actually encountered the second example (template function) not too long ago and ended up writing wrapper types, if I recall correctly...



Note: I don't think we should allow type names unless you can explain
how that would differ from what we already have :-).

What we have is

struct foo
{
   using bar = int;
  
   struct baz {};
}

The names bar and baz are fixed here.

If we could inject identifiers, then why not allow something like this:

template<name X, name Y>
struct sample
{
   using X = int;

   struct Y {};
};

As mentioned above, I don't have a use case for this yet, but it would seem like an unnecessary restriction to not allow it.


I would not allow the following though:

template<name X>
struct X {};

As you wrote, we have the mechanisms for that already.


Regards,

Roland

Matthew Woehlke

unread,
Mar 31, 2015, 10:16:22 AM3/31/15
to std-pr...@isocpp.org
On 2015-03-31 03:09, Roland Bock wrote:
> On 2015-03-30 22:45, Matthew Woehlke wrote:
>> Note: I don't think we should allow type names unless you can explain
>> how that would differ from what we already have :-).
>
> What we have is
>
> struct foo
> {
> using bar= int;
>
> struct baz {};
> }
>
> The names bar and baz are fixed here.
>
> If we could inject identifiers, then why not allow something like this:
>
> template<name X, name Y>
> struct sample
> {
> using X = int;
>
> struct Y {};
> };

Ah. Yes, that seems reasonable. I was thinking of something like:

template <name X> struct sample { X member; };

I think the appropriate clarification here is, a 'name' may be used to
*introduce a new typename*. It may *not* be used to name an existing
type (because that would be redundant with 'typename'). Possibly with an
exception to the latter if the typename was previously introduced by the
template.

> I would /not/ allow the following though:
>
> template<name X>
> struct X {};
>
> As you wrote, we have the mechanisms for that already.

Actually I *wasn't* thinking about that, but that... does indeed seem a
bit strange. And we can achieve the same thing with 'using' already, so
I agree, I don't think we *need* to allow that. (That said, I'd be okay
not expressly forbidding it, either, as it might actually be more
implementation effort to forbid :-).)

--
Matthew

Roland Bock

unread,
Mar 31, 2015, 11:55:52 AM3/31/15
to std-pr...@isocpp.org
On 2015-03-31 16:15, Matthew Woehlke wrote:
> On 2015-03-31 03:09, Roland Bock wrote:
>> On 2015-03-30 22:45, Matthew Woehlke wrote:
>>> Note: I don't think we should allow type names unless you can explain
>>> how that would differ from what we already have :-).
>> What we have is
>>
>> struct foo
>> {
>> using bar= int;
>>
>> struct baz {};
>> }
>>
>> The names bar and baz are fixed here.
>>
>> If we could inject identifiers, then why not allow something like this:
>>
>> template<name X, name Y>
>> struct sample
>> {
>> using X = int;
>>
>> struct Y {};
>> };
> Ah. Yes, that seems reasonable. I was thinking of something like:
>
> template <name X> struct sample { X member; };
>
> I think the appropriate clarification here is, a 'name' may be used to
> *introduce a new typename*. It may *not* be used to name an existing
> type (because that would be redundant with 'typename'). Possibly with an
> exception to the latter if the typename was previously introduced by the
> template.
Actually, not having any clue how the whole thing would be implemented
in a compiler, I would follow your statement below: If it is easier to
implement allowing this construct, then why not, even if it seems a bit
weird.


>> I would /not/ allow the following though:
>>
>> template<name X>
>> struct X {};
>>
>> As you wrote, we have the mechanisms for that already.
> Actually I *wasn't* thinking about that, but that... does indeed seem a
> bit strange. And we can achieve the same thing with 'using' already, so
> I agree, I don't think we *need* to allow that. (That said, I'd be okay
> not expressly forbidding it, either, as it might actually be more
> implementation effort to forbid :-).)
>
Agreed :-)


Cheers,

Roland

Matthew Woehlke

unread,
Mar 31, 2015, 12:17:48 PM3/31/15
to std-pr...@isocpp.org
On 2015-03-31 11:55, Roland Bock wrote:
> On 2015-03-31 16:15, Matthew Woehlke wrote:
>> I was thinking of something like:
>>
>> template <name X> struct sample { X member; };
>>
>> I think the appropriate clarification here is, a 'name' may be used to
>> *introduce a new typename*. It may *not* be used to name an existing
>> type (because that would be redundant with 'typename'). Possibly with an
>> exception to the latter if the typename was previously introduced by the
>> template.
>
> Actually, not having any clue how the whole thing would be implemented
> in a compiler, I would follow your statement below: If it is easier to
> implement allowing this construct, then why not, even if it seems a bit
> weird.

Sure; I can agree with that, though in the same vein I would leave it
"implementation defined" and not require it to be supported.

Related: is the name required to be unqualified? In the original case
(naming a member), it would be, but in the other cases it could be
qualified (or even include template arguments). Possibly we don't want
to try to support that in the first pass, however.

--
Matthew

Roland Bock

unread,
Mar 31, 2015, 1:03:48 PM3/31/15
to std-pr...@isocpp.org
Can you elaborate? What would be a qualified name?

Matthew Woehlke

unread,
Mar 31, 2015, 1:25:10 PM3/31/15
to std-pr...@isocpp.org
A::B, or even ::B. Consider the previous example of giving a function
name; it may be the case that you need to namespace-qualify the name.
(For obvious reasons, it is an error to give a qualified name if the use
of the name is to define a new identifier.)


--
Matthew

Roland Bock

unread,
Mar 31, 2015, 3:05:53 PM3/31/15
to std-pr...@isocpp.org
Right.

So I would assume that this works:

template<name A, name B>
struct sample
{
   using type = A::B;
};
auto x = typename sample<`std`, `string`>::type{};


But I guess we should not aim for this:

template<name A>
struct sample
{
   using type = A
};
auto x = typename sample<`std::string`>::type{};

We should probably leave that to somebody in X+3, where

X = (the year when names become standard)

;-)

Best,

Roland

Thiago Macieira

unread,
Mar 31, 2015, 3:15:17 PM3/31/15
to std-pr...@isocpp.org
On Tuesday 31 March 2015 21:05:50 Roland Bock wrote:
> template<name A, name B>

Please note that this syntax will conflict with a concept of type "name".
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
PGP/GPG: 0x6EF45358; fingerprint:
E067 918B B660 DBD1 105C 966C 33F5 F005 6EF4 5358

Matthew Woehlke

unread,
Mar 31, 2015, 3:36:56 PM3/31/15
to std-pr...@isocpp.org
On 2015-03-31 15:05, Roland Bock wrote:
> On 2015-03-31 19:24, Matthew Woehlke wrote:
>> On 2015-03-31 13:03, Roland Bock wrote:
>>> On 2015-03-31 18:16, Matthew Woehlke wrote:
>>>> Related: is the name required to be unqualified? In the original case
>>>> (naming a member), it would be, but in the other cases it could be
>>>> qualified (or even include template arguments). Possibly we don't want
>>>> to try to support that in the first pass, however.
>>> Can you elaborate? What would be a qualified name?
>> A::B, or even ::B. Consider the previous example of giving a function
>> name; it may be the case that you need to namespace-qualify the name.
>> (For obvious reasons, it is an error to give a qualified name if the use
>> of the name is to define a new identifier.)
>
> Right.
>
> So I would assume that this works:
>
> template<name A, name B>
> struct sample
> {
> using type = A::B;
> };
> auto x = typename sample<`std`, `string`>::type{};

I would hope that works, iff 'name' can be used anywhere 'typename' can
be used.

> But I guess we should /not/ aim for this:
>
> template<name A>
> struct sample
> {
> using type = A
> };
> auto x = typename sample<`std::string`>::type{};

I would disagree here, although I don't consider this a good example.
Rather, I would argue that this should work:

template <name Operator, typename Args...>
auto sample(Args... args) { return Operator(args); }

auto x = sample<my_sum>(3, 5); // I think we both agree this is okay
auto y = sample<std::max>(3, 5); // I think this should also be okay
auto z = sample<std::min>(data);

It seems unnecessarily restrictive if we're going to be serious about
supporting a 'name' in other than member declarations to prohibit
qualified names.

--
Matthew

Roland Bock

unread,
Mar 31, 2015, 3:48:20 PM3/31/15
to std-pr...@isocpp.org
On 2015-03-31 21:15, Thiago Macieira wrote:
> On Tuesday 31 March 2015 21:05:50 Roland Bock wrote:
>> template<name A, name B>
> Please note that this syntax will conflict with a concept of type "name".
I am aware of that, but I haven't found a better syntax yet and it
currently seems to work well in explaining the idea :-)

To avoid the conflict, either "name" becomes a keyword at least inside
the template parameter list (and cannot be used as a concept) or it has
to be declared in a different way.

Suggestions welcome :-)

Roland Bock

unread,
Mar 31, 2015, 3:48:48 PM3/31/15
to std-pr...@isocpp.org
I should probably call it a night and stop writing mails when tired...

You are right, of course, your example is quite convincing.

Interesting that you don't use forward ticks or other markers to
indicate a name. How would you write names outside of template parameter
lists? E.g.

struct sample
{
using my_name = `sample`;
};

Alex B

unread,
Mar 31, 2015, 3:58:08 PM3/31/15
to std-pr...@isocpp.org
I'm not sure if the idea floated around, but why not use some kind of compile time string instead of backticks or the name keyword?
 
template <typename T, __CompileTimeString s>
struct X
{
   T __name
(s);
};
 
X
<int, "value"> var;
var.value = 0;

 
 
What __CompileTimeString precisely is would be to determine (are there any proposals about compile-time strings in the reflection group?).
Operator __name (bikeshed) would convert a compile time string to an actual name.
 
Combine this + compile time strings + other cool reflection features and we get something really powerful.

Matthew Woehlke

unread,
Mar 31, 2015, 4:15:02 PM3/31/15
to std-pr...@isocpp.org
On 2015-03-31 15:48, Roland Bock wrote:
> Interesting that you don't use forward ticks or other markers to
> indicate a name.

That's *at least* part laziness :-)... and an opinion that the little
syntax bits aren't nailed down yet. Don't read too much into it :-).

> How would you write names outside of template parameter
> lists? E.g.
>
> struct sample
> {
> using my_name = `sample`;
> };

That's an interesting point, although I don't entirely follow the
example. How would you subsequently use 'my_name'? I guess as a template
parameter? (IOW, I guess you are proposing something a little like, but
not entirely the same as, a string literal?)

--
Matthew

Richard Smith

unread,
Mar 31, 2015, 4:57:39 PM3/31/15
to std-pr...@isocpp.org
On Tue, Mar 31, 2015 at 12:58 PM, Alex B <deva...@gmail.com> wrote:
I'm not sure if the idea floated around, but why not use some kind of compile time string instead of backticks or the name keyword?
 
template <typename T, __CompileTimeString s>
struct X
{
   T __name
(s);
};
 
X
<int, "value"> var;
var.value = 0;

 
 
What __CompileTimeString precisely is would be to determine (are there any proposals about compile-time strings in the reflection group?).
Operator __name (bikeshed) would convert a compile time string to an actual name.
 
Combine this + compile time strings + other cool reflection features and we get something really powerful.

A name (or rather, an unqualified-id) is not a string. Consider, for instance, the name 'operator int', which should presumably be the same name as 'operator T' if T is a typedef for 'int'.
 

--

---
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.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.

Richard Smith

unread,
Mar 31, 2015, 5:06:08 PM3/31/15
to std-pr...@isocpp.org
On Tue, Mar 31, 2015 at 12:15 PM, Thiago Macieira <thi...@macieira.org> wrote:
On Tuesday 31 March 2015 21:05:50 Roland Bock wrote:
> template<name A, name B>

Please note that this syntax will conflict with a concept of type "name".

It would also conflict with a type with name "name" (where A and B would be non-type template parameters).

One could imagine solving this conflict by adding a standard-library type std::name (or std::reflect::name, or whatever), with `...` being a literal of that type. Following similar systems in other languages (Template Haskell, lisp macros, ...), there should probably be an explicit reification syntax ($foo, perhaps) to disambiguate between mention and expansion. So:

namespace std { using name = decltype(`blah`); }
template<std::name A> struct X { // name is a normal identifier here
  Y<A> ya; // pass name A to Y
  Y<$A> yda; // look up the name represented by A, pass the result to Y
  int k1 = ya.A; // look up member A in ya
  int k2 = ya.$A; // look up the name represented by A
};
X<`foo`> xfoo;

If such a thing were formally proposed, it would seem prudent to consider whether it can be extended naturally to capture other constructs (types, expressions, statements).

--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
   Software Architect - Intel Open Source Technology Center
      PGP/GPG: 0x6EF45358; fingerprint:
      E067 918B B660 DBD1 105C  966C 33F5 F005 6EF4 5358

Roland Bock

unread,
Apr 1, 2015, 1:41:22 AM4/1/15
to std-pr...@isocpp.org
On 2015-03-31 21:58, Alex B wrote:
I'm not sure if the idea floated around, but why not use some kind of compile time string instead of backticks or the name keyword?
 
template <typename T, __CompileTimeString s>
struct X
{
   T __name
(s);
};
 
X
<int, "value"> var;
var.value = 0;

 
 
What __CompileTimeString precisely is would be to determine (are there any proposals about compile-time strings in the reflection group?).
Operator __name (bikeshed) would convert a compile time string to an actual name.
 
Combine this + compile time strings + other cool reflection features and we get something really powerful.

IMO compile time strings (can contain anything) and names are different animals. It should be possible to turn names into compile time strings. I am not sure about the other way.

And I agree that goal should be to combine reflection (analysis) and names (synthesis).

Roland Bock

unread,
Apr 1, 2015, 2:11:28 AM4/1/15
to std-pr...@isocpp.org
On 2015-03-31 22:57, Richard Smith wrote:
On Tue, Mar 31, 2015 at 12:58 PM, Alex B <deva...@gmail.com> wrote:
I'm not sure if the idea floated around, but why not use some kind of compile time string instead of backticks or the name keyword?
 
template <typename T, __CompileTimeString s>
struct X
{
   T __name
(s);
};
 
X
<int, "value"> var;
var.value = 0;

 
 
What __CompileTimeString precisely is would be to determine (are there any proposals about compile-time strings in the reflection group?).
Operator __name (bikeshed) would convert a compile time string to an actual name.
 
Combine this + compile time strings + other cool reflection features and we get something really powerful.

A name (or rather, an unqualified-id) is not a string.
Agreed, although I think that a name should be convertible to some kind of string.


Consider, for instance, the name 'operator int', which should presumably be the same name as 'operator T' if T is a typedef for 'int'.
 
Depends on where you look, right?

template<typename T, std::name X>
struct foo
{
   $X();
};

struct bar
{
   using T = int;
   using A = foo<int,   `operator T`>;
   using B = foo<float, `operator T`>;
   using C = foo<float, `operator int`>;
   static_assert(std::is_same<A, C>::value == true, "");
   static_assert(std::is_same<B, C>::value == false, "");
};

In case B, $X would evaluate to `operator float`, because T is float in foo.


Roland Bock

unread,
Apr 1, 2015, 2:14:02 AM4/1/15
to std-pr...@isocpp.org
On 2015-03-31 22:14, Matthew Woehlke wrote:
> On 2015-03-31 15:48, Roland Bock wrote:
>> Interesting that you don't use forward ticks or other markers to
>> indicate a name.
> That's *at least* part laziness :-)... and an opinion that the little
> syntax bits aren't nailed down yet. Don't read too much into it :-).
>
>> How would you write names outside of template parameter
>> lists? E.g.
>>
>> struct sample
>> {
>> using my_name = `sample`;
>> };
> That's an interesting point, although I don't entirely follow the
> example. How would you subsequently use 'my_name'? I guess as a template
> parameter?
>
My use case was the CRTP-replaced-by-mixin example in the blog post[1],
here's the mixin:

template <typename Derived>
struct mixin
{
using name = `check`; // name of the mixin
Derived* _derived;

template <typename T>
bool operator()(const T& t) const // mixin is callable
{
return _derived->_data == t;
}
};

// Made this variadic for fun
template<template<typename> class ... Mixin>
struct MyClass
{
Mixin<MyClass> Mixin<MyClass>::name = {this};...
int _data = 7;
};

auto x = MyClass<mixin>{};
x.check(7);


So the mixin has a name (`check`) which is then used by MyClass to name
the member object. Thus, the name is not a template argument here.

> (IOW, I guess you are proposing something a little like, but
> not entirely the same as, a string literal?)


Actually, my original idea of a name literal was similar to a compile
time string. As Richard pointed out, this might be a bad idea (not 100%
sure yet).



[1]: http://cpp.eudoxos.de/dreaming-of-names/

Roland Bock

unread,
Apr 1, 2015, 2:36:44 AM4/1/15
to std-pr...@isocpp.org
On 2015-03-31 23:06, Richard Smith wrote:
On Tue, Mar 31, 2015 at 12:15 PM, Thiago Macieira <thi...@macieira.org> wrote:
On Tuesday 31 March 2015 21:05:50 Roland Bock wrote:
> template<name A, name B>

Please note that this syntax will conflict with a concept of type "name".

It would also conflict with a type with name "name" (where A and B would be non-type template parameters).
Right.



One could imagine solving this conflict by adding a standard-library type std::name (or std::reflect::name, or whatever), with `...` being a literal of that type. Following similar systems in other languages (Template Haskell, lisp macros, ...), there should probably be an explicit reification syntax ($foo, perhaps) to disambiguate between mention and expansion. So:

namespace std { using name = decltype(`blah`); }
template<std::name A> struct X { // name is a normal identifier here
  Y<A> ya; // pass name A to Y
  Y<$A> yda; // look up the name represented by A, pass the result to Y
I am a bit confused here, what would be the differences of

Y<A>
Y<$A>

here? Or did you intend to write

Y<`A`>
Y<$A>

?


  int k1 = ya.A; // look up member A in ya
  int k2 = ya.$A; // look up the name represented by A
};
X<`foo`> xfoo;

If such a thing were formally proposed, it would seem prudent to consider whether it can be extended naturally to capture other constructs (types, expressions, statements).
Whoops! That is a fascinating idea that requires some digestion time :-)

Btw: Quite a few people suggested concatenation of names at CppCon and fantasized about never needing the preprocessor again (not sure about that).

Roland Bock

unread,
Apr 1, 2015, 4:03:45 AM4/1/15
to std-pr...@isocpp.org
On 2015-04-01 08:36, Roland Bock wrote:
>> If such a thing were formally proposed, it would seem prudent to
>> consider whether it can be extended naturally to capture other
>> constructs (types, expressions, statements).
> Whoops! That is a fascinating idea that requires some digestion time :-)
>
> Btw: Quite a few people suggested concatenation of names at CppCon and
> fantasized about never needing the preprocessor again (not sure about
> that).
I should have written "macros" instead of "the preprocessor".

David Krauss

unread,
Apr 1, 2015, 5:32:26 AM4/1/15
to std-pr...@isocpp.org
On 2015–04–01, at 1:41 PM, Roland Bock <rb...@eudoxos.de> wrote:

IMO compile time strings (can contain anything) and names are different animals. It should be possible to turn names into compile time strings. I am not sure about the other way.

Strings into names is a well-defined transformation: “just” compile the string. Nothing else really makes sense.

Names into strings leaves a lot of headroom for lazy implementations. What does __func__ look like in a constructor, destructor, operator overload, or conversion function? Typically it doesn’t look like source code. More common to see compatibility with dlsym than with the C++ compiler, and no reason to expect that trend to reverse.

And I agree that goal should be to combine reflection (analysis) and names (synthesis).

Not sure how well all this fits into the existing template model.

It could be nice to add basic scope awareness to the current preprocessor, just at the level of minding namespaces and counting braces. We already have the ## operator for synthesizing names and the # operator for getting strings, they just suffer from lack of encapsulation.

Roland Bock

unread,
Apr 1, 2015, 11:46:07 AM4/1/15
to std-pr...@isocpp.org
On 2015-04-01 09:29, David Krauss wrote:

On 2015–04–01, at 1:41 PM, Roland Bock <rb...@eudoxos.de> wrote:

IMO compile time strings (can contain anything) and names are different animals. It should be possible to turn names into compile time strings. I am not sure about the other way.

Strings into names is a well-defined transformation: “just” compile the string. Nothing else really makes sense.
I can't really comment on the "just".



Names into strings leaves a lot of headroom for lazy implementations. What does __func__ look like in a constructor, destructor, operator overload, or conversion function? Typically it doesn’t look like source code. More common to see compatibility with dlsym than with the C++ compiler, and no reason to expect that trend to reverse.
The internal representation is not what this is about. The idea is to have

- tokens
- identifiers
- or even expressions

as literals which can be used as template arguments for instance.



And I agree that goal should be to combine reflection (analysis) and names (synthesis).

Not sure how well all this fits into the existing template model.
I'd be very interested in your doubts.



It could be nice to add basic scope awareness to the current preprocessor, just at the level of minding namespaces and counting braces. We already have the ## operator for synthesizing names and the # operator for getting strings, they just suffer from lack of encapsulation.

True, but that is a different topic in my eyes, isn't it?


Best,

Roland

Richard Smith

unread,
Apr 1, 2015, 2:57:03 PM4/1/15
to std-pr...@isocpp.org
On Tue, Mar 31, 2015 at 11:36 PM, Roland Bock <rb...@eudoxos.de> wrote:
On 2015-03-31 23:06, Richard Smith wrote:
On Tue, Mar 31, 2015 at 12:15 PM, Thiago Macieira <thi...@macieira.org> wrote:
On Tuesday 31 March 2015 21:05:50 Roland Bock wrote:
> template<name A, name B>

Please note that this syntax will conflict with a concept of type "name".

It would also conflict with a type with name "name" (where A and B would be non-type template parameters).
Right.


One could imagine solving this conflict by adding a standard-library type std::name (or std::reflect::name, or whatever), with `...` being a literal of that type. Following similar systems in other languages (Template Haskell, lisp macros, ...), there should probably be an explicit reification syntax ($foo, perhaps) to disambiguate between mention and expansion. So:

namespace std { using name = decltype(`blah`); }
template<std::name A> struct X { // name is a normal identifier here
  Y<A> ya; // pass name A to Y
  Y<$A> yda; // look up the name represented by A, pass the result to Y
I am a bit confused here, what would be the differences of

Y<A>
Y<$A>

here?

As the comments say, the first passes the name A to Y, the second passes the result of looking up the name to Y. So, given

  constexpr std::name foo = `bar`;

X<`foo`>::ya would have type Y<`foo`>
X<`foo`>::yda would have type Y<`bar`>
 
Or did you intend to write

Y<`A`>
Y<$A>

No; Y<`A`> is something different again.

?

  int k1 = ya.A; // look up member A in ya
  int k2 = ya.$A; // look up the name represented by A
};
X<`foo`> xfoo;

If such a thing were formally proposed, it would seem prudent to consider whether it can be extended naturally to capture other constructs (types, expressions, statements).
Whoops! That is a fascinating idea that requires some digestion time :-)

Btw: Quite a few people suggested concatenation of names at CppCon and fantasized about never needing the preprocessor again (not sure about that).

--

Richard Smith

unread,
Apr 1, 2015, 3:06:22 PM4/1/15
to std-pr...@isocpp.org
On Tue, Mar 31, 2015 at 11:11 PM, Roland Bock <rb...@eudoxos.de> wrote:
On 2015-03-31 22:57, Richard Smith wrote:
On Tue, Mar 31, 2015 at 12:58 PM, Alex B <deva...@gmail.com> wrote:
I'm not sure if the idea floated around, but why not use some kind of compile time string instead of backticks or the name keyword?
 
template <typename T, __CompileTimeString s>
struct X
{
   T __name
(s);
};
 
X
<int, "value"> var;
var.value = 0;

 
 
What __CompileTimeString precisely is would be to determine (are there any proposals about compile-time strings in the reflection group?).
Operator __name (bikeshed) would convert a compile time string to an actual name.
 
Combine this + compile time strings + other cool reflection features and we get something really powerful.

A name (or rather, an unqualified-id) is not a string.
Agreed, although I think that a name should be convertible to some kind of string.

Consider, for instance, the name 'operator int', which should presumably be the same name as 'operator T' if T is a typedef for 'int'.
 
Depends on where you look, right?

No, I don't think it should, and I don't think it can. The translation from the token sequence to a name should be performed where the name lexically appears.
 
template<typename T, std::name X>
struct foo
{
   $X();
};

struct bar
{
   using T = int;
   using A = foo<int,   `operator T`>;
   using B = foo<float, `operator T`>;

Both these references to T should find the T declared just above, that is, 'int'. If not, it's not possible to form a name representing a conversion operator to some local type, or template type parameter, or similar. And consider

  `operator T::U<V>`

We need to know whether T::U is a template to be able to parse this sort of thing, which means we should bind it early.

   using C = foo<float, `operator int`>;
   static_assert(std::is_same<A, C>::value == true, "");
   static_assert(std::is_same<B, C>::value == false, "");
};

In case B, $X would evaluate to `operator float`, because T is float in foo.

Being able to snoop into other scopes by deferring the lookup of a component of a name seems like a bad idea to me. I find it hard to imagine your interpretation of code like the above being the desired one.

Roland Bock

unread,
Apr 1, 2015, 3:37:54 PM4/1/15
to std-pr...@isocpp.org
Got it. I did not think of the possibility that foo might be a std::name itself.

Roland Bock

unread,
Apr 1, 2015, 4:49:23 PM4/1/15
to std-pr...@isocpp.org
That sounds wrong to me. Here's another example:

template<typename T1, typename T2, std::name N1, std::name N2>
struct named_pair
{
   T1 N1;
   T2 N2;
};

struct sample
{
  using A = int;
  using B = float;
  using C = named_pair<char, char, `A`, `B`>;
};
using D = named_pair<char, char, `A`, `B`>;

If I understand you correctly, since A and B are defined in sample, you would interpret them as `int` and `float`, which would lead to a compile error for C, while D is OK?

It would mean that named_pair<char, char, `A`, `B`> could mean different things depending on the scope it is used in?



If not, it's not possible to form a name representing a conversion operator to some local type, or template type parameter, or similar.
I wonder whether this is a real use case? I am asking because I have no idea yet how I would use `operator T`?


template<typename T, std::name X>
struct sample
{
   X() {/* What goes here? */}
};



And consider

  `operator T::U<V>`

We need to know whether T::U is a template to be able to parse this sort of thing, which means we should bind it early.

Assuming my interpretation, using this would imply a bunch of requirements for the template and/or other template parameters, but that is nothing new, is it?

using X = foo<`operator T::U<V>`>;

For this to compile, foo has to have a type T such that T::U<V> makes sense. But that's ok, I'd say.


Best,

Roland

Richard Smith

unread,
Apr 1, 2015, 5:31:58 PM4/1/15
to std-pr...@isocpp.org
No. Look at [basic]p8:

"Two names are the same if
--  they are identifiers composed of the same character sequence, or
[...]
-- they are conversion-function-ids formed with the same type, or
[...]"

When an identifier is used as a name, the meaning of that name is determined by the sequence of characters in the identifier, so `A` should not be resolved to a type (how could it? a type's not a name).

When a conversion-function-id is used as a name, the meaning of that name is determined by the identity of the type, and doesn't depend on how the type is spelled. So `operator A` should be resolved to `operator int` and would be the same name as any other way of spelling that name.
 
It would mean that named_pair<char, char, `A`, `B`> could mean different things depending on the scope it is used in?


If not, it's not possible to form a name representing a conversion operator to some local type, or template type parameter, or similar.
I wonder whether this is a real use case? I am asking because I have no idea yet how I would use `operator T`?

template<typename T, std::name X>
struct sample
{
   X() {/* What goes here? */}
};


And consider

  `operator T::U<V>`

We need to know whether T::U is a template to be able to parse this sort of thing, which means we should bind it early.

Assuming my interpretation, using this would imply a bunch of requirements for the template and/or other template parameters, but that is nothing new, is it?

using X = foo<`operator T::U<V>`>;

For this to compile, foo has to have a type T such that T::U<V> makes sense. But that's ok, I'd say.


Best,

Roland

--

Roland Bock

unread,
Apr 2, 2015, 1:31:14 AM4/2/15
to std-pr...@isocpp.org
Ah, got it.
Thanks for the reference and the explanation :-)

Now that I read your mail and the respective section of the standard, it seems quite obvious...

Best,

Roland
Reply all
Reply to author
Forward
0 new messages