[RFC] Uniform designated initializers and arguments for C++ (implementation available)

1,239 views
Skip to first unread message

TONGARI J

unread,
Apr 13, 2016, 4:33:28 AM4/13/16
to ISO C++ Standard - Future Proposals
Hi,

The draft can be viewed at:
http://jamboree.github.io/designator-draft.html

The prototype based on Clang can be found at:


I'm not sure if it's proper to submit a new proposal before C++17 is settled, anyway, I'd like to make this work public now.
Please let me know if anyone wants to be a champion if it's submitted.


Thanks

Giovanni Piero Deretta

unread,
Apr 13, 2016, 6:13:51 AM4/13/16
to ISO C++ Standard - Future Proposals
On Wednesday, April 13, 2016 at 9:33:28 AM UTC+1, TONGARI J wrote:
Hi,

The draft can be viewed at:
http://jamboree.github.io/designator-draft.html

The prototype based on Clang can be found at:


I'm not sure if it's proper to submit a new proposal before C++17 is settled, anyway, I'd like to make this work public now.


Not much to say. Seems a pretty good proposal. I like the syntax for opting-in on named function arguments.

-- gpd

 


Thanks

Vadim Petrochenkov

unread,
Apr 13, 2016, 6:29:09 AM4/13/16
to ISO C++ Standard - Future Proposals
As a some practical example, qemu is a large project written in C and it uses designated initializers all over the place (they are irreplaceable for large aggregates).
If you try to compile qemu code as C++, then designated initializers is probably the largest reason preventing it from compiling.
I'm not talking that qemu is ever going to be converted to C++, but designated initializers (even in their simplest form `{ .field = value }`) can be helpful for migrating other C libraries into C++.

Andrey Semashev

unread,
Apr 13, 2016, 6:54:32 AM4/13/16
to std-pr...@isocpp.org
In a few places in the proposal document, you state that
copy-initialization is used with designated initializers. This is not so
with normal initializers or positional function arguments and depends on
whether the initialized member/argument is a reference. Is this
difference intentional? If yes, then what's the rationale for it (as it
doesn't look reasonable to me)?

TONGARI J

unread,
Apr 13, 2016, 8:33:51 AM4/13/16
to std-pr...@isocpp.org
In the "Design decisions" section?
It's not intentional, I'll try to improve the wording...

qwa...@gmail.com

unread,
Apr 13, 2016, 1:59:03 PM4/13/16
to ISO C++ Standard - Future Proposals
2.3 can be solved by much smaller change in standard.
Do you consider allowing such syntax:
obj.f(default, b)

Richard Smith

unread,
Apr 13, 2016, 3:31:20 PM4/13/16
to std-pr...@isocpp.org
This looks more or less like how I'd always imagined designated initializers would work in C++. Thanks for writing this! One question, you say:

"3.2.1.1 Function redeclaration

If a function is declared with designatable parameters and/or default arguments, it cannot be redeclared with designatable parameters and/or default arguments, otherwise, the program is ill-formed."

This seems very strange to me. I would expect that:
a) every declaration of the same function must specify the same designatable parameters, with the same names, and
b) perhaps overloading on designated parameter names should be permitted

One example of why (b) would be useful:

struct my_complex {
  my_complex(double .rho, double .theta);
  my_complex(double .real, double .imag);
};

Here, these constructors could *only* be called with designated parameter names, because any non-designated call would be ambiguous.

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

Ville Voutilainen

unread,
Apr 13, 2016, 3:33:33 PM4/13/16
to ISO C++ Standard - Future Proposals
On 13 April 2016 at 22:31, Richard Smith <ric...@metafoo.co.uk> wrote:
> This looks more or less like how I'd always imagined designated initializers
> would work in C++. Thanks for writing this! One question, you say:
>
> "3.2.1.1 Function redeclaration
>
> If a function is declared with designatable parameters and/or default
> arguments, it cannot be redeclared with designatable parameters and/or
> default arguments, otherwise, the program is ill-formed."
>
> This seems very strange to me. I would expect that:
> a) every declaration of the same function must specify the same designatable
> parameters, with the same names, and
> b) perhaps overloading on designated parameter names should be permitted
>
> One example of why (b) would be useful:
>
> struct my_complex {
> my_complex(double .rho, double .theta);
> my_complex(double .real, double .imag);
> };
>
> Here, these constructors could *only* be called with designated parameter
> names, because any non-designated call would be ambiguous.


Just out of curiosity, how do you plan to mangle those names? The
reason why I ask is that
sure, yeah, such overloading could be handy, but it also introduces
additional complexity
to deal with.

Richard Smith

unread,
Apr 13, 2016, 3:48:38 PM4/13/16
to std-pr...@isocpp.org
On Wed, Apr 13, 2016 at 12:33 PM, Ville Voutilainen <ville.vo...@gmail.com> wrote:
On 13 April 2016 at 22:31, Richard Smith <ric...@metafoo.co.uk> wrote:
> This looks more or less like how I'd always imagined designated initializers
> would work in C++. Thanks for writing this! One question, you say:
>
> "3.2.1.1 Function redeclaration
>
> If a function is declared with designatable parameters and/or default
> arguments, it cannot be redeclared with designatable parameters and/or
> default arguments, otherwise, the program is ill-formed."
>
> This seems very strange to me. I would expect that:
> a) every declaration of the same function must specify the same designatable
> parameters, with the same names, and
> b) perhaps overloading on designated parameter names should be permitted
>
> One example of why (b) would be useful:
>
> struct my_complex {
>   my_complex(double .rho, double .theta);
>   my_complex(double .real, double .imag);
> };
>
> Here, these constructors could *only* be called with designated parameter
> names, because any non-designated call would be ambiguous.

Just out of curiosity, how do you plan to mangle those names?

As a modifier immediately preceding or following the mangled type of the parameter. (And for what it's worth, I think it'd also be useful for the name to be part of the function type, with a standard conversion to remove the parameter names, but I'm not necessarily convinced the added complexity is worth it.)

The
reason why I ask is that
sure, yeah, such overloading could be handy, but it also introduces
additional complexity
to deal with.

I agree, but I don't think this mangling aspect is a big deal. The original proposal introduces more significant mangling complexities regardless of whether we allow overloading -- in a function template signature, decltype(f(.a=T())) and decltype(f(T())) must mangle differently if T is a dependent type, for instance.

If we go ahead with this proposal but don't allow overloading, I expect people to fake it:

struct my_complex {
private:
  struct polar_tag {};
  struct cartesian_tag {};
public:
  my_complex(double .rho, double .theta, polar_tag = {});
  my_complex(double .real, double .imag, cartesian_tag = {});
};

Ville Voutilainen

unread,
Apr 13, 2016, 4:10:39 PM4/13/16
to ISO C++ Standard - Future Proposals
On 13 April 2016 at 22:48, Richard Smith <ric...@metafoo.co.uk> wrote:
>> The
>> reason why I ask is that
>> sure, yeah, such overloading could be handy, but it also introduces
>> additional complexity
>> to deal with.
> I agree, but I don't think this mangling aspect is a big deal. The original
> proposal introduces more significant mangling complexities regardless of
> whether we allow overloading -- in a function template signature,
> decltype(f(.a=T())) and decltype(f(T())) must mangle differently if T is a
> dependent type, for instance.

Well, I don't think it's the biggest hurdle either. A much bigger
hurdle is having this proposal
survive five minutes after its presentation, because the previous ones
dropped dead in about that time.
Let me draw you a paraphrased caricature of what usually happens when
named arguments are proposed:

1) a "both sides must opt in" proposal comes in. The response is "this
doesn't help anyone in the near future, and makes
function declarations more complex, for questionable benefit. Either
use stronger and different types, or a parameter
type that has named setters, or <insert various work-around
suggestions here>..."

2) a "works automatically" proposal comes in. The response is "well,
that doesn't work with the standard library,
and authors of other libraries don't want to have the introduced
requirement to provide stable names without their opt-in,
so <insert the work-arounds again>..."

3) in either case, the complexity and its ratio to the apparent
benefit get strongly questioned.

Yeah, sure, this proposal doesn't look half bad. But I have no
illusions that it would somehow magically avoid all of the
opposing arguments that all previous proposals for named arguments have faced.

> If we go ahead with this proposal but don't allow overloading, I expect
> people to fake it:
>
> struct my_complex {
> private:
> struct polar_tag {};
> struct cartesian_tag {};
> public:
> my_complex(double .rho, double .theta, polar_tag = {});
> my_complex(double .real, double .imag, cartesian_tag = {});
> };

For some values of "people", maybe. But introducing such new kind of
overloading will probably meet resistance,
and be fuel for questioning the cost/benefit ratio.

Matthew Woehlke

unread,
Apr 13, 2016, 4:50:08 PM4/13/16
to std-pr...@isocpp.org
On 2016-04-13 16:10, Ville Voutilainen wrote:
> A much bigger hurdle is having this proposal survive five minutes
> after its presentation, because the previous ones dropped dead in
> about that time.
>
> 1) a "both sides must opt in" proposal comes in. The response is
> "this doesn't help anyone in the near future, and makes function
> declarations more complex, for questionable benefit.

Would it be the end of the world to make the following be synonymous?

int square(int .x);
int square([[named("x")]] int);
int square([[named("x")]] int not_x);

I realize that attributes with meaning aren't loved, but the advantage
this has is addressing the 'not useful *now*' complaint, by making it
possible to write code (without using macros) that can be compiled with
a C++11 compiler that still provides named arguments when used with a
newer compiler. It also addresses the standard library to some extent,
as it permits adding named arguments to the API while still allowing the
implementation to use whatever it wants for names.

Note that this would however preclude encoding the argument names into
the ABI.

--
Matthew

Richard Smith

unread,
Apr 13, 2016, 4:58:45 PM4/13/16
to std-pr...@isocpp.org
On Wed, Apr 13, 2016 at 1:33 AM, TONGARI J <tong...@gmail.com> wrote:
Hi,

The draft can be viewed at:
http://jamboree.github.io/designator-draft.html

The prototype based on Clang can be found at:

Have you considered the interaction of this proposal with forwarding functions and with types like std::function? I would find it problematic if correct forwarding is not possible for functions with designated parameters. I think you will encounter resistance if something like this is not possible, and we have no known path forward to support it:

  void f(int .foo, int .bar);
  std::function<void(int .foo, int .bar)> fn = f;
  fn(.bar = 1, .foo = 2);

One possible way to make this work could be:
 (a) designated parameter names to become part of a function type, and
 (b) add a way to write a function template that accepts a pack of designated parameters, capturing both the names and the values, and allowing both to be used when forwarding
But (b) in particular seems like it would add a lot of complexity. I hope there's a better alternative.
 
I'm not sure if it's proper to submit a new proposal before C++17 is settled, anyway, I'd like to make this work public now.
Please let me know if anyone wants to be a champion if it's submitted.


Thanks

--

barry....@gmail.com

unread,
Apr 13, 2016, 5:13:54 PM4/13/16
to ISO C++ Standard - Future Proposals
2) a "works automatically" proposal comes in. The response is "well,
that doesn't work with the standard library,
and authors of other libraries don't want to have the introduced
requirement to provide stable names without their opt-in,
so <insert the work-arounds again>..."

Is that really the argument against "works automatically"? Library authors could simply not document/advertise those names (i.e. use those at your own risk).  Keeping argument names stable doesn't strike me as more burdensome than keep everything else stable. How often do you change an argument name, but nothing else? 

Besides, Python has had this ability for years and been wildly successful at sharing libraries. 

Ville Voutilainen

unread,
Apr 13, 2016, 5:19:00 PM4/13/16
to ISO C++ Standard - Future Proposals
On 13 April 2016 at 23:49, Matthew Woehlke <mwoehlk...@gmail.com> wrote:
> On 2016-04-13 16:10, Ville Voutilainen wrote:
>> A much bigger hurdle is having this proposal survive five minutes
>> after its presentation, because the previous ones dropped dead in
>> about that time.
>>
>> 1) a "both sides must opt in" proposal comes in. The response is
>> "this doesn't help anyone in the near future, and makes function
>> declarations more complex, for questionable benefit.
>
> Would it be the end of the world to make the following be synonymous?
>
> int square(int .x);
> int square([[named("x")]] int);
> int square([[named("x")]] int not_x);

I'm not quite sure what you're suggesting there. Make them synonymous, as in
allow all of those various syntaxes?

> I realize that attributes with meaning aren't loved, but the advantage
> this has is addressing the 'not useful *now*' complaint, by making it

No, the "not useful now" complaint is that in order to use named arguments,
the call site obviously needs to say that it uses them, but in an
opt-in proposal
the declaration also has to say so. Therefore both a user and a (whatever, not
necessarily the standard) library author need to decide that they want
such a thing.
That is in contrast to the other kinds of proposals where the caller
can just name
(except when the arguments can't be named, like with standard library functions)
the arguments so that the names are picked up from some declaration. Whatever
syntax you come up with, the issues remain. You may disagree on whether that's
an issue, but the real complaint is that some audiences don't want
named arguments
because then there's potentially lots of churn (or at least requests
for such churn)
in many codebases to add support for them, for arguably questionable benefit.

Andrey Semashev

unread,
Apr 13, 2016, 5:21:40 PM4/13/16
to std-pr...@isocpp.org
On 2016-04-13 22:31, Richard Smith wrote:
> This looks more or less like how I'd always imagined designated
> initializers would work in C++. Thanks for writing this! One question,
> you say:
>
> "3.2.1.1 Function redeclaration
>
> If a function is declared with designatable parameters and/or default
> arguments, it cannot be redeclared with designatable parameters and/or
> default arguments, otherwise, the program is ill-formed."
>
> This seems very strange to me. I would expect that:
> a) every declaration of the same function must specify the same
> designatable parameters, with the same names, and
> b) perhaps overloading on designated parameter names should be permitted
>
> One example of why (b) would be useful:
>
> struct my_complex {
> my_complex(double .rho, double .theta);
> my_complex(double .real, double .imag);
> };
>
> Here, these constructors could *only* be called with designated
> parameter names, because any non-designated call would be ambiguous.

I don't think this is a good idea. If those were two functions, there
would be no way to resolve the ambiguity when taking an address of the
function.

Ville Voutilainen

unread,
Apr 13, 2016, 5:23:42 PM4/13/16
to ISO C++ Standard - Future Proposals
On 14 April 2016 at 00:13, <barry....@gmail.com> wrote:
>> 2) a "works automatically" proposal comes in. The response is "well,
>> that doesn't work with the standard library,
>> and authors of other libraries don't want to have the introduced
>> requirement to provide stable names without their opt-in,
>> so <insert the work-arounds again>..."
> Is that really the argument against "works automatically"?

Well, considering that it was an argument that was raised when discussing
http://open-std.org/JTC1/SC22/WG21/docs/papers/2014/n4172.htm,
sure, it's an argument against "works automatically". Whether you agree with
that argument is a separate matter.

> Library authors could simply not document/advertise those names (i.e. use those at your own
> risk). Keeping argument names stable doesn't strike me as more burdensome
> than keep everything else stable. How often do you change an argument name,
> but nothing else?

Based on the arguments made when discussing the aforementioned paper,
depending on who you
ask, the answers are 1) that will not work 2) it is more burdensome
and 3) often enough to oppose
such a proposal.

> Besides, Python has had this ability for years and been wildly successful at
> sharing libraries.

Yeah, but the argument is that C++ library writers know that they
don't need to deal with it, and don't want
it imposed on them, and then we go back to "both sides opt in" which
will not work with the standard library,
because unlike python and lisp, the argument names are not standardized in C++.

Matthew Woehlke

unread,
Apr 13, 2016, 5:39:32 PM4/13/16
to std-pr...@isocpp.org
On 2016-04-13 17:18, Ville Voutilainen wrote:
> On 13 April 2016 at 23:49, Matthew Woehlke <mwoehlk...@gmail.com> wrote:
>> On 2016-04-13 16:10, Ville Voutilainen wrote:
>>> A much bigger hurdle is having this proposal survive five minutes
>>> after its presentation, because the previous ones dropped dead in
>>> about that time.
>>>
>>> 1) a "both sides must opt in" proposal comes in. The response is
>>> "this doesn't help anyone in the near future, and makes function
>>> declarations more complex, for questionable benefit.
>>
>> Would it be the end of the world to make the following be synonymous?
>>
>> int square(int .x);
>> int square([[named("x")]] int);
>> int square([[named("x")]] int not_x);
>
> I'm not quite sure what you're suggesting there. Make them synonymous, as in
> allow all of those various syntaxes?

Yes. As in, all three lines would a) be allowed and b) have the same
meaning.

>> I realize that attributes with meaning aren't loved, but the advantage
>> this has is addressing the 'not useful *now*' complaint, by making it
>
> No, the "not useful now" complaint is that in order to use named
> arguments, the call site obviously needs to say that it uses them,
> but in an opt-in proposal the declaration also has to say so.
> Therefore both a user and a (whatever, not necessarily the standard)
> library author need to decide that they want such a thing.

But this is a direct result of the parameter names not being implicitly
part of the API (i.e. your "authors of other libraries don't want to
have the introduced requirement to provide stable names without their
opt-in"). If both complaints are fatal to any possible proposal, then we
cannot make progress, because we *must* accept one or the other of those
limitations.

That may be the case. If so, however, it would be good to know (and
perhaps document?) so that we can shoot down any named argument
proposals immediately as being fundamentally intractable.

The idea behind the above was to provide a not-too-painful mechanism
that would let authors that aren't willing to commit to C++20 to write
libraries that can require only C++11 but still provide named argument
support without too much pain, so that libraries can be forward-looking
in order to mitigate that complaint.

> That is in contrast to the other kinds of proposals where the caller
> can just name (except when the arguments can't be named, like with
> standard library functions) the arguments so that the names are
> picked up from some declaration.

Again, the suggestion is intended to mitigate this (especially the
standard library case), by having an API argument name that is separate
from the "actual" name as used by the implementation. (I would argue
that if you aren't willing to commit to a stable API argument name, you
aren't committing to a stable API, period.)

> You may disagree on whether that's an issue

I don't. I recognize that there are legitimate concerns from both sides,
and am trying to come up with a way to at least mitigate (note: *not*
"eliminate"; I suspect that's impossible) them so that progress can be
made. I made the above suggestion in the hopes that it might be useful
in that sense, though I suspect it isn't, much :-(.

--
Matthew

Tony V E

unread,
Apr 13, 2016, 5:42:50 PM4/13/16
to Andrey Semashev
‎0. Naming is hard. 

Something to consider: how would I later rename a param? Particularly once users have started ‎using the old name and I don't want to break them.  

I could rename the whole function, but see #0 above. (and if it is the constructor, do I rename the whole class?)

So it might be nice to allow ways to overload, even if the overload just turns around and calls the original:

‎my_complex(double .real, double .imag);
// whoops! Coding guidelines say no short forms
my_complex(doule .real, double .imaginary) ;



Sent from my BlackBerry portable Babbage Device
  Original Message  
From: Andrey Semashev
Sent: Wednesday, April 13, 2016 5:21 PM
To: std-pr...@isocpp.org
Reply To: std-pr...@isocpp.org
Subject: Re: [std-proposals] [RFC] Uniform designated initializers and arguments for C++ (implementation available)
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/abd1385f-dfeb-cee2-184d-d5f6936896fa%40gmail.com.

Ville Voutilainen

unread,
Apr 13, 2016, 5:55:47 PM4/13/16
to ISO C++ Standard - Future Proposals
On 14 April 2016 at 00:39, Matthew Woehlke <mwoehlk...@gmail.com> wrote:
>>> Would it be the end of the world to make the following be synonymous?
>>>
>>> int square(int .x);
>>> int square([[named("x")]] int);
>>> int square([[named("x")]] int not_x);
>>
>> I'm not quite sure what you're suggesting there. Make them synonymous, as in
>> allow all of those various syntaxes?
>
> Yes. As in, all three lines would a) be allowed and b) have the same
> meaning.

Well, I will not make end-of-the-world predictions, but such an idea
is guaranteed to generate
strong opposition. Partly because it complicates the idea, partly
because it's using attributes
for semantics.

>> No, the "not useful now" complaint is that in order to use named
>> arguments, the call site obviously needs to say that it uses them,
>> but in an opt-in proposal the declaration also has to say so.
>> Therefore both a user and a (whatever, not necessarily the standard)
>> library author need to decide that they want such a thing.
>
> But this is a direct result of the parameter names not being implicitly
> part of the API (i.e. your "authors of other libraries don't want to
> have the introduced requirement to provide stable names without their
> opt-in"). If both complaints are fatal to any possible proposal, then we
> cannot make progress, because we *must* accept one or the other of those
> limitations.

Well, perhaps that's one of the reasons why named arguments have
failed every time.

> That may be the case. If so, however, it would be good to know (and
> perhaps document?) so that we can shoot down any named argument
> proposals immediately as being fundamentally intractable.

Well - 'automatic' ones are highly questionable because they don't
work with the standard library,
unless everything in it changes, which is not backwards compatible,
which means we can probably
skip to an 'opt-in'. I'm not sure whether I know all the
counterarguments and opposition of it.
I have indeed toyed with the idea of writing a rumination paper on why
named arguments have failed,
as far as I know and have observed it (which doesn't cover all the old
proposals), but perhaps I was
kinda hoping I wouldn't need to. :)

> The idea behind the above was to provide a not-too-painful mechanism
> that would let authors that aren't willing to commit to C++20 to write
> libraries that can require only C++11 but still provide named argument
> support without too much pain, so that libraries can be forward-looking
> in order to mitigate that complaint.

Ah, so the expectation is that a C++11 implementation would just
ignore the attributes it doesn't grok,
and a C++20 implementation would give them semantic meaning. That is a
silent change in meaning
of code, and those tend to have a short life expectancy.

>> That is in contrast to the other kinds of proposals where the caller
>> can just name (except when the arguments can't be named, like with
>> standard library functions) the arguments so that the names are
>> picked up from some declaration.
> Again, the suggestion is intended to mitigate this (especially the
> standard library case), by having an API argument name that is separate
> from the "actual" name as used by the implementation. (I would argue
> that if you aren't willing to commit to a stable API argument name, you
> aren't committing to a stable API, period.)

Well, today, and for multiple decades, an argument name has been an
implementation detail,
nothing more. Hence the argument that not providing a stable name
means not providing a
stable API cannot possibly be sound.

paulp...@gmail.com

unread,
Apr 13, 2016, 6:33:43 PM4/13/16
to ISO C++ Standard - Future Proposals
Am Mittwoch, 13. April 2016 22:58:45 UTC+2 schrieb Richard Smith:
On Wed, Apr 13, 2016 at 1:33 AM, TONGARI J <tong...@gmail.com> wrote:
Hi,

The draft can be viewed at:
http://jamboree.github.io/designator-draft.html

The prototype based on Clang can be found at:

Have you considered the interaction of this proposal with forwarding functions and with types like std::function? I would find it problematic if correct forwarding is not possible for functions with designated parameters. I think you will encounter resistance if something like this is not possible, and we have no known path forward to support it:

  void f(int .foo, int .bar);
  std::function<void(int .foo, int .bar)> fn = f;
  fn(.bar = 1, .foo = 2);

One possible way to make this work could be:
 (a) designated parameter names to become part of a function type, and

What are the downsides of disignated parameters being part of a function type?
 
 (b) add a way to write a function template that accepts a pack of designated parameters, capturing both the names and the values, and allowing both to be used when forwarding

Wouldn't this require some kind of reflection mechanism? (To save the names).

Richard Smith

unread,
Apr 13, 2016, 8:38:38 PM4/13/16
to std-pr...@isocpp.org
Note that I'm also in favour of designatable parameter names being part of the function type. With that change, this would work the same as any other overload / address of function situation.

Richard Smith

unread,
Apr 13, 2016, 8:48:29 PM4/13/16
to std-pr...@isocpp.org
On Wed, Apr 13, 2016 at 3:33 PM, <paulp...@gmail.com> wrote:
Am Mittwoch, 13. April 2016 22:58:45 UTC+2 schrieb Richard Smith:
On Wed, Apr 13, 2016 at 1:33 AM, TONGARI J <tong...@gmail.com> wrote:
Hi,

The draft can be viewed at:
http://jamboree.github.io/designator-draft.html

The prototype based on Clang can be found at:

Have you considered the interaction of this proposal with forwarding functions and with types like std::function? I would find it problematic if correct forwarding is not possible for functions with designated parameters. I think you will encounter resistance if something like this is not possible, and we have no known path forward to support it:

  void f(int .foo, int .bar);
  std::function<void(int .foo, int .bar)> fn = f;
  fn(.bar = 1, .foo = 2);

One possible way to make this work could be:
 (a) designated parameter names to become part of a function type, and

What are the downsides of disignated parameters being part of a function type?

I think increased complexity is the big one. Presumably we'd want this to work:

void f(int .a, int .b);
void (*p)(int, int) = &f;

... which implies there is a conversion that discards the names from the type. Adding names should presumably also be permissible (perhaps with an explicit cast), as should calling through a function pointer whose parameter names differ from those of the function.

It also means that everyone who is decomposing a function type via a template needs to change their code to cope with designators. The way that works would depend on how we tackle part (b):
 
 (b) add a way to write a function template that accepts a pack of designated parameters, capturing both the names and the values, and allowing both to be used when forwarding

Wouldn't this require some kind of reflection mechanism? (To save the names).

"Reflection" is not a well-defined term; we can't say what is or isn't a reflection mechanism. But I don't see this as being much different from template argument deduction deducing the types of parameters from the type of arguments (is that reflection? opinions vary).

We could imagine something like this:

template<typename F, designator ...D, typename ...T> decltype(auto) f(F f, T &&... .D) {
  return f(0, .D = std::forward<T>(D) ...);
}

That is: a new kind of template parameter representing a designator, and designator names are looked up to see if they name such a parameter before assuming they're an opaque identifier. (Plus a notional "empty designator" that is deduced in cases where no designator is specified.) I don't think I like this approach, and as noted, I hope there's a better answer.
 
But (b) in particular seems like it would add a lot of complexity. I hope there's a better alternative.
 
I'm not sure if it's proper to submit a new proposal before C++17 is settled, anyway, I'd like to make this work public now.
Please let me know if anyone wants to be a champion if it's submitted.


Thanks

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

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

gmis...@gmail.com

unread,
Apr 13, 2016, 9:36:07 PM4/13/16
to ISO C++ Standard - Future Proposals
This looks like a very well written and presented proposal. I think it could be better evaluated though if there were more discussion about it in the context of existing API designs that have been created with languages that already have the features the proposed. In particular regarding named function parameters.

My own experience of using libraries with extensive numbers of parameters are the Microsoft COM object libraries that lay behind applications like Microsoft Word and Excel etc.
See here:

This proposal would seem to make using such libraries a lot easier?

As important to me though is whether people consider such API designs good? And do we want to encourage more of them by making them easier?

So to my mind we have to evaluate this proposal against real existing APIs while also determining if those real API's need to be as complex as they are so we. This should help us verify if this proposal helps with using such API's and reveal whether an alternative API design or language feature would be better to get the kinds of api's we do want.

I don't feel I can appreciate the value of this proposal without such discussion

If more recent API's could be discussed it might even more relevant. I am thinking some of the web driving api's (think setting html attributes from code etc). like those in the ASP.NET's MVC API, which are C# API's. Maybe that could be addressed? Perhaps a lot of lessons/encouragement could be learned from that?

So I think ultimately we have to compare this proposal against these types of API's and evaluate the alternatives to appreciate this proposal, don't we?

Thanks for the proposal so far.

TONGARI J

unread,
Apr 13, 2016, 9:56:20 PM4/13/16
to ISO C++ Standard - Future Proposals, qwa...@gmail.com
Actually I did. This could be useful in its own, but it's outside the scope of this proposal.

TONGARI J

unread,
Apr 13, 2016, 11:09:55 PM4/13/16
to ISO C++ Standard - Future Proposals
Hi Richard,


On Thursday, April 14, 2016 at 3:31:20 AM UTC+8, Richard Smith wrote:
This looks more or less like how I'd always imagined designated initializers would work in C++. Thanks for writing this! One question, you say:

"3.2.1.1 Function redeclaration

If a function is declared with designatable parameters and/or default arguments, it cannot be redeclared with designatable parameters and/or default arguments, otherwise, the program is ill-formed."

This seems very strange to me. I would expect that:
a) every declaration of the same function must specify the same designatable parameters, with the same names, and
b) perhaps overloading on designated parameter names should be permitted

One example of why (b) would be useful:

struct my_complex {
  my_complex(double .rho, double .theta);
  my_complex(double .real, double .imag);
};

Here, these constructors could *only* be called with designated parameter names, because any non-designated call would be ambiguous.

There are some principles I set when designing the feature:
* Don't interfere with the type system
* Don't require ABI changes
* Don't complicate the function declaration

I did consider allowing multiple function declarations with different designators in early stage, but the idea soon got dropped.
The idea was different from what you would expect though:
void f(int .a, int .b);
void f(int .x, int .y);

The 2 declarations will be the same function, not 2 overloads. This may be useful in some case but also confusing and may complicate the implementation so I dropped it.

As for allowing multiple identical declarations, my argument against it is for consistency, consider default arguments:
void f(int a = 1);
void f(int a = 1);

It's not allowed even though the 2 are lexically identical, and I think the same should apply to designatable parameters as well.

What you suggested in the my_complex example is to make them overloads, which is a design decision I don't want to take in this proposal.
 
Have you considered the interaction of this proposal with forwarding functions and with types like std::function? I would find it problematic if correct forwarding is not possible for functions with designated parameters. I think you will encounter resistance if something like this is not possible, and we have no known path forward to support it:

  void f(int .foo, int .bar);
  std::function<void(int .foo, int .bar)> fn = f;
  fn(.bar = 1, .foo = 2);

One possible way to make this work could be:
 (a) designated parameter names to become part of a function type, and
 (b) add a way to write a function template that accepts a pack of designated parameters, capturing both the names and the values, and allowing both to be used when forwarding
But (b) in particular seems like it would add a lot of complexity. I hope there's a better alternative.

Actually, forwarding function is explicitly listed in my non-goals, that's why I deliberately disable the indirect use in the end of section 3.2.3.

If I were going to support forwarding functions, that means I have to make designatable parameters part of the type as you suggested in (a), and that breaks my design principles.
My choice for designated arguments is much like parameter pack, that is, don't make them first class objects.

I should emphasize how reluctant I am in interfering with the type system - it's a no-go for me if adding designatable parameters to existing API would break the ABI.

TONGARI J

unread,
Apr 13, 2016, 11:20:01 PM4/13/16
to ISO C++ Standard - Future Proposals
Do you mean the argument names cannot be standardized in the future? why?
I don't see any problem with the opt-in model I proposed, as I suggested in section 4.1.

TONGARI J

unread,
Apr 13, 2016, 11:26:55 PM4/13/16
to ISO C++ Standard - Future Proposals, mwoehlk...@gmail.com
Why not macros? Is the following really that bad?

#if has_designatable_parameters
#define NAMED_PARAM(p) .p
#else
#define NAMED_PARAM(p) p
#endif

void f(int NAMED_PARAM(a)); // use it now

Jens Maurer

unread,
Apr 14, 2016, 9:03:03 AM4/14/16
to std-pr...@isocpp.org
On 04/14/2016 02:48 AM, Richard Smith wrote:
> I think increased complexity is the big one. Presumably we'd want this to work:
>
> void f(int .a, int .b);
> void (*p)(int, int) = &f;
>
> ... which implies there is a conversion that discards the names from the type. Adding names should presumably also be permissible (perhaps with an explicit cast), as should calling through a function pointer whose parameter names differ from those of the function.

Note that I've already done some work for function pointer conversions (4.12)
when I introduced "noexcept" to the type system. That doesn't mean it's
easy, but the locations that need changes are at least text-searchable.

> That is: a new kind of template parameter representing a designator,
> and designator names are looked up to see if they name such a
> parameter before assuming they're an opaque identifier. (Plus a
> notional "empty designator" that is deduced in cases where no
> designator is specified.) I don't think I like this approach, and as
> noted, I hope there's a better answer.

Agreed, in particular with the last sentence.

Jens

Matthew Woehlke

unread,
Apr 14, 2016, 10:19:34 AM4/14/16
to std-pr...@isocpp.org
On 2016-04-13 17:55, Ville Voutilainen wrote:
> Well - 'automatic' ones are highly questionable because they don't
> work with the standard library, unless everything in it changes,
> which is not backwards compatible,

...Why? Parameter names today have no effect on the generated code. If
we take the route that they don't affect the ABI, then why can't they be
changed in order to conform to a new codification of what they should be?

(OTOH, of course, that's a massive change to codify all the parameter
names everywhere... it could be done piecemeal, though, with the
standard providing no guarantees except where the names are specified.)

> On 14 April 2016 at 00:39, Matthew Woehlke wrote:
>> On 13 April 2016 at 23:49, Matthew Woehlke wrote:
>>> Would it be the end of the world to make the following be synonymous?
>>>
>>> int square(int .x);
>>> int square([[named("x")]] int);
>>> int square([[named("x")]] int not_x);
>>
>> The idea behind the above was to provide a not-too-painful mechanism
>> that would let authors that aren't willing to commit to C++20 to write
>> libraries that can require only C++11 but still provide named argument
>> support without too much pain, so that libraries can be forward-looking
>> in order to mitigate that complaint.
>
> Ah, so the expectation is that a C++11 implementation would just
> ignore the attributes it doesn't grok, and a C++20 implementation
> would give them semantic meaning. That is a silent change in meaning
> of code, and those tend to have a short life expectancy.

...no? There are no named parameter semantics in C++11/14/17. The only
change would be that a new feature is available in (e.g.) C++20. I don't
see how this would be any different from C11 designated initializers vs.
C89/C99. The only change is a pure *addition*; you can do something you
couldn't do before.

Mind, I'm assuming that parameter names do *not* affect the ABI, and
that if it affects the function pointer type, a function pointer type
with named parameters decays into the same signature less the parameter
names. If either of those is not the case, then yes, my idea would not
be reasonable :-).

>> (I would argue that if you aren't willing to commit to a stable API
>> argument name, you aren't committing to a stable API, period.)
>
> Well, today, and for multiple decades, an argument name has been an
> implementation detail, nothing more. Hence the argument that not
> providing a stable name means not providing a stable API cannot
> possibly be sound.

I didn't say "won't", I said "not willing". There is a subtle
difference. Yes, the name has no compiler-defined semantic at present.
What it *does* have is a user-defined semantic. The argument, in other
words, is to ask why you would need to *change* the parameter name, once
a reasonable one has been selected (and therefore made part of the API),
if the user semantic of the parameter has not changed. The obvious
reasons (change of semantic meaning of the parameter, massive change of
coding style) imply that your API *is actually different*, and
therefore, an API break is not only warranted, it may be a *feature*.

Especially with my idea that would allow separating API name from
implementation name, such a change becomes *by definition* an API
change. (Note also that I'm assuming an opt-in approach at least at the
political level; i.e. in case of an automatic approach, a library might
explicitly disclaim API compatibility across versions w.r.t. parameter
names.)

I'd be interested in examples why you would *need* to change a parameter
name (as exported in the API) when the semantic meaning of the parameter
is *not* changed.

--
Matthew

Matthew Woehlke

unread,
Apr 14, 2016, 10:25:06 AM4/14/16
to std-pr...@isocpp.org
On 2016-04-13 23:26, TONGARI J wrote:
> Why not macros? Is the following really that bad?
>
> #if has_designatable_parameters
> #define NAMED_PARAM(p) .p
> #else
> #define NAMED_PARAM(p) p
> #endif

Yes. You have to either undefine this macro after use, or prefix it with
something unique. Remember, you're talking about a library that may be
one of many libraries used by an application, all of which want to use
such a macro, and all of which have their own subtly different version
thereof. (The only reasonable way around this might be to have the macro
specified by the standard, with the proviso that users may define it -
with a specified definition - if it is not defined.)

Also, don't discount being able to give the parameter different names in
the API vs. the implementation. Or people that just plain hate macros.

--
Matthew

Matthew Woehlke

unread,
Apr 14, 2016, 10:32:19 AM4/14/16
to std-pr...@isocpp.org
On 2016-04-13 17:42, Tony V E wrote:
> ‎0. Naming is hard.

Yup :-).

> Something to consider: how would I later rename a param? Particularly once users have started ‎using the old name and I don't want to break them.
>
> I could rename the whole function, but see #0 above. (and if it is the constructor, do I rename the whole class?)
>
> So it might be nice to allow ways to overload, even if the overload just turns around and calls the original:
>
> ‎my_complex(double .real, double .imag);
> // whoops! Coding guidelines say no short forms
> my_complex(doule .real, double .imaginary) ;

If this is the *only* case of renaming you want to support (i.e. same
functions with "options" for names), you could easily extend my
attribute idea to do that. Even better:

my_complex(
double
[[named("real")]]
p_u,
double
[[deprecated]] [[named("imag")]]
[[named("imaginary")]]
p_v);

(Syntax definitely needs some love, but you get the idea... and I was
even able to make the old name deprecated, so code using it will emit a
warning! Yes, I can do that with your version also, but the warning will
be slightly more specific with mine, as the compiler can tell you
specifically that a parameter name is what is deprecated and can suggest
the replacement name. For a case like this where the code semantics
haven't changed, it would even be fairly easy to write a tool to
automatically "update" user code.)

--
Matthew

Ville Voutilainen

unread,
Apr 14, 2016, 10:41:13 AM4/14/16
to ISO C++ Standard - Future Proposals
On 14 April 2016 at 17:19, Matthew Woehlke <mwoehlk...@gmail.com> wrote:
> On 2016-04-13 17:55, Ville Voutilainen wrote:
>> Well - 'automatic' ones are highly questionable because they don't
>> work with the standard library, unless everything in it changes,
>> which is not backwards compatible,
>
> ...Why? Parameter names today have no effect on the generated code. If
> we take the route that they don't affect the ABI, then why can't they be
> changed in order to conform to a new codification of what they should be?

Because my standard library implementation needs to work with elder
front-ends, so
in order to add conditional support for named arguments, you need a
strong argument that named
arguments are useful and necessary. Every proposal author that has
proposed named
arguments has thus far failed to do that.

>>>> Would it be the end of the world to make the following be synonymous?
>>>>
>>>> int square(int .x);
>>>> int square([[named("x")]] int);
>>>> int square([[named("x")]] int not_x);
>>>
>>> The idea behind the above was to provide a not-too-painful mechanism
>>> that would let authors that aren't willing to commit to C++20 to write
>>> libraries that can require only C++11 but still provide named argument
>>> support without too much pain, so that libraries can be forward-looking
>>> in order to mitigate that complaint.
>>
>> Ah, so the expectation is that a C++11 implementation would just
>> ignore the attributes it doesn't grok, and a C++20 implementation
>> would give them semantic meaning. That is a silent change in meaning
>> of code, and those tend to have a short life expectancy.
>
> ...no? There are no named parameter semantics in C++11/14/17. The only
> change would be that a new feature is available in (e.g.) C++20. I don't
> see how this would be any different from C11 designated initializers vs.
> C89/C99. The only change is a pure *addition*; you can do something you
> couldn't do before.

Yes, except that the meaning of the function declaration changes depending on
which front-end you use. I'm probably going to use a macro for such a
thing, at which
point the usefulness of supporting such an attribute there becomes
nil, because I can
just macro the dot-name of the argument while I'm at it.

>> Well, today, and for multiple decades, an argument name has been an
>> implementation detail, nothing more. Hence the argument that not
>> providing a stable name means not providing a stable API cannot
>> possibly be sound.
>
> I didn't say "won't", I said "not willing". There is a subtle
> difference. Yes, the name has no compiler-defined semantic at present.
> What it *does* have is a user-defined semantic. The argument, in other

I fail to see what that user-defined "semantic" could possibly be.

> words, is to ask why you would need to *change* the parameter name, once
> a reasonable one has been selected (and therefore made part of the API),
> if the user semantic of the parameter has not changed. The obvious
> reasons (change of semantic meaning of the parameter, massive change of
> coding style) imply that your API *is actually different*, and
> therefore, an API break is not only warranted, it may be a *feature*.

I fail to see how a change of coding style implies that the API is
different. Sure,
such an API break *may* be a feature, it may not.

> Especially with my idea that would allow separating API name from
> implementation name, such a change becomes *by definition* an API
> change. (Note also that I'm assuming an opt-in approach at least at the
> political level; i.e. in case of an automatic approach, a library might
> explicitly disclaim API compatibility across versions w.r.t. parameter
> names.)
>
> I'd be interested in examples why you would *need* to change a parameter
> name (as exported in the API) when the semantic meaning of the parameter
> is *not* changed.

I'm not sure whether I can find such examples. But other people report
they can easily
find examples where a declaration has a long elaborated name for a
parameter, and
the definition has a very terse name. The terse names may easily be changed over
time, and might e.g. become less terse. Even with your proposed
name-mapping, that
means changing multiple declarations, so named arguments do make such
name changes
much harder. So the question becomes "why bother"?

Matthew Woehlke

unread,
Apr 14, 2016, 11:20:35 AM4/14/16
to std-pr...@isocpp.org
On 2016-04-14 10:41, Ville Voutilainen wrote:
> On 14 April 2016 at 17:19, Matthew Woehlke wrote:
>> On 2016-04-13 17:55, Ville Voutilainen wrote:
>>> Well - 'automatic' ones are highly questionable because they don't
>>> work with the standard library, unless everything in it changes,
>>> which is not backwards compatible,
>>
>> ...Why? Parameter names today have no effect on the generated code. If
>> we take the route that they don't affect the ABI, then why can't they be
>> changed in order to conform to a new codification of what they should be?
>
> Because my standard library implementation needs to work with elder
> front-ends,

...but we were talking about *automatic* named parameters. We already
established that the parameter names don't matter currently, so why is
changing them to match an updated standard an issue?

In any case, I'm not seeing an issue here that can't be fixed with
macros, or (at least for "elderly" being minimum C++11) my attribute idea.

>>>>> Would it be the end of the world to make the following be synonymous?
>>>>>
>>>>> int square(int .x);
>>>>> int square([[named("x")]] int);
>>>>> int square([[named("x")]] int not_x);
>>>>
>>>> The idea behind the above was to provide a not-too-painful mechanism
>>>> that would let authors that aren't willing to commit to C++20 to write
>>>> libraries that can require only C++11 but still provide named argument
>>>> support without too much pain, so that libraries can be forward-looking
>>>> in order to mitigate that complaint.
>>>
>>> Ah, so the expectation is that a C++11 implementation would just
>>> ignore the attributes it doesn't grok, and a C++20 implementation
>>> would give them semantic meaning. That is a silent change in meaning
>>> of code, and those tend to have a short life expectancy.
>>
>> ...no? There are no named parameter semantics in C++11/14/17. The only
>> change would be that a new feature is available in (e.g.) C++20. I don't
>> see how this would be any different from C11 designated initializers vs.
>> C89/C99. The only change is a pure *addition*; you can do something you
>> couldn't do before.
>
> Yes, except that the meaning of the function declaration changes

...in what way? You keep asserting this, but I am not understanding what
change you think is occurring. I can only assume therefore that we
aren't talking about the same implementation.

Given:

int add(int [[named("a")] a, int [[named("b")]] b);

In both cases you have a function with the signature `int (int, int)`
(either because names aren't part of the type, or at least that decays
to that). In both cases, you can call the function like `add(2, 3);`.
All that has changed is that in one case you can *also* call it like
`add(.b = 3, .a = 2);`.

Again, this wasn't an issue for C11. Why would it be an issue for C++?

> I'm probably going to use a macro for such a thing, at which point
> the usefulness of supporting such an attribute there becomes nil,
> because I can just macro the dot-name of the argument while I'm at
> it.

Maybe. The advantage of the attribute is that it does not require a
macro (for people that hate those, and for people that care about
namespacing it properly), and allows the API name and implementation
name to be different.

Is that worth it? Maybe not :-).

>>> Well, today, and for multiple decades, an argument name has been an
>>> implementation detail, nothing more. Hence the argument that not
>>> providing a stable name means not providing a stable API cannot
>>> possibly be sound.
>>
>> I didn't say "won't", I said "not willing". There is a subtle
>> difference. Yes, the name has no compiler-defined semantic at present.
>> What it *does* have is a user-defined semantic. The argument, in other
>
> I fail to see what that user-defined "semantic" could possibly be.

The function/meaning of the parameter. For example, in `sin(double
.radians)`, the "user semantic" of `radians` is 'an angle in radians'.
If I want to rename that to `degrees`, it is a *feature* that this is an
API break, because the API did in fact change in a non-compatible
manner. This is different from "compiler semantic" because the compiler
(at least currently) has no way to diagnose if I pass degrees to a
function that expects radians, or vice versa.

Given a function where the parameter name is part of the API, I expect
that name to relate to the user-semantic of the parameter. A change in
the name¹ therefore implies a change in the user-semantic of the
parameter, which is an API break. Hopefully that makes sense now?

(¹ Tony V.E. already covered the one case I can think of where one might
reasonably want to rename a parameter where no semantic change occurs.
See also my reply there.)

>> I'd be interested in examples why you would *need* to change a parameter
>> name (as exported in the API) when the semantic meaning of the parameter
>> is *not* changed.
>
> I'm not sure whether I can find such examples. But other people
> report they can easily find examples where a declaration has a long
> elaborated name for a parameter, and the definition has a very terse
> name. The terse names may easily be changed over time, and might e.g.
> become less terse.

Sounds like an advantage of my attribute idea.

> Even with your proposed name-mapping, that means changing multiple
> declarations,

Not if we don't allow overloading on names?²

// header
int foo(int [[named("bar")]] maybe_bar);

// implementation
int foo(int not_bar) // note: API name not repeated
{ ... }

(² FWIW, I'll note that Python doesn't - at least not without jumping
through hoops - allow overloading *at all*, so this doesn't seem like an
unreasonable limitation. Especially if we ever get strong type aliases,
which would allow type overloading for most of the cases where you'd
otherwise want name overloading.)

--
Matthew

Ville Voutilainen

unread,
Apr 14, 2016, 11:30:07 AM4/14/16
to ISO C++ Standard - Future Proposals
On 14 April 2016 at 18:20, Matthew Woehlke <mwoehlk...@gmail.com> wrote:
> On 2016-04-14 10:41, Ville Voutilainen wrote:
>> On 14 April 2016 at 17:19, Matthew Woehlke wrote:
>>> On 2016-04-13 17:55, Ville Voutilainen wrote:
>>>> Well - 'automatic' ones are highly questionable because they don't
>>>> work with the standard library, unless everything in it changes,
>>>> which is not backwards compatible,
>>>
>>> ...Why? Parameter names today have no effect on the generated code. If
>>> we take the route that they don't affect the ABI, then why can't they be
>>> changed in order to conform to a new codification of what they should be?
>>
>> Because my standard library implementation needs to work with elder
>> front-ends,
>
> ...but we were talking about *automatic* named parameters. We already
> established that the parameter names don't matter currently, so why is
> changing them to match an updated standard an issue?

Because I expect that in that newer standard they won't be uglified,
whereas they
need to be when an older standard is used, so now I have to write conditional
code. I don't have time to do such nonsensical work unless the
motivation for it is strong.

> In any case, I'm not seeing an issue here that can't be fixed with
> macros, or (at least for "elderly" being minimum C++11) my attribute idea.

Yay, so we fix a problem with macros, and now we have two problems. :)
Well, duh, I can now also call the function like that because the meaning
of the function declaration changed. Had it not changed, I wouldn't be able
to call it that way. The meaning now changes automatically, regardless
of whether
I change anything anywhere. I might not want that, which is why I would probably
macro-conditionalize the declaration anyway, and therefore the
attribute doesn't help
me at all.

>>>> Well, today, and for multiple decades, an argument name has been an
>>>> implementation detail, nothing more. Hence the argument that not
>>>> providing a stable name means not providing a stable API cannot
>>>> possibly be sound.
>>>
>>> I didn't say "won't", I said "not willing". There is a subtle
>>> difference. Yes, the name has no compiler-defined semantic at present.
>>> What it *does* have is a user-defined semantic. The argument, in other
>>
>> I fail to see what that user-defined "semantic" could possibly be.
> The function/meaning of the parameter. For example, in `sin(double
> .radians)`, the "user semantic" of `radians` is 'an angle in radians'.
> If I want to rename that to `degrees`, it is a *feature* that this is an
> API break, because the API did in fact change in a non-compatible
> manner. This is different from "compiler semantic" because the compiler
> (at least currently) has no way to diagnose if I pass degrees to a
> function that expects radians, or vice versa.

In other words, you think you can make names have a "user semantic" if
named arguments are supported. That doesn't mean that all names have such
"user semantics", because currently none of them do, hence my confusion about
what these "user semantics" are. Today, they are a figment of imagination. With
named arguments, they might be a thing, although they might not be. I wouldn't
call that the strongest rationale for named arguments.

TONGARI J

unread,
Apr 14, 2016, 11:49:58 AM4/14/16
to ISO C++ Standard - Future Proposals, mwoehlk...@gmail.com
On Thursday, April 14, 2016 at 11:20:35 PM UTC+8, Matthew Woehlke wrote:
Not if we don't allow overloading on names?²

  // header
  int foo(int [[named("bar")]] maybe_bar);

  // implementation
  int foo(int not_bar) // note: API name not repeated
  { ... }

Out of curious, why bother to specify maybe_bar in the header? Is the name used anywhere?

FWIW, in my proposal, this is allowed:

int foo(int .bar);
int foo(int not_bar);

But this is not:

int foo(int .bar);
int foo(int .not_bar);

section 5.6 gave some rationale. What Tony V.E described is path 1, and what Richard Smith described is path 2.

Matthew Woehlke

unread,
Apr 14, 2016, 11:57:59 AM4/14/16
to std-pr...@isocpp.org
On 2016-04-14 11:30, Ville Voutilainen wrote:
> On 14 April 2016 at 18:20, Matthew Woehlke wrote:
>> ...but we were talking about *automatic* named parameters. We already
>> established that the parameter names don't matter currently, so why is
>> changing them to match an updated standard an issue?
>
> Because I expect that in that newer standard they won't be uglified,
> whereas they need to be when an older standard is used,

I don't get it. If parameter names don't matter in "old" C++, why must
the 'ugly' names be used? What am I missing?

>> On 2016-04-14 10:41, Ville Voutilainen wrote:
>>> [...] the meaning of the function declaration changes
>>
>> ...in what way? You keep asserting this, but I am not understanding what
>> change you think is occurring. I can only assume therefore that we
>> aren't talking about the same implementation.
>>
>> Given:
>>
>> int add(int [[named("a")] a, int [[named("b")]] b);
>>
>> In both cases you have a function with the signature `int (int, int)`
>> (either because names aren't part of the type, or at least that decays
>> to that). In both cases, you can call the function like `add(2, 3);`.
>> All that has changed is that in one case you can *also* call it like
>> `add(.b = 3, .a = 2);`.
>
> Well, duh, I can now also call the function like that because the meaning
> of the function declaration changed. Had it not changed, I wouldn't be able
> to call it that way.

Huh?

Let me try rephrasing this. Let's say we have a C struct:

struct foo { int bar };

In C89, the "meaning" of this struct is that I can construct it like
`{1}`. In C11, the "meaning" is that I can (additionally) construct it
like `{.bar=1}`. This was evidently not a problem for C11. What "change
of meaning" occurs in C++ that is different from this example and
therefore problematic?

>> The function/meaning of the parameter. For example, in `sin(double
>> .radians)`, the "user semantic" of `radians` is 'an angle in radians'.
>> If I want to rename that to `degrees`, it is a *feature* that this is an
>> API break, because the API did in fact change in a non-compatible
>> manner. This is different from "compiler semantic" because the compiler
>> (at least currently) has no way to diagnose if I pass degrees to a
>> function that expects radians, or vice versa.
>
> In other words, you think you can make names have a "user semantic" if
> named arguments are supported.

They would (when employed) become part of the public API in the same
manner as the function name, with similar issues arising if you want to
change the names. (In fact, I hadn't realized this before... the same
problems that would arise with renaming named parameters *already exist*
w.r.t. the name of the function itself. And yet, somehow we're managing.)

> That doesn't mean that all names have such
> "user semantics", because currently none of them do, hence my confusion about
> what these "user semantics" are. Today, they are a figment of imagination. With
> named arguments, they might be a thing, although they might not be. I wouldn't
> call that the strongest rationale for named arguments.

Ah... I think you perhaps misunderstand the intended scope. This
particular argument is not meant as an argument *for* named parameters,
only as an argument why the concern about "committing my API to a name"
is possibly overemphasized.

--
Matthew

Ville Voutilainen

unread,
Apr 14, 2016, 12:03:06 PM4/14/16
to ISO C++ Standard - Future Proposals
On 14 April 2016 at 18:57, Matthew Woehlke <mwoehlk...@gmail.com> wrote:
> On 2016-04-14 11:30, Ville Voutilainen wrote:
>> On 14 April 2016 at 18:20, Matthew Woehlke wrote:
>>> ...but we were talking about *automatic* named parameters. We already
>>> established that the parameter names don't matter currently, so why is
>>> changing them to match an updated standard an issue?
>>
>> Because I expect that in that newer standard they won't be uglified,
>> whereas they need to be when an older standard is used,
>
> I don't get it. If parameter names don't matter in "old" C++, why must
> the 'ugly' names be used? What am I missing?

Macros must not leak in?

>
>>> On 2016-04-14 10:41, Ville Voutilainen wrote:
>>>> [...] the meaning of the function declaration changes
>>>
>>> ...in what way? You keep asserting this, but I am not understanding what
>>> change you think is occurring. I can only assume therefore that we
>>> aren't talking about the same implementation.
>>>
>>> Given:
>>>
>>> int add(int [[named("a")] a, int [[named("b")]] b);
>>>
>>> In both cases you have a function with the signature `int (int, int)`
>>> (either because names aren't part of the type, or at least that decays
>>> to that). In both cases, you can call the function like `add(2, 3);`.
>>> All that has changed is that in one case you can *also* call it like
>>> `add(.b = 3, .a = 2);`.
>>
>> Well, duh, I can now also call the function like that because the meaning
>> of the function declaration changed. Had it not changed, I wouldn't be able
>> to call it that way.
>
> Huh?
>
> Let me try rephrasing this. Let's say we have a C struct:
>
> struct foo { int bar };
>
> In C89, the "meaning" of this struct is that I can construct it like
> `{1}`. In C11, the "meaning" is that I can (additionally) construct it
> like `{.bar=1}`. This was evidently not a problem for C11. What "change
> of meaning" occurs in C++ that is different from this example and
> therefore problematic?

With an opt-in, there is no problem, since I can choose with a macro
whether I opt in or not.
I can do that without the attributes you propose. So, again, those
attributes don't seem to be useful.
I don't want to use such attributes because then the opt-in happens
half-automatically, although
I admit that's not a huge problem because nobody is forcing me to use
those attributes.

Matthew Woehlke

unread,
Apr 14, 2016, 12:05:07 PM4/14/16
to std-pr...@isocpp.org
On 2016-04-14 11:49, TONGARI J wrote:
> On Thursday, April 14, 2016 at 11:20:35 PM UTC+8, Matthew Woehlke wrote:
>> // header
>> int foo(int [[named("bar")]] maybe_bar);
>>
>> // implementation
>> int foo(int not_bar) // note: API name not repeated
>> { ... }
>
> Out of curious, why bother to specify maybe_bar in the header?

Purely for illustrative purposes. I wanted to show that the
implementation name in a declaration can be different from the
implementation name in the definition. I could also have omitted it
entirely.

> FWIW, in my proposal, this is allowed:
>
> int foo(int .bar);
> int foo(int not_bar);

Cool :-). FWIW, I think that's a good approach.

--
Matthew

Matthew Woehlke

unread,
Apr 14, 2016, 12:46:53 PM4/14/16
to std-pr...@isocpp.org
On 2016-04-14 12:03, Ville Voutilainen wrote:
> On 14 April 2016 at 18:57, Matthew Woehlke wrote:
>> On 2016-04-14 11:30, Ville Voutilainen wrote:
>>> On 14 April 2016 at 18:20, Matthew Woehlke wrote:
>>>> ...but we were talking about *automatic* named parameters. We already
>>>> established that the parameter names don't matter currently, so why is
>>>> changing them to match an updated standard an issue?
>>>
>>> Because I expect that in that newer standard they won't be uglified,
>>> whereas they need to be when an older standard is used,
>>
>> I don't get it. If parameter names don't matter in "old" C++, why must
>> the 'ugly' names be used? What am I missing?
>
> Macros must not leak in?

Do you mean that, for example std::map::find cannot use `key` as the
parameter name in case the user has done `#define key` somewhere?

If yes, a) wouldn't that be a problem for a "modern" standard library
with codified parameter names also, and b) isn't this solved by my
attribute idea? (In particular, because the parameter name given in an
attribute is quoted, it should not be subject to PP 'tampering'. This is
not the case with `.name` as in the original proposal.)

--
Matthew

Ville Voutilainen

unread,
Apr 14, 2016, 1:54:09 PM4/14/16
to ISO C++ Standard - Future Proposals
On 14 April 2016 at 19:46, Matthew Woehlke <mwoehlk...@gmail.com> wrote:
>>> I don't get it. If parameter names don't matter in "old" C++, why must
>>> the 'ugly' names be used? What am I missing?
>>
>> Macros must not leak in?
>
> Do you mean that, for example std::map::find cannot use `key` as the
> parameter name in case the user has done `#define key` somewhere?

Yes.

> If yes, a) wouldn't that be a problem for a "modern" standard library
> with codified parameter names also, and b) isn't this solved by my

Excellent question. :) Yes, I think a modern library should solve that
problem too.

> attribute idea? (In particular, because the parameter name given in an
> attribute is quoted, it should not be subject to PP 'tampering'. This is

Yay.. I mean, eurgh. :)

> not the case with `.name` as in the original proposal.)

How so? I don't think you can #define .name whatever.

Richard Smith

unread,
Apr 14, 2016, 1:57:27 PM4/14/16
to std-pr...@isocpp.org
On Wed, Apr 13, 2016 at 8:09 PM, TONGARI J <tong...@gmail.com> wrote:
Hi Richard,

On Thursday, April 14, 2016 at 3:31:20 AM UTC+8, Richard Smith wrote:
This looks more or less like how I'd always imagined designated initializers would work in C++. Thanks for writing this! One question, you say:

"3.2.1.1 Function redeclaration

If a function is declared with designatable parameters and/or default arguments, it cannot be redeclared with designatable parameters and/or default arguments, otherwise, the program is ill-formed."

This seems very strange to me. I would expect that:
a) every declaration of the same function must specify the same designatable parameters, with the same names, and
b) perhaps overloading on designated parameter names should be permitted

One example of why (b) would be useful:

struct my_complex {
  my_complex(double .rho, double .theta);
  my_complex(double .real, double .imag);
};

Here, these constructors could *only* be called with designated parameter names, because any non-designated call would be ambiguous.

There are some principles I set when designing the feature:
* Don't interfere with the type system

Please allow me to suggest an alternative principle: integrate into the existing language.

Far too often, people proposing features make the mistake of making their feature be its own special island that has rules different from the rest of the language. This is a trap to be avoided. Language continuity is important: C++20 should look and feel like a development of C++17, in order to make it easier for programmers and programs to adapt, but we don't get there by minimizing the amount of change to the language or specification, we get there by making the changes complete so they feel like an extension of what came before.

A feature that does not integrate into the type system, but whose design would naturally lead to type system changes, is flawed. In my opinion, you should first design the feature, and then analyze the type system implications, not the other way around. (And if that leads to no type system changes, that's fine, of course.)

* Don't require ABI changes

I think you should distinguish between "don't change the ABI of existing libraries" and "don't change the ABI when designated parameters are added to an interface". The first seems like an important principle. The second, less so -- as long as the library maintainer still gets to control their ABI as they see fit.
 
* Don't complicate the function declaration

I did consider allowing multiple function declarations with different designators in early stage, but the idea soon got dropped.
The idea was different from what you would expect though:
void f(int .a, int .b);
void f(int .x, int .y);

The 2 declarations will be the same function, not 2 overloads. This may be useful in some case but also confusing and may complicate the implementation so I dropped it.

As for allowing multiple identical declarations, my argument against it is for consistency, consider default arguments:
void f(int a = 1);
void f(int a = 1);

It's not allowed even though the 2 are lexically identical, and I think the same should apply to designatable parameters as well.

What you suggested in the my_complex example is to make them overloads, which is a design decision I don't want to take in this proposal.
 
Have you considered the interaction of this proposal with forwarding functions and with types like std::function? I would find it problematic if correct forwarding is not possible for functions with designated parameters. I think you will encounter resistance if something like this is not possible, and we have no known path forward to support it:

  void f(int .foo, int .bar);
  std::function<void(int .foo, int .bar)> fn = f;
  fn(.bar = 1, .foo = 2);

One possible way to make this work could be:
 (a) designated parameter names to become part of a function type, and
 (b) add a way to write a function template that accepts a pack of designated parameters, capturing both the names and the values, and allowing both to be used when forwarding
But (b) in particular seems like it would add a lot of complexity. I hope there's a better alternative.

Actually, forwarding function is explicitly listed in my non-goals, that's why I deliberately disable the indirect use in the end of section 3.2.3.

The fact that this is a non-goal is going to make your proposal very unappealing to some portion of the committee.
 
If I were going to support forwarding functions, that means I have to make designatable parameters part of the type as you suggested in (a), and that breaks my design principles.
My choice for designated arguments is much like parameter pack, that is, don't make them first class objects.

I should emphasize how reluctant I am in interfering with the type system - it's a no-go for me if adding designatable parameters to existing API would break the ABI.

Adding parameters with default arguments to existing API would break the ABI too, as would many other API changes. With a suitable overload resolution rule, I'd expect to be able to change an API that does this:

  struct A {
    void f(int a, int b);
  };

... to do this:

  struct A {
    void f(int a, int b);
    void f(int .a, int .b) { f(a, b); }
  };

... as a non-breaking change (except if someone is taking the address of the function), for people who need perfect ABI compatibility.

Matthew Woehlke

unread,
Apr 14, 2016, 2:38:18 PM4/14/16
to std-pr...@isocpp.org
On 2016-04-14 13:54, Ville Voutilainen wrote:
> On 14 April 2016 at 19:46, Matthew Woehlke wrote:
>> (In particular, because the parameter name given in an attribute is
>> quoted, it should not be subject to PP 'tampering'. This is not the
>> case with `.name` as in the original proposal.)
>
> How so? I don't think you can #define .name whatever.

No, but if you `#define name oops`, `.name` will be replaced with
`.oops`. (There is a certain nasty case that recently bit me where
_GNU_SOURCE "adds" a pseudo-member to a system struct using exactly this
kludge; ugh. Also, I just checked¹ :-).)

(¹ `echo -e '#define foo bar\nnone(.foo = 5);' | gcc -E -`)

--
Matthew

Matthew Woehlke

unread,
Apr 14, 2016, 2:54:11 PM4/14/16
to std-pr...@isocpp.org
On 2016-04-14 13:57, Richard Smith wrote:
> Adding parameters with default arguments to existing API would break the
> ABI too, as would many other API changes. With a suitable overload
> resolution rule, I'd expect to be able to change an API that does this:
>
> struct A {
> void f(int a, int b);
> };
>
> ... to do this:
>
> struct A {
> void f(int a, int b);
> void f(int .a, int .b) { f(a, b); }
> };
>
> ... as a non-breaking change (except if someone is taking the address of
> the function), for people who need perfect ABI compatibility.

I'd expect that this:

struct A {
void f(int .a, int .b);
};

...is a (mostly?¹) non-breaking change. Yes, function pointer and all;
*if* named parameters are part of the function type, then IMO the
respective pointer types should implicitly decay to pointers with fewer
named arguments. IOW:

int f(int .a, int .b);
int (*p1)(int .a, int .b) = &f; // okay
int (*p2)(int, int .b) = &f; // also okay
int (*p3)(int, int) = &f; // also okay

int g(int .a, int .b);
int g(int .x, int .y);
int (*p4)(int .a, int .b) = &g; // okay
int (*p5)(int, int .b) = &g; // still okay
int (*p6)(int, int) = &g; // error: ambiguous
int (*p7)(int, int) = p4; // okay

(¹ There might be cases where the change in type of the function pointer
breaks something, but not just due to trying to feed it to something
that expects the no-named-parameters pointer type.)

However, I'll mention again that IMO there are non-trivial issues
including parameter names in the type system, and that the language most
commonly cited for supporting named parameters (Python) *doesn't allow
overloading* (at all). Also, that most cases that come to mind where
you'd want it seem better solved with strong type aliases. This causes
me to strongly question the value of including parameter names in the
type system.

--
Matthew

Farid Mehrabi

unread,
Apr 14, 2016, 3:53:18 PM4/14/16
to std-proposals
​Feels like designated initialization of ​POD types is less controversial, more conservative, yet more elegant than expected.
For naming function arguments one might wrap it in a function object:

struct caller;
void caller::operator()()
{
    f(x,y);
};
caller{.x=1,.y=2}();

or even shorter:

struct caller;
caller::~caller()
{
    f(x,y);
};
caller{.x=1,.y=2};

Of Course it doesn't cover every corner case of function calls such as constructor with named args(eg. std::vector::vector or std::complex::complex). 

For constructors I would rather put all args in a POD type:

std::complex<double>   c1{ polar{ .magnitude=1.0, .angle=degree(30) } };
std::complex<double>   c1{ cartesian{ .real=1.0, .image=1.0 } };
 
I agree that this is not as sweat as functions with named variables.

Another more fundamental problem with types is that uninitialized members will try to use implicit default constructors. This might partially be overcome by forcing the programmer to explicitly initialize members:

struct bar{
      //new syntax:
      explicit int x;// explicitly initialized
      int y;            // don't care about y
};

bar b1{1,2};  //ok
bar b2{.x=2};//ok
bar b3;         //compile error: x not initialized

or even stricter:

//new syntax:
explicit struct foo //every member is explicitly initialized, except if defaulted
{
     int x;
     int y=1;  // y has default value
};

foo f1{.x=0};         //ok
foo f2{.y=2,.x=0}; //ok
foo f3;                  //compile error: x not initialized

regards,
FM.
 

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



--
how am I supposed to end the twisted road of  your hair in such a dark night??
unless the candle of your face does shed some light upon my way!!!

Tony V E

unread,
Apr 14, 2016, 5:19:25 PM4/14/16
to Matthew Woehlke
> ‎Huh?
>
> Let me try rephrasing this. Let's say we have a C struct:
>
> struct foo { int bar };
>
> In C89, the "meaning" of this struct is that I can construct it like `{1}`. In C11, the "meaning" is that I can (additionally) construct it‎ like `{.bar=1}`. This was evidently not a problem for C11. What "change of meaning" occurs in C++ that is different from this example and therefore problematic?
>


In a struct, the member names are already part of the API.



Sent from my BlackBerry portable Babbage Device
  Original Message  
From: Matthew Woehlke
Sent: Thursday, April 14, 2016 11:57 AM
To: std-pr...@isocpp.org
Reply To: std-pr...@isocpp.org
Subject: [std-proposals] Re: [RFC] Uniform designated initializers and arguments for C++ (implementation available)
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/neoelk%249ko%241%40ger.gmane.org.

Bryce Glover

unread,
Apr 14, 2016, 5:19:27 PM4/14/16
to Ville Voutilainen, std-pr...@isocpp.org
Ville Voutilainen <ville.vo...@gmail.com>: Apr 14 12:55AM +0300

…I have indeed toyed with the idea of writing a rumination paper on why named arguments have failed, as far as I know and have observed it (which doesn’t cover all the old proposals), but perhaps I was kinda hoping I wouldn’t need to. :)


     As a lurker who’s been following some of the more recent discussions on adding named arguments to C++, I would actually appreciate seeing a more expansive paper that does go back and, as you put it, ‘cover all the old arguments’ in addition to your more concise summary of more contemporary events in this area.  Given your reluctance to do that right now, though, I wouldn’t mind seeing the more extensive analysis as either part of a later revision of your paper or a separate one altogether.  

— Bryce Glover

Bryce Glover

unread,
Apr 14, 2016, 5:27:23 PM4/14/16
to gmis...@gmail.com, std-pr...@isocpp.org
gmis...@gmail.com: Apr 13 06:36PM -0700


This looks like a very well written and presented proposal. I think it could be better evaluated though if there were more discussion about it in the context of existing API designs that have been created with languages that already have the features the proposed. In particular regarding named function parameters.

My own experience of using libraries with extensive numbers of parameters are the Microsoft COM object libraries that lay behind applications like Microsoft Word and Excel etc.
See here:
https://msdn.microsoft.com/en-us/library/kw65a0we.aspx
https://msdn.microsoft.com/en-us/library/office/ff194068.aspx

This proposal would seem to make using such libraries a lot easier?

As important to me though is whether people consider such API designs good? And do we want to encourage more of them by making them easier?

So to my mind we have to evaluate this proposal against real existing APIs while also determining if those real API's need to be as complex as they are so we. This should help us verify if this proposal helps with using such API's and reveal whether an alternative API design or language feature would be better to get the kinds of api's we do want.

I don't feel I can appreciate the value of this proposal without such discussion

If more recent API's could be discussed it might even more relevant. I am thinking some of the web driving api's (think setting html attributes from code etc). like those in the ASP.NET's MVC API, which are C# API's. Maybe that could be addressed? Perhaps a lot of lessons/encouragement could be learned from that?

So I think ultimately we have to compare this proposal against these types of API’s and evaluate the alternatives to appreciate this proposal, don't we?

Thanks for the proposal so far.

     Off the top of my head, another set of examples that might bear examination could exist in Apple’s Cocoa library for Objective-C, which, IIRC, heavily utilizes that language’s support for named arguments.  

— Bryce Glover

Thiago Macieira

unread,
Apr 14, 2016, 7:00:39 PM4/14/16
to std-pr...@isocpp.org
On quinta-feira, 14 de abril de 2016 17:19:21 PDT Tony V E wrote:
> > ‎Huh?
> >
> > Let me try rephrasing this. Let's say we have a C struct:
> >
> > struct foo { int bar };
> >
> > In C89, the "meaning" of this struct is that I can construct it like
> > `{1}`. In C11, the "meaning" is that I can (additionally) construct it‎
> > like `{.bar=1}`. This was evidently not a problem for C11. What "change
> > of meaning" occurs in C++ that is different from this example and
> > therefore problematic?
> In a struct, the member names are already part of the API.

Kinda...

struct ip6_hdr
{
union
{
struct ip6_hdrctl
{
uint32_t ip6_un1_flow; /* 4 bits version, 8 bits TC,
20 bits flow-ID */
uint16_t ip6_un1_plen; /* payload length */
uint8_t ip6_un1_nxt; /* next header */
uint8_t ip6_un1_hlim; /* hop limit */
} ip6_un1;
uint8_t ip6_un2_vfc; /* 4 bits version, top 4 bits tclass */
} ip6_ctlun;
struct in6_addr ip6_src; /* source address */
struct in6_addr ip6_dst; /* destination address */
};

#define ip6_vfc ip6_ctlun.ip6_un2_vfc
#define ip6_flow ip6_ctlun.ip6_un1.ip6_un1_flow
#define ip6_plen ip6_ctlun.ip6_un1.ip6_un1_plen
#define ip6_nxt ip6_ctlun.ip6_un1.ip6_un1_nxt
#define ip6_hlim ip6_ctlun.ip6_un1.ip6_un1_hlim
#define ip6_hops ip6_ctlun.ip6_un1.ip6_un1_hlim

The API for this struct says
hdr.ip6_flow

but there's no member called "ip6_flow" in struct ip6_hdr.

See https://tools.ietf.org/html/rfc3542#section-2.1.

--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center

Patrice Roy

unread,
Apr 14, 2016, 8:14:10 PM4/14/16
to std-pr...@isocpp.org
Thiago, this is an evil (but welcome!) example.

I read the proposal and I like it in general; in that area, it's among the good ones.

Contrary to some, I've been quite often in a situation where changing argument names in functions was important, and in situations where the documentary names in a .h/.hpp file were different from the names used in the function's implementation. I would be inclined to support a proposal that showed how named arguments fit in with this reality of software development.

I don't see named arguments as a «need», but they do look like a «want». We do need a clear path to existing (and future, in this case, as argument names can change anytime) code management. The attribute pathway is a no-go (semantics) but does have a nice upside, which is separating the «interface name» from the «implementation name»; this quality is not something to throw away, IMHO.

Many have mentioned the C11 path, but C has no constructors, and C is a one name, one function language. I'm not convinced it's a good basis for our discussion.

Cheers! (and thanks to the author for bringing an old idea with a clear and interesting presentation to make discussion meaningful)

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

TONGARI J

unread,
Apr 14, 2016, 9:43:41 PM4/14/16
to ISO C++ Standard - Future Proposals
On Friday, April 15, 2016 at 8:14:10 AM UTC+8, Patrice Roy wrote:
Thiago, this is an evil (but welcome!) example.

I read the proposal and I like it in general; in that area, it's among the good ones.

I guess Thiago's example is an argument for designator list?
  
Contrary to some, I've been quite often in a situation where changing argument names in functions was important, and in situations where the documentary names in a .h/.hpp file were different from the names used in the function's implementation. I would be inclined to support a proposal that showed how named arguments fit in with this reality of software development.

That's allowed in my proposal, see my reply to Matthew.
It's just a normal function redeclaration after all.

TONGARI J

unread,
Apr 14, 2016, 10:17:20 PM4/14/16
to ISO C++ Standard - Future Proposals
On Friday, April 15, 2016 at 1:57:27 AM UTC+8, Richard Smith wrote:
On Wed, Apr 13, 2016 at 8:09 PM, TONGARI J <tong...@gmail.com> wrote:
There are some principles I set when designing the feature:
* Don't interfere with the type system

Please allow me to suggest an alternative principle: integrate into the existing language.

Well, it does integrate into the existing language.
I know what I want for this feature, and smooth adoption w/o binary incompatibility is what I desire, hence the principle.
 
Far too often, people proposing features make the mistake of making their feature be its own special island that has rules different from the rest of the language. This is a trap to be avoided. Language continuity is important: C++20 should look and feel like a development of C++17, in order to make it easier for programmers and programs to adapt, but we don't get there by minimizing the amount of change to the language or specification, we get there by making the changes complete so they feel like an extension of what came before.

A feature that does not integrate into the type system, but whose design would naturally lead to type system changes, is flawed. In my opinion, you should first design the feature, and then analyze the type system implications, not the other way around. (And if that leads to no type system changes, that's fine, of course.)

* Don't require ABI changes

I think you should distinguish between "don't change the ABI of existing libraries" and "don't change the ABI when designated parameters are added to an interface". The first seems like an important principle. The second, less so -- as long as the library maintainer still gets to control their ABI as they see fit.

I have a feeling that there would be a resistance to adopt designatable parameters because of ABI incompatibility.
 
Actually, forwarding function is explicitly listed in my non-goals, that's why I deliberately disable the indirect use in the end of section 3.2.3.

The fact that this is a non-goal is going to make your proposal very unappealing to some portion of the committee.
 
If I were going to support forwarding functions, that means I have to make designatable parameters part of the type as you suggested in (a), and that breaks my design principles.
My choice for designated arguments is much like parameter pack, that is, don't make them first class objects.

I should emphasize how reluctant I am in interfering with the type system - it's a no-go for me if adding designatable parameters to existing API would break the ABI.

Adding parameters with default arguments to existing API would break the ABI too

I don't see any difference in the generated LLVM IR with or without default arguments, any example how it affects the ABI?

 as would many other API changes. With a suitable overload resolution rule, I'd expect to be able to change an API that does this:

  struct A {
    void f(int a, int b);
  };

... to do this:

  struct A {
    void f(int a, int b);
    void f(int .a, int .b) { f(a, b); }
  };

... as a non-breaking change (except if someone is taking the address of the function), for people who need perfect ABI compatibility.
 
So call A{}.f(0, 1)is ruled by some conversion sequence?

What about:
void f(int .a, int .b);
void f(int .b, int .a);

Is the overload valid? Is f(.a = 0, .b = 1) ambiguous?

And how would you forward the designators for, say, std::function?

Richard Smith

unread,
Apr 14, 2016, 10:42:17 PM4/14/16
to std-pr...@isocpp.org
On Thu, Apr 14, 2016 at 7:17 PM, TONGARI J <tong...@gmail.com> wrote:
On Friday, April 15, 2016 at 1:57:27 AM UTC+8, Richard Smith wrote:
On Wed, Apr 13, 2016 at 8:09 PM, TONGARI J <tong...@gmail.com> wrote:
There are some principles I set when designing the feature:
* Don't interfere with the type system

Please allow me to suggest an alternative principle: integrate into the existing language.

Well, it does integrate into the existing language.
I know what I want for this feature, and smooth adoption w/o binary incompatibility is what I desire, hence the principle.
 
Far too often, people proposing features make the mistake of making their feature be its own special island that has rules different from the rest of the language. This is a trap to be avoided. Language continuity is important: C++20 should look and feel like a development of C++17, in order to make it easier for programmers and programs to adapt, but we don't get there by minimizing the amount of change to the language or specification, we get there by making the changes complete so they feel like an extension of what came before.

A feature that does not integrate into the type system, but whose design would naturally lead to type system changes, is flawed. In my opinion, you should first design the feature, and then analyze the type system implications, not the other way around. (And if that leads to no type system changes, that's fine, of course.)

* Don't require ABI changes

I think you should distinguish between "don't change the ABI of existing libraries" and "don't change the ABI when designated parameters are added to an interface". The first seems like an important principle. The second, less so -- as long as the library maintainer still gets to control their ABI as they see fit.

I have a feeling that there would be a resistance to adopt designatable parameters because of ABI incompatibility.
 
Actually, forwarding function is explicitly listed in my non-goals, that's why I deliberately disable the indirect use in the end of section 3.2.3.

The fact that this is a non-goal is going to make your proposal very unappealing to some portion of the committee.
 
If I were going to support forwarding functions, that means I have to make designatable parameters part of the type as you suggested in (a), and that breaks my design principles.
My choice for designated arguments is much like parameter pack, that is, don't make them first class objects.

I should emphasize how reluctant I am in interfering with the type system - it's a no-go for me if adding designatable parameters to existing API would break the ABI.

Adding parameters with default arguments to existing API would break the ABI too

I don't see any difference in the generated LLVM IR with or without default arguments, any example how it affects the ABI?

"void f(int a);" and "void f(int a, int b = 0);" generate different code. The point is that we only need to provide tools to avoid ABI breaks, we don't need to make sure that using the feature doesn't break ABI. That's up to the library vendor to deal with.
 
 as would many other API changes. With a suitable overload resolution rule, I'd expect to be able to change an API that does this:

  struct A {
    void f(int a, int b);
  };

... to do this:

  struct A {
    void f(int a, int b);
    void f(int .a, int .b) { f(a, b); }
  };

... as a non-breaking change (except if someone is taking the address of the function), for people who need perfect ABI compatibility.
 
So call A{}.f(0, 1)is ruled by some conversion sequence?

Sure.
 
What about:
void f(int .a, int .b);
void f(int .b, int .a);

Is the overload valid? Is f(.a = 0, .b = 1) ambiguous?

Yes, ambiguous.
 
And how would you forward the designators for, say, std::function?

I asked you the same question upthread :) I also sketched out one possible approach.

Andrew Tomazos

unread,
Apr 14, 2016, 11:36:59 PM4/14/16
to std-pr...@isocpp.org
Hi Tongari,

I've actually been working on a designated initialization proposal.  My current thinking on the matter was to propose an uncontroversial first step of motivating and adopting in C++ a strict subset of the existing C feature (just the parts that clearly make sense for C++), and then showing how this fits into a large family of possible future roadmaps, such as the one you outline in your proposal.

My reaction to reading your proposal is that you are being too ambitious.  It would be multiple years of work to get standardized such a large feature of "uniform designators" across function calls, aggregate initialization and other types of initialization - and that is assuming consensus can be achieved at all, which given the history of named parameter proposals, is a long shot.

That's why I think breaking off and nailing down a smaller first step would be more practical.  The C feature has extensive existing practice that can be appealed to, so I think a subset-only proposal has a much better chance of getting through.  Once that gets in and is nailed down, a proposal to extend an existing feature will be much better received than trying to do everything at once.

Regards,
Andrew.



On Wed, Apr 13, 2016 at 10:33 AM, TONGARI J <tong...@gmail.com> wrote:
Hi,

The draft can be viewed at:
http://jamboree.github.io/designator-draft.html

The prototype based on Clang can be found at:


I'm not sure if it's proper to submit a new proposal before C++17 is settled, anyway, I'd like to make this work public now.
Please let me know if anyone wants to be a champion if it's submitted.


Thanks

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

TONGARI J

unread,
Apr 14, 2016, 11:56:53 PM4/14/16
to ISO C++ Standard - Future Proposals
Hi Andrew,


On Friday, April 15, 2016 at 11:36:59 AM UTC+8, Andrew Tomazos wrote:
Hi Tongari,

I've actually been working on a designated initialization proposal.  My current thinking on the matter was to propose an uncontroversial first step of motivating and adopting in C++ a strict subset of the existing C feature (just the parts that clearly make sense for C++), and then showing how this fits into a large family of possible future roadmaps, such as the one you outline in your proposal.

My reaction to reading your proposal is that you are being too ambitious.  It would be multiple years of work to get standardized such a large feature of "uniform designators" across function calls, aggregate initialization and other types of initialization - and that is assuming consensus can be achieved at all, which given the history of named parameter proposals, is a long shot.

Obviously, Richard is much more ambitious than me ;)
What I proposed is really the minimal, simplest model in practice.
 
That's why I think breaking off and nailing down a smaller first step would be more practical.  The C feature has extensive existing practice that can be appealed to, so I think a subset-only proposal has a much better chance of getting through.  Once that gets in and is nailed down, a proposal to extend an existing feature will be much better received than trying to do everything at once.

I'm not sure if you're an advocator of the alternative described in section 5.7, which I already showed would be a dead end for C++, or you just want to cutoff the proposal to an even smaller subset.
Anyway, Richard seems to have the exact opposite opinion that we should give a more comprehensive proposal, quoting from his mail:

Thiago Macieira

unread,
Apr 15, 2016, 12:13:20 AM4/15/16
to std-pr...@isocpp.org
On quinta-feira, 14 de abril de 2016 18:43:41 PDT TONGARI J wrote:
> I guess Thiago's example is an argument for designator list?

It's an argument against "member names are part of the API". However, this
*does* work in C99:

struct ip6_hdr hdr = {
.ip6_flow = 0,
.ip6_plen = 4,
.ip6_nxt = 46,
.ip6_hops = 64
};

Andrew Tomazos

unread,
Apr 15, 2016, 12:46:02 AM4/15/16
to std-pr...@isocpp.org
On Fri, Apr 15, 2016 at 5:56 AM, TONGARI J <tong...@gmail.com> wrote:
Hi Andrew,

On Friday, April 15, 2016 at 11:36:59 AM UTC+8, Andrew Tomazos wrote:
Hi Tongari,

I've actually been working on a designated initialization proposal.  My current thinking on the matter was to propose an uncontroversial first step of motivating and adopting in C++ a strict subset of the existing C feature (just the parts that clearly make sense for C++), and then showing how this fits into a large family of possible future roadmaps, such as the one you outline in your proposal.

My reaction to reading your proposal is that you are being too ambitious.  It would be multiple years of work to get standardized such a large feature of "uniform designators" across function calls, aggregate initialization and other types of initialization - and that is assuming consensus can be achieved at all, which given the history of named parameter proposals, is a long shot.

Obviously, Richard is much more ambitious than me ;)
What I proposed is really the minimal, simplest model in practice.
 
That's why I think breaking off and nailing down a smaller first step would be more practical.  The C feature has extensive existing practice that can be appealed to, so I think a subset-only proposal has a much better chance of getting through.  Once that gets in and is nailed down, a proposal to extend an existing feature will be much better received than trying to do everything at once.

I'm not sure if you're an advocator of the alternative described in section 5.7, which I already showed would be a dead end for C++, or you just want to cutoff the proposal to an even smaller subset.

An even smaller subset.  Consider it as the intersection of your proposal and the existing C feature.  This would necessarily be forward compatible with your proposal, as the intersection is a subset of your proposal.  The people that support your proposal must also support such a step.  Additionally, there may be people that don't support your proposal in entirety, but support such a step.  Therefore that step has a better chance of achieving consensus than your proposal.

Anyway, Richard seems to have the exact opposite opinion that we should give a more comprehensive proposal, quoting from his mail:
 
we don't get there by minimizing the amount of change to the language or specification, we get there by making the changes complete so they feel like an extension of what came before.

I think Richard and I are talking about different things.  He is talking about the design.  I am talking about the execution.

I'm just suggesting we should break up the proposal into a series of steps, not suggesting we change the destination.

TONGARI J

unread,
Apr 15, 2016, 2:01:21 AM4/15/16
to ISO C++ Standard - Future Proposals
OK, quoting from your post:

We could imagine something like this:
template<typename F, designator ...D, typename ...T> decltype(auto) f(F f, T &&... .D) {
  return f(0, .D = std::forward<T>(D) ...);
}
That is: a new kind of template parameter representing a designator, and designator names are looked up to see if they name such a parameter before assuming they're an opaque identifier. (Plus a notional "empty designator" that is deduced in cases where no designator is specified.) I don't think I like this approach, and as noted, I hope there's a better answer. 

I imagine a more proper syntax would be:

template<designator ...D, typename F, typename ...T> decltype(auto) call(F f, T && D ...t) {
  return f(0, D = std::forward<T>(t) ...);
}

* "designator" would be a contextual keyword only allowed in template param-list.
* The designators can be used in both param-list in the function declaration (isolated from the actual param-name) and the expr-list in function-call-expr.

However, I'm not sure if you could  deduce the designators from args or not?
call(f, .b = 1, .a = 2); // ok?

If you allow the call above, what if call is defined as below:
template<typename F, typename ...T> decltype(auto) call(F f, T && ...t) {
 
return f(0, std::forward<T>(t) ...);
}
Should it give the same result?

I suspect you cannot do that. A simpler way is to force designators to be explicitly specified:
call<.a, .b>(f, .b = 1, .a = 2);

I'm not sure I like it though, seems problematic if we're going to support designated template arguments in the future, e.g. V<.T = int>.

You really think it worth the complexity?

TONGARI J

unread,
Apr 15, 2016, 2:41:49 AM4/15/16
to std-pr...@isocpp.org
2016-04-15 12:46 GMT+08:00 Andrew Tomazos <andrew...@gmail.com>:
On Fri, Apr 15, 2016 at 5:56 AM, TONGARI J <tong...@gmail.com> wrote:
Hi Andrew,

On Friday, April 15, 2016 at 11:36:59 AM UTC+8, Andrew Tomazos wrote:
Hi Tongari,

I've actually been working on a designated initialization proposal.  My current thinking on the matter was to propose an uncontroversial first step of motivating and adopting in C++ a strict subset of the existing C feature (just the parts that clearly make sense for C++), and then showing how this fits into a large family of possible future roadmaps, such as the one you outline in your proposal.

My reaction to reading your proposal is that you are being too ambitious.  It would be multiple years of work to get standardized such a large feature of "uniform designators" across function calls, aggregate initialization and other types of initialization - and that is assuming consensus can be achieved at all, which given the history of named parameter proposals, is a long shot.

Obviously, Richard is much more ambitious than me ;)
What I proposed is really the minimal, simplest model in practice.
 
That's why I think breaking off and nailing down a smaller first step would be more practical.  The C feature has extensive existing practice that can be appealed to, so I think a subset-only proposal has a much better chance of getting through.  Once that gets in and is nailed down, a proposal to extend an existing feature will be much better received than trying to do everything at once.

I'm not sure if you're an advocator of the alternative described in section 5.7, which I already showed would be a dead end for C++, or you just want to cutoff the proposal to an even smaller subset.

An even smaller subset.  Consider it as the intersection of your proposal and the existing C feature.  This would necessarily be forward compatible with your proposal, as the intersection is a subset of your proposal.  The people that support your proposal must also support such a step.  Additionally, there may be people that don't support your proposal in entirety, but support such a step.  Therefore that step has a better chance of achieving consensus than your proposal.

Well, it'd be fine if you could push designated initializers into C++17, and I can aim for C++20. But if designated initializers could only aim for C++20, another 3 years seem to be a long time for designated arguments...during the long period, people could have established the boring tricks to emulate designated arguments and it's not hard to imagine that they're ask for the syntax-sugar alternative I illustrated instead of a formal approach.

Maybe I'm just too pessimistic...

Matthew Woehlke

unread,
Apr 15, 2016, 10:37:43 AM4/15/16
to std-pr...@isocpp.org
On 2016-04-14 17:19, Tony V E wrote:
>> Let me try rephrasing this. Let's say we have a C struct:
>>
>> struct foo { int bar };
>>
>> In C89, the "meaning" of this struct is that I can construct it
>> like `{1}`. In C11, the "meaning" is that I can (additionally)
>> construct it‎ like `{.bar=1}`. This was evidently not a problem for
>> C11. What "change of meaning" occurs in C++ that is different from
>> this example and therefore problematic?
>
> In a struct, the member names are already part of the API.

That's not relevant. Remember, this was talking about an opt-in approach
using attributes. This would mean that in C++11, the parameter names are
*also* already part of the API; they're just not an *accessible* part
(the attribute is there, but ignored because it isn't recognized by
C++11). This is almost exactly the same as in C89, the member names are
"already part" of the initializer list; you just can't use them.

Example:

int foo(int [[named("x")]]);

Note that the parameter name "x" is *already part of the API* (although
not a recognized part by C++11). In C++11, I can call `foo(5)`, just
like in C89 I can construct my struct like `{5}`. In C++??¹ I can *also*
call `foo(.x = 5)`... again, just like in C11 I can also construct my
struct like `{.bar = 5}`.

I have yet to meet a non-hand-waving argument argument why the C89->C11
change is okay but the C++11->C++?? change is not.

(¹ C++?? -> a hypothetical version of C++ that includes this feature.)

--
Matthew

Thiago Macieira

unread,
Apr 15, 2016, 12:50:54 PM4/15/16
to std-pr...@isocpp.org
On sexta-feira, 15 de abril de 2016 10:37:14 PDT Matthew Woehlke wrote:
> That's not relevant. Remember, this was talking about an opt-in approach
> using attributes.

I recommend you stop talking about attributes, as this lowers the chance of
the feature being accepted. Not only would the feature itself be fighting to
prove its worth (it's been rejected more than once), using attributes means
fighting against established rules over attributes.

Remember that this feature is asking for differences in overload resolution
and has consequences for the ABI. That's way outside what attributes are meant
to do.

Andrew Tomazos

unread,
Apr 15, 2016, 1:04:15 PM4/15/16
to std-pr...@isocpp.org
I think it's already too late to put new work into C++17.

As proposals are approved they are applied to the working draft.  Multiple proposals that build upon one another can be applied to the working draft between a single release cycle of the IS.

Having said that, I think C++20 is too ambitious a target for your entire proposal.  I consider it about twice as large as defaulted comparisons.

Default comparisons were at the stage of your proposal in late 2013 by Oleg Smolsky (informal paper, prototype impl):


and it's not clear yet whether they will just squeak into C++17.

I don't think there is much risk of people getting a "syntax-sugar alternative" instead of a "formal approach".   All alternative approaches will be discussed and considered ad nauseam for many years before selecting and standardizing one or none.

As long as whatever the first step proposal is, is shown to be forward compatible with all the feasible approaches we might want to select later, I think it can achieve consensus, and C++20 is a realistic target.

Matthew Woehlke

unread,
Apr 15, 2016, 1:29:43 PM4/15/16
to std-pr...@isocpp.org
On 2016-04-15 12:50, Thiago Macieira wrote:
> I recommend you stop talking about attributes, as this lowers the chance of
> the feature being accepted. Not only would the feature itself be fighting to
> prove its worth (it's been rejected more than once), using attributes means
> fighting against established rules over attributes.
>
> Remember that this feature is asking for differences in overload resolution
> and has consequences for the ABI. That's way outside what attributes are meant
> to do.

It *doesn't* have consequences for the ABI; Tongari has been rather
explicit on this point. I suspect it isn't "asking for differences in
overload resolution" either; not, at least, in the sense I suspect you
are thinking.

Note:

int foo(int .a);
int foo(int); // same as previous
int foo(int .b); // error

I'm starting to think that the problem is that folks are interpreting
conversations based on the features and drawbacks of their own pet
implementation, rather than the variation actually being discussed in a
particular conversation thread.

--
Matthew

TONGARI J

unread,
Apr 15, 2016, 1:34:34 PM4/15/16
to std-pr...@isocpp.org
2016-04-16 1:04 GMT+08:00 Andrew Tomazos <andrew...@gmail.com>:
I think it's already too late to put new work into C++17.

As proposals are approved they are applied to the working draft.  Multiple proposals that build upon one another can be applied to the working draft between a single release cycle of the IS.

Having said that, I think C++20 is too ambitious a target for your entire proposal.  I consider it about twice as large as defaulted comparisons.

Default comparisons were at the stage of your proposal in late 2013 by Oleg Smolsky (informal paper, prototype impl):


and it's not clear yet whether they will just squeak into C++17.

I don't think there is much risk of people getting a "syntax-sugar alternative" instead of a "formal approach".   All alternative approaches will be discussed and considered ad nauseam for many years before selecting and standardizing one or none.

As long as whatever the first step proposal is, is shown to be forward compatible with all the feasible approaches we might want to select later, I think it can achieve consensus, and C++20 is a realistic target.

Fine, then please c.c. me if you have a draft to review or anything happens to your proposal so I can continue the work.
You could also mention some future direction as the idea I illustrated in my proposal, if that helps.

Good luck!

TONGARI J

unread,
Apr 15, 2016, 1:38:01 PM4/15/16
to std-pr...@isocpp.org
2016-04-16 1:29 GMT+08:00 Matthew Woehlke <mwoehlk...@gmail.com>:
On 2016-04-15 12:50, Thiago Macieira wrote:
> I recommend you stop talking about attributes, as this lowers the chance of
> the feature being accepted. Not only would the feature itself be fighting to
> prove its worth (it's been rejected more than once), using attributes means
> fighting against established rules over attributes.
>
> Remember that this feature is asking for differences in overload resolution
> and has consequences for the ABI. That's way outside what attributes are meant
> to do.

It *doesn't* have consequences for the ABI; Tongari has been rather
explicit on this point. I suspect it isn't "asking for differences in
overload resolution" either; not, at least, in the sense I suspect you
are thinking.

To be clear, it doesn't affect function overloading but does add a new step in overload resolution process (i.e. argument-mapping).

Thiago Macieira

unread,
Apr 15, 2016, 5:26:39 PM4/15/16
to std-pr...@isocpp.org
On sexta-feira, 15 de abril de 2016 13:29:32 PDT Matthew Woehlke wrote:
> It *doesn't* have consequences for the ABI; Tongari has been rather
> explicit on this point. I suspect it isn't "asking for differences in
> overload resolution" either; not, at least, in the sense I suspect you
> are thinking.
>
> Note:
>
> int foo(int .a);
> int foo(int); // same as previous
> int foo(int .b); // error
>
> I'm starting to think that the problem is that folks are interpreting
> conversations based on the features and drawbacks of their own pet
> implementation, rather than the variation actually being discussed in a
> particular conversation thread.

Fair enough. I haven't paid enough attention to this thread, but I've seen
several times code like this going around:

complex<double> make_complex(double .real, double .imag);
complex<double> make_complex(double .magnitude, double .argument);

Still, attributes are the wrong solution for this.
Reply all
Reply to author
Forward
0 new messages