I, personally, don't like the idea of quoted strings used for
identifiers, but, I suppose, that's just a bikeshed.
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 Type, typename Name, // New template parameter type Type Value> struct Foo { Type _<Name> = Value; };
// ------------------------ 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;
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, 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
[...]
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.
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.
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.
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
and
ms.foo
to
ms._<__name::foo>
Comparing ms.foo to ms._<__name::foo> I know what I'd prefer :-)
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?
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>;};
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.
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.
Le 24/03/15 10:53, Roland Bock a écrit :
I'm not a compiler implementer, but I suspect that easy to implement would be an important factor.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.
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`.
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.
Right, it is the namespace __name. This IMO is an implementation detail.
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.
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.
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 :-)
The last.
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?
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>;};
I would say yes, but I have not think enough about the implications. Do you see a problem?
How about
struct S
{
int a;
int b;
};
struct T
{
int _<__name::a>;
int b;
};
Wouldd they also be equivalent?
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.
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.Can you elaborate?
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.
Hopping the transformation I have described could make it more probable.
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.
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: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.Can you elaborate?
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.
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.
Hopping the transformation I have described could make it more probable.
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.
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.
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 {struct S {int a;};int _<__name::a>;};
would result on a compile error as S is declared twice.
You're suggesting an alternative way by saying
So the approaches are almost the same:
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`){}
};
template<typename X>
struct T
{
int _<X>;
T(int _<X, __name::_arg>):X(_<X,
__name::_arg>){}
};
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 aheadHopping the transformation I have described could make it more probable.
Yes, I got that and I am thankful for the discussion!
Sorry for the distraction.
Rp;and Bock >Do we (plan to) have aliases for members?
Thumbs up for that!!!
And member functions too.
> b) you do not have to declare those aliases for every loop you write
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.
Note: I don't think we should allow type names unless you can explain how that would differ from what we already have :-).
template <typename T, __CompileTimeString s>
struct X
{
T __name(s);
};
X<int, "value"> var;
var.value = 0;
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.
--
---
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/.
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
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.
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'.
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 hereY<A> ya; // pass name A to YY<$A> yda; // look up the name represented by A, pass the result to Y
int k1 = ya.A; // look up member A in yaint 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).
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.
And I agree that goal should be to combine reflection (analysis) and names (synthesis).
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.
On 2015-03-31 23:06, Richard Smith wrote:
Right.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).
I am a bit confused here, what would be the differences of
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 hereY<A> ya; // pass name A to YY<$A> yda; // look up the name represented by A, pass the result to Y
Y<A>
Y<$A>
here?
Or did you intend to write
Y<`A`>
Y<$A>
?
Whoops! That is a fascinating idea that requires some digestion time :-)int k1 = ya.A; // look up member A in yaint 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).
Btw: Quite a few people suggested concatenation of names at CppCon and fantasized about never needing the preprocessor again (not sure about that).
--
On 2015-03-31 22:57, Richard Smith wrote:
Agreed, although I think that a name should be convertible to some kind of string.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.
Depends on where you look, right?Consider, for instance, the name 'operator int', which should presumably be the same name as 'operator T' if T is a typedef for 'int'.
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.
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.
It would mean that named_pair<char, char, `A`, `B`> could mean different things depending on the scope it is used in?
I wonder whether this is a real use case? I am asking because I have no idea yet how I would use `operator T`?If not, it's not possible to form a name representing a conversion operator to some local type, or template type parameter, or similar.
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
--