Named arguments proposal

448 views
Skip to first unread message

Berkus Infinitus

unread,
Oct 4, 2013, 3:18:49 AM10/4/13
to std-pr...@isocpp.org
I've been thinking about named arguments support in C++ with a simple, non-intrusive syntax. I've posted a formatted version of the idea here https://github.com/berkus/cpp_named_args

I would like to hear what do you think is wrong with this approach and why would it not work, before I jump to patch up LLVM to support this feature?

Ville Voutilainen

unread,
Oct 4, 2013, 7:32:24 AM10/4/13
to std-pr...@isocpp.org
On 4 October 2013 10:18, Berkus Infinitus <ber...@gmail.com> wrote:
I've been thinking about named arguments support in C++ with a simple, non-intrusive syntax. I've posted a formatted version of the idea here https://github.com/berkus/cpp_named_args

I would like to hear what do you think is wrong with this approach and why would it not work, before I jump to patch up LLVM to support this feature?



How is this different from previous proposals for named arguments?

Troy Heron

unread,
Oct 4, 2013, 6:42:10 PM10/4/13
to std-pr...@isocpp.org
Can you point as to the previous proposal for named arguments?

Ville Voutilainen

unread,
Oct 4, 2013, 7:01:45 PM10/4/13
to std-pr...@isocpp.org
On 5 October 2013 01:42, Troy Heron <troy....@hixxy.org> wrote:
Can you point as to the previous proposal for named arguments?


Oh, this is surprising. I can't find a proposal paper for it. D&E gives some explanation
about why named parameters weren't adopted and proposes using something
along the lines of
http://www.parashift.com/c++-faq/named-parameter-idiom.html
instead. So, any new proposal would have to consider the reasons given in D&E not
to adopt named parameters, I think.

Troy Heron

unread,
Oct 4, 2013, 7:21:23 PM10/4/13
to std-pr...@isocpp.org
Shame that's only useful for objects, not free functions, and seems like another hack to use as an excuse for not making language levels changes to the standard.


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

To post to this group, send email to std-pr...@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.

Ville Voutilainen

unread,
Oct 4, 2013, 7:28:58 PM10/4/13
to std-pr...@isocpp.org
On 5 October 2013 02:21, Troy Heron <troy....@hixxy.org> wrote:
Shame that's only useful for objects, not free functions, and seems like another hack to use as an excuse for not

The technique can be used with free functions as well.
 
making language levels changes to the standard.



The argument against named parameters has for a long time been that they are not important
enough to justify having the core language change when an arguably palatable work-around
can be used.

I would certainly welcome a library addition to the standard that would make the work-around
easier to write, if a language change remains unattainable. Having to write the work-around
from scratch is unacceptable for quite many cases where it would serve a purpose, because
it leads to a proliferation of custom solutions. And I don't think
http://www.boost.org/doc/libs/1_54_0/libs/parameter/doc/html/index.html
helps much, as technically good a solution as it may be, since the amount of boilerplate
written is still over the top.

Richard Smith

unread,
Oct 4, 2013, 7:39:32 PM10/4/13
to std-pr...@isocpp.org
FWIW, in Clang (which support C99's designated initializers in C++), you can do this sort of thing already:

struct rect {
  struct ltwh { int left = 0, top = 0, width = 0, height = 0; };
  struct ltrb { int left, top, right, bottom; };
  rect(ltwh);
  rect(ltrb);
};

rect r({ .width = 200, .height = 100 });
rect r2({ .left = 0, .right = 200, .top = 0, .bottom = 100 });

Fernando Pelliccioni

unread,
Oct 4, 2013, 7:55:33 PM10/4/13
to std-pr...@isocpp.org
I think Dave Abrahams has proposed

Troy Heron

unread,
Oct 4, 2013, 8:00:38 PM10/4/13
to std-pr...@isocpp.org
I've seen a mention on the boost mailing list a long while ago about a desire for this to be proposed by Dave Abrahams, I can't find a proposal anywhere though.

Ville: There is a multitude of problems with that named parameter idiom (which I've never used, so I'm mentioning these problems just based on a quick glance at the url):

Drawbacks of named parameter idiom:

* This idiom implies that all parameters not passed directly to the constructor all have default
values as equivalent to being 'T variable = value' inside the constructor parameter list.
This is most often not the intended behaviour when defining a constructor.

* As all the member variables having been initialised by a default value due to the problem
in 1, they will be twice initialised when having their value set via a named member function.
This is often unacceptable.

* It's not possible to have a named parameter that is required to have a value passed to it.
As per 1, they're all defaulted whether that is your intention or not. You also can't guarantee
that the user is going to call the named method and this isn't enforceable at compile time.

* I still don't see how this is usable for free functions.


Troy

Ville Voutilainen

unread,
Oct 5, 2013, 2:21:47 AM10/5/13
to std-pr...@isocpp.org
On 5 October 2013 03:00, Troy Heron <troy....@hixxy.org> wrote:
I've seen a mention on the boost mailing list a long while ago about a desire for this to be proposed by Dave Abrahams, I can't find a proposal anywhere though.

Ville: There is a multitude of problems with that named parameter idiom (which I've never used, so I'm mentioning these problems just based on a quick glance at the url):

Trust me, I'm aware of its problems.
 

Drawbacks of named parameter idiom:

* This idiom implies that all parameters not passed directly to the constructor all have default
values as equivalent to being 'T variable = value' inside the constructor parameter list.
This is most often not the intended behaviour when defining a constructor.

What you do with parameters not passed to a constructor is your decision..
 

* As all the member variables having been initialised by a default value due to the problem
in 1, they will be twice initialised when having their value set via a named member function.
This is often unacceptable.

You can use a parameter struct that contains optionals if that's an issue.
 

* It's not possible to have a named parameter that is required to have a value passed to it.
As per 1, they're all defaulted whether that is your intention or not. You also can't guarantee
that the user is going to call the named method and this isn't enforceable at compile time.

Sure, that isn't enforceable at compile time, but you can still enforce it, so the "it's not
possible" part does not hold.
 

* I still don't see how this is usable for free functions.



I don't quite understand why you think so. It's a matter of having
void f(ChainableParameterStruct);
or
void MyClass::memberFunc(ChainableParameterStruct);
or
MyClass::MyClass(ChainableParameterStruct);
The same technique applies equally to members and free functions.
If a  constructor chooses to use the parameter struct for member initialization,
that's its business, but nothing prevents a free function using such a parameter
struct for other purposes.

Berkus Infinitus

unread,
Oct 7, 2013, 1:32:05 PM10/7/13
to std-pr...@isocpp.org
Agree, pointers to previous proposals would be nice since I'm not very familiar with isocpp processes.

Also, it has been pointed out that the mandatory naming for named arguments becomes a little bit redundant in cases like

bool isReliable = /* some calculations */;
writeData(data, isReliable: isReliable); // oops, tautology

To fix this I was thinking about removing the requirement for naming the argument if it has an lvalue passed to it. This becomes more cumbersome as it imposes restrictions on e.g. order of arguments (I wouldn't want make a compiler guess for example what a localised variable name means).

The "named-parameter-idiom" from parashift.com is actually more like a fluent interface and has been mentioned in my draft. While it's sometimes viable, I think it's an abuse of the idiom.


About the question why my syntax is better than the previous ones (without actually reading about any previous proposals): I think it uses a "label" semantic inside function call site, which has some sense, at least to me, for the potentially arbitrarily placed arguments.

Bengt Gustafsson

unread,
Oct 7, 2013, 5:28:18 PM10/7/13
to std-pr...@isocpp.org
@Berkus: What's the reason for requiring a colon in the parameter definition? I think this feature would be much more useful if it was allowed for any function call.

Presently labels are not allowed inside an expression so there should be no syntactic problem there.

One problem would be that the formal names are allowed to be different in different declarations. A simple rule to counteract this problem would be to disallow the "label" from naming a parameter which is in different positions in different declarations (for any overload of the function).

The big problem is overloaded functions, as overloads just match the type combination of the arguments at the call site. With named parameters we don't really know the type combination until we have selected an overload. But maybe this is not an unpassable obstacle. First we can rule out all signatures which don't have all the "labels" named in the call site. When this is done we can try to match each of the remaining signatures with the actuals, performing permutations of the actuals based on the name order of that particular candidate function. Some signatures may be ruled out as uncallable by this step, just as for any signature selection. FInally the "best" candidate should be possible to select using the same rules as for current call sites, with the same rules for ambiguous calls.

Remains to be defined is what happens if we have unnamed arguments after a named one in the call site. One simple idea is to forbid this (A). I think this may be the best choice as it is easy to understand. Another possibility (B) is to have a "cursor" in the formal parameter list which is incremented after each argument and moved by a label. This is also quite easy to understand but is a bit error prone as you may get confused if different overloads have different subsequent parameters after a like-named one. A third alternative (C) would be to have a "cursor" which only increments after unnamed actuals, so that named arguments can be written anywhere in the actuals list without affecting the enumeration of unnamed arguments. This I think is really scary.

Both the two latter options create the possibility that the same formal parameter gets two different values at the call site. The issue is if this is only to rule out the particular overload where it happens or if it is misformed totally. A similar case in the B alternative is that you may state too many unnamed arguments after a labeled one. This is very similar to stating too many unnamed arguments today, in which case the signatures taking fewer parameters are ruled out. This suggests that these cases should be treated similarly as the call site may still be valid for some other overload.

As an extra feature it would be nice if you could just state the name of a bool parameter to set it to true. I don't think this would be possible as the same name may be defined in the scope of the call site, but with a trailing colon character it could work. This would not look as nice but still better than having to write "myBoolParameter: true" every time.

I have actually implemented named parameters in an entirely different way, but that used a macro for the function name so it was really not a very good approach. The macro lifted its (variadic) parameters into a lambda where they were preceeded by a using of a special namespace containing the parameter names as functions. The syntax at the call site became something like this Window(Title("My Window"), CloseButton, Colour(0x404080)). Window is the macro name and the other names are hidden in a namespace. The macro expansion contains an object of a special type with an operator,() which picks up the results of all the argument functions. The boolean "turn on" feature CloseButton is implemented as a static object in the namespace. However ugly the implementation the appearance at the call site was quite nice...

To implement this without using a macro something like "reverse ADL lookup" would be needed. By this I mean that in the argument list of a function call site you can use names from the namespace of the called function (or class of the method, even). I haven't given this much thought really, but it seems to be unworkable as the names like Colour in my example could be defined in the call site context and so changes the meaning of lots of code. Possibly reverse ADL could kick in only if everything else fails, but that's not how normal ADL works, is it? Ideally it should be allowed to write Colour(Colour) in keeping with what is allowed in ctor initializer lists, but that does not seem reasonable to expect, as it needs more than a reverse ADL. One possibility would be to allow a "using namespace xyz;" inside the formals list to explicitly call for a reverse ADL to be allowed. Still a lot of boilerplate as each named parameter must be a ctor of some type and the called function be a variadic template which calls methods in all these objects with "this" as a parameter. Lots of overhead there...

This was an aside, more as a starting point for someone else to think of alternative approaches. Right now I would prefer the labeled arguments idea. Especially if the requirement to change function declarations can be lifted it seems like a very useful feature that could be beneficial for many cases. There would be no overhead whatsoever to make functions callable in this way which compared to Boosts macros and boilerplate or the runtime overhead of the .function().function() approach much easier to understand when looking at a call site. Note also that you can't use the function().function() approach for constructors of named variables:

MyWindow window("Title").WithBorder().Sunken(); // Error!

Bikeshed: I think that by calling this idea "labeled arguments" programmers will immediately understand what is meant. Well, at least the few that remember goto and labels (none, I wish!).

Richard Smith

unread,
Oct 7, 2013, 6:31:39 PM10/7/13
to std-pr...@isocpp.org
On Mon, Oct 7, 2013 at 2:28 PM, Bengt Gustafsson <bengt.gu...@beamways.com> wrote:
@Berkus: What's the reason for requiring a colon in the parameter definition? I think this feature would be much more useful if it was allowed for any function call.

Please no. If you allow existing parameter names to gain semantics like this, then renaming parameters becomes an ABI breaking change. Functions should be required to opt into their parameter names having meaning.
 

Jean-Marc Bourguet

unread,
Oct 8, 2013, 2:38:57 AM10/8/13
to std-pr...@isocpp.org
Le mardi 8 octobre 2013 00:31:39 UTC+2, Richard Smith a écrit :
On Mon, Oct 7, 2013 at 2:28 PM, Bengt Gustafsson <bengt.gu...@beamways.com> wrote:
@Berkus: What's the reason for requiring a colon in the parameter definition? I think this feature would be much more useful if it was allowed for any function call.

Please no. If you allow existing parameter names to gain semantics like this, then renaming parameters becomes an ABI breaking change. Functions should be required to opt into their parameter names having meaning.

API not ABI I assume?  If not, could you point out the implications I've missed? (I agree that it doesn't make a difference, it should be an opt-in).

Thanks,

-- 
Jean-Marc

Brendon Costa

unread,
Oct 8, 2013, 5:38:33 AM10/8/13
to std-pr...@isocpp.org

On 8 October 2013 04:32, Berkus Infinitus <ber...@gmail.com> wrote:
about removing the requirement for naming the a

I like the idea of named arguments and find them very useful in other languages. One thing I use a bit in python is perfect forwarding with named args. It would be worth spending some time to think if this proposal could work with perfect forwarding.






Diego Sánchez

unread,
Oct 8, 2013, 9:14:25 AM10/8/13
to std-pr...@isocpp.org
As far as I can tell, there are two different use cases for named arguments:

1. Documentation of call parameters. This could easily be faked by adding comments, as in: write_datagram(buf, size, /* is_reliable */ true)  ugly as ****, but works.

2. Skipping over optional arguments.


This is what I propose:

a) For the documentation problem: If an argument name in a function declaration is immediately followed by a colon, the argument name must be present also in all calls. No reordering of arguments is allowed here; named arguments must stay in the same place as they are in the function signature. This would reduce the problem as a parsing one: The compiler should issue an error (maybe a warning) if no name is given for a named arg, but can forget about the parameter labeling from then on.

b) Skipping over optional arguments. I would follow here the same rules as for optional args:

      - In a function declaration, if an optional argument name is followed by a colon, that turns it into a named argument. As with optional args, all arguments following it must also be named arguments.
      - In a function call, if a named optional argument is labelled, all arguments from then on must also be labelled. As in case a), arguments must also be in the same order as in the function declaration.


In both cases, the rule that parameters must keep the same order as in the function declaration preserves the signature, so no new 
ambiguities are introduced. In fact, the function definition must not know about named args, just as it doesn't know about default values.



A final note about the syntax. I like the use of the colon in the function declaration, that turns the argument name into a label; but on function calls I would prefer the equal sign; it makes the purpose clearer:

write_datagram(buf, size, is_reliable: true);
vs
write_datagram(buf, size, is_reliable=true);


2013/10/8 Brendon Costa <brendon...@gmail.com>

On 8 October 2013 04:32, Berkus Infinitus <ber...@gmail.com> wrote:
about removing the requirement for naming the a

I like the idea of named arguments and find them very useful in other languages. One thing I use a bit in python is perfect forwarding with named args. It would be worth spending some time to think if this proposal could work with perfect forwarding.






Ville Voutilainen

unread,
Oct 8, 2013, 9:29:37 AM10/8/13
to std-pr...@isocpp.org
On 8 October 2013 16:14, Diego Sánchez <dsd...@gmail.com> wrote:



A final note about the syntax. I like the use of the colon in the function declaration, that turns the argument name into a label; but on function calls I would prefer the equal sign; it makes the purpose clearer:

write_datagram(buf, size, is_reliable: true);
vs
write_datagram(buf, size, is_reliable=true);



'fraid we can't have that. This is already valid:

void f(bool x) {}
int main()
{
    bool x=false;
    f(x=true);
}

Diego Sánchez

unread,
Oct 8, 2013, 9:34:14 AM10/8/13
to std-pr...@isocpp.org
 Oops!  That should have been obvious.

Ville Voutilainen

unread,
Oct 8, 2013, 9:34:38 AM10/8/13
to std-pr...@isocpp.org
On 8 October 2013 16:14, Diego Sánchez <dsd...@gmail.com> wrote:
As far as I can tell, there are two different use cases for named arguments:

1. Documentation of call parameters. This could easily be faked by adding comments, as in: write_datagram(buf, size, /* is_reliable */ true)  ugly as ****, but works.

2. Skipping over optional arguments.


This is what I propose:

a) For the documentation problem: If an argument name in a function declaration is immediately followed by a colon, the argument name must be present also in all calls. No reordering of arguments is allowed here; named arguments must stay in the same place as they are in the function signature. This would reduce the problem as a parsing one: The compiler should issue an error (maybe a warning) if no name is given for a named arg, but can forget about the parameter labeling from then on.

b) Skipping over optional arguments. I would follow here the same rules as for optional args:

      - In a function declaration, if an optional argument name is followed by a colon, that turns it into a named argument. As with optional args, all arguments following it must also be named arguments.
      - In a function call, if a named optional argument is labelled, all arguments from then on must also be labelled. As in case a), arguments must also be in the same order as in the function declaration.


In both cases, the rule that parameters must keep the same order as in the function declaration preserves the signature, so no new 
ambiguities are introduced. In fact, the function definition must not know about named args, just as it doesn't know about default values.



Funny. I'd expect

void f(int x : 0, int y : 0, int z : 0);

to be callable as
f(y : 42); // 'unordered' call, use default args for x and z

so I don't quite grasp what the same-order requirement buys. Furthermore, I'd expect
it to be callable as
f(42); // 'ordered' call, use default args for y and z
and
f(42, 666); // 'ordered' call, use default args for z

What is the interesting question is whether to allow mixing, like

f(666, z : 5); // ' x=666, y=0, z=5

Diego Sánchez

unread,
Oct 8, 2013, 10:06:55 AM10/8/13
to std-pr...@isocpp.org
The idea is that you can call f( y : 42 )  or f ( y :42, z : 666 ), but not f( z: 666, y: 42 ). This would preserve the function signature after filling the gaps.

Say you have void g ( int a : 0, float b: 0, bool c : true )

g( b:1, c:false )  is void ()( int, float, bool )  
g( c:true, b:2 ) is void ()( int, bool, float )


Regarding mixing, I'm not sure what could be better. My idea was that for optional arguments, after you start labeling, you must keep labeling; i.e.: f( 5, y:42, z:666 ), but not  f( 5, y:42, 666 ) , but on second thought there is no real need for that; the next argument after y must be z .


Ville Voutilainen

unread,
Oct 8, 2013, 10:10:43 AM10/8/13
to std-pr...@isocpp.org
On 8 October 2013 17:06, Diego Sánchez <dsd...@gmail.com> wrote:

Funny. I'd expect

void f(int x : 0, int y : 0, int z : 0);

to be callable as
f(y : 42); // 'unordered' call, use default args for x and z

so I don't quite grasp what the same-order requirement buys. Furthermore, I'd expect
it to be callable as
f(42); // 'ordered' call, use default args for y and z
and
f(42, 666); // 'ordered' call, use default args for z

What is the interesting question is whether to allow mixing, like

f(666, z : 5); // ' x=666, y=0, z=5

The idea is that you can call f( y : 42 )  or f ( y :42, z : 666 ), but not f( z: 666, y: 42 ). This would preserve the function signature after filling the gaps.

I understand that. I don't think we should have such a restriction about the order of named
arguments.
 

Say you have void g ( int a : 0, float b: 0, bool c : true )

g( b:1, c:false )  is void ()( int, float, bool )  
g( c:true, b:2 ) is void ()( int, bool, float )

Please, no.
 


Regarding mixing, I'm not sure what could be better. My idea was that for optional arguments, after you start labeling, you must keep labeling; i.e.: f( 5, y:42, z:666 ), but not  f( 5, y:42, 666 ) , but on second thought there is no real need for that; the next argument after y must be z .



I would be fine with the restriction that once labeling starts, it can't stop. Especially if we
don't restrict the ordering of labeled arguments themselves in calls. This is how it's
done in Lisp, for instance, and it works just fine.

Zhihao Yuan

unread,
Oct 8, 2013, 10:22:53 AM10/8/13
to std-pr...@isocpp.org
On Tue, Oct 8, 2013 at 9:34 AM, Ville Voutilainen
<ville.vo...@gmail.com> wrote:
> Funny. I'd expect
>
> void f(int x : 0, int y : 0, int z : 0);
>
> to be callable as
> f(y : 42); // 'unordered' call, use default args for x and z
>
> so I don't quite grasp what the same-order requirement buys. Furthermore,
> I'd expect
> it to be callable as
> f(42); // 'ordered' call, use default args for y and z
> and
> f(42, 666); // 'ordered' call, use default args for z

I hope order call is not supported on named arguments at all. Python 2
has it, but caused lots of problems, especially when a signature is
changed, the old problem may still run but runtime behavior is silently
changed, which is really horrible, and finally people added "keyword-
only argument" in Python 3.

--
Zhihao Yuan, ID lichray
The best way to predict the future is to invent it.
___________________________________________________
4BSD -- http://4bsd.biz/

Ville Voutilainen

unread,
Oct 8, 2013, 10:29:28 AM10/8/13
to std-pr...@isocpp.org
On 8 October 2013 17:22, Zhihao Yuan <z...@miator.net> wrote:
On Tue, Oct 8, 2013 at 9:34 AM, Ville Voutilainen
<ville.vo...@gmail.com> wrote:
> Funny. I'd expect
>
> void f(int x : 0, int y : 0, int z : 0);
>
> to be callable as
> f(y : 42); // 'unordered' call, use default args for x and z
>
> so I don't quite grasp what the same-order requirement buys. Furthermore,
> I'd expect
> it to be callable as
> f(42); // 'ordered' call, use default args for y and z
> and
> f(42, 666); // 'ordered' call, use default args for z

I hope order call is not supported on named arguments at all.  Python 2
has it, but caused lots of problems, especially when a signature is
changed, the old problem may still run but runtime behavior is silently
changed, which is really horrible, and finally people added "keyword-
only argument" in Python 3.



It doesn't cause any problem that wasn't always present with functions
ever since c++98. If a user is concerned about that, he should use
named arguments in his calls, but I see no reason to prevent
unnamed arguments for those who wish to use them.

Zhihao Yuan

unread,
Oct 8, 2013, 10:32:54 AM10/8/13
to std-pr...@isocpp.org
?? I guess you miss understood.

What I mean is, if an argument is declared as named argument:

void f(int x, int y:); // y in this example

Call the function with the named argument named works:

f(3, y: 4);

But with the named argument unnamed should not:

f(3, 4); // no!!!

Ville Voutilainen

unread,
Oct 8, 2013, 10:35:24 AM10/8/13
to std-pr...@isocpp.org
On 8 October 2013 17:32, Zhihao Yuan <z...@miator.net> wrote:
On Tue, Oct 8, 2013 at 10:29 AM, Ville Voutilainen
<ville.vo...@gmail.com> wrote:
>> I hope order call is not supported on named arguments at all.  Python 2
>> has it, but caused lots of problems, especially when a signature is
>> changed, the old problem may still run but runtime behavior is silently
>> changed, which is really horrible, and finally people added "keyword-
>> only argument" in Python 3.
>>
>>
>
> It doesn't cause any problem that wasn't always present with functions
> ever since c++98. If a user is concerned about that, he should use
> named arguments in his calls, but I see no reason to prevent
> unnamed arguments for those who wish to use them.

?? I guess you miss understood.

What I mean is, if an argument is declared as named argument:

  void f(int x, int y:);  // y in this example

Call the function with the named argument named works:

  f(3, y: 4);

But with the named argument unnamed should not:

  f(3, 4);  // no!!!



I understood it just fine, and YES, I want that call to work.

morw...@gmail.com

unread,
Oct 8, 2013, 10:48:42 AM10/8/13
to std-pr...@isocpp.org
Le mardi 8 octobre 2013 16:35:24 UTC+2, Ville Voutilainen a écrit :
I understood it just fine, and YES, I want that call to work.

And so do I.
Morevoer, it allows to name some previously unnamed arguments in any librabry without breaking the user code. It will only extend the user's possibilities.

Berkus

unread,
Oct 8, 2013, 10:50:34 AM10/8/13
to std-pr...@isocpp.org

On 08 Oct 2013, at 17:06, Diego Sánchez <dsd...@gmail.com> wrote:
The idea is that you can call f( y : 42 )  or f ( y :42, z : 666 ), but not f( z: 666, y: 42 ). This would preserve the function signature after filling the gaps.


I’d say f(z:666, y:42) is fine, since compiler knows the order, it can fill the gaps.

In Objective-C, where this seems to come from the “names” of the arguments are part of the method signature (but not the formal argument names). For example renaming an argument in the source code is gonna be a BIC change, I’m afraid, so I’m cautious about allowing it everywhere, hence the clearly distinct “label” syntax.

Ville Voutilainen

unread,
Oct 8, 2013, 11:44:33 AM10/8/13
to std-pr...@isocpp.org
It also avoids mucking with the type of such a function declaration, and it thus allows
function pointers, bind and generic code to just work with it like they work with
"legacy" functions. The safety is not so important to break old-convention
usages for such functions.

Thiago Macieira

unread,
Oct 8, 2013, 11:44:53 AM10/8/13
to std-pr...@isocpp.org
On terça-feira, 8 de outubro de 2013 17:50:34, Berkus wrote:
> > The idea is that you can call f( y : 42 ) or f ( y :42, z : 666 ), but
> > not f( z: 666, y: 42 ). This would preserve the function signature after
> > filling the gaps.>
> >
>
> I’d say f(z:666, y:42) is fine, since compiler knows the order, it can fill
> the gaps.

What happens if you call f(z: w(), y: h())? What is called first, w() or h()?

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

Ville Voutilainen

unread,
Oct 8, 2013, 11:50:18 AM10/8/13
to std-pr...@isocpp.org
On 8 October 2013 18:44, Thiago Macieira <thi...@macieira.org> wrote:
On terça-feira, 8 de outubro de 2013 17:50:34, Berkus wrote:
> > The idea is that you can call f( y : 42 )  or f ( y :42, z : 666 ), but
> > not f( z: 666, y: 42 ). This would preserve the function signature after
> > filling the gaps.>
> >
>
> I’d say f(z:666, y:42) is fine, since compiler knows the order, it can fill
> the gaps.

What happens if you call f(z: w(), y: h())? What is called first, w() or h()?



Personally I don't mind if that remains unspecified as with normal functions.
The named parameters are then just syntactic sugar with no additional
magic.

Diego Sánchez

unread,
Oct 8, 2013, 12:55:07 PM10/8/13
to std-pr...@isocpp.org

I also agree that naming parameters at call place should be optional. Compilers could implement a warning to alert the user if a named parameter goes unnamed, but that should remain optional and disabled by default.

Anyhow, following Bengt Gustafsson 's concern, suppose we have something like this:

void f( a:0, b:0, c:0, d:0, e:0, f:0 );

A user may want to write f( c:2, 3, a:7, 4 );

The most obvious interpretation is that a = 7, b =4, c = 2, d = 3, e = 0, f = 0. But this is error prone and obscures more than clarifies the code.

And taking it one step beyond:  f( c:2, 3, a:7, 4, 5 );

Now c has been specified twice.

That's why I proposed to forbid reordering, but if a rule exists that once you start labeling you must keep labeling this problem is probably solved.

Now a couple points to consider:

 What if a library writer wants to define an argument to be mandatory but wants to provide a label for it? That would mean that the user must name that and all remaining parameters for the function.

f( int a, bool b:, int c = 0, int d = 0 );

This would be invalid, since there is no way you could name c or d. So the library writer must declare the function as:

f( int a, bool b:, int c: = 0, int d: = 0 );

And, if a user wants to name the boolean, she must write:

f( 12, b:true, c:1, d:2 );

which may be cumbersome if the function has a lot of arguments, all of them with long, descriptive names; this would probably lead the user to completely drop the label.



Another point I would like considered is: would this seamlessly extend into template parameters?


Ville Voutilainen

unread,
Oct 8, 2013, 1:00:58 PM10/8/13
to std-pr...@isocpp.org
On 8 October 2013 19:55, Diego Sánchez <dsd...@gmail.com> wrote:

Now a couple points to consider:

 What if a library writer wants to define an argument to be mandatory but wants to provide a label for it? That would mean that the user must name that and all remaining parameters for the function.

f( int a, bool b:, int c = 0, int d = 0 );

This would be invalid, since there is no way you could name c or d. So the library writer must declare the function as:

f( int a, bool b:, int c: = 0, int d: = 0 );

Yes.
 

And, if a user wants to name the boolean, she must write:

f( 12, b:true, c:1, d:2 );

Assuming that the user didn't want to use the default arguments for c and d, yes.
 

which may be cumbersome if the function has a lot of arguments, all of them with long, descriptive names; this would probably lead the user to completely drop the label.

All things considered, I don't consider this a problem worth solving at the cost of other things.

 

Another point I would like considered is: would this seamlessly extend into template parameters?



I guess it should.

Berkus

unread,
Oct 8, 2013, 1:39:50 PM10/8/13
to std-pr...@isocpp.org
Counter question: what happens if you call f(w(), h())? What is called first, w() or h()?

I think it is best left unspecified as for the general function argument evaluation order. The order that the compiler “knows" and that I was talking about is the order of function arguments, not their evaluation order. Given f(z: 666, y: 42) it’s fairly easy to see the only missing defaulted argument is x and fill that in. This can actually help a bit for the defaulted arguments order, e.g.

int fun_with_def(int x, int y: = 22, int z: = 33);

Usually you cannot run fun_with_def() without specifying y, because defaulted arguments require you specify them in order. However, with named arguments you could have this valid code:

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

Brendon Costa

unread,
Oct 8, 2013, 3:15:21 PM10/8/13
to std-pr...@isocpp.org

On 9 October 2013 00:14, Diego Sánchez <dsd...@gmail.com> wrote:
Skipping over optional argument

I don't see the reason for the restrictions or the declaration markup. What we are saying is that we can only call functions that are marked up in the declaration with keyword arguments? I.e. Named arg calls cant be applied to existing functions.

Why do we need to decorate the declaration at all?

Why cant we have:
void f(int x, int y=0, int z=0);

and be able to call it like:
f(x:3, z:12);
f(z:12, x:3); // Order does not matter for named params
f(1,2,3);
f(1, z:0);

f(2, x:12); // Compile error

If the reason is that existing declarations can already have multiple different arg names, then that could easily become a compile error when attempting to use named arg calls with those functions. All other functions written in a sensible way could be called using the named arg syntax. You can thus use legacy code, or code written in C etc.

This would not change anything about a function declaration or ABI or API in a non-backwards compatible way. It only adds extra ways to be used at the call site.

Surely this can be resolved easily:
* First resolve non-named args (holes not allowed, must come before named args)
* Second resolve named args
* Resolve any defaults
* If any args in the function call don't have a value then it is a compile error




Zhihao Yuan

unread,
Oct 8, 2013, 3:18:14 PM10/8/13
to std-pr...@isocpp.org
On Tue, Oct 8, 2013 at 3:15 PM, Brendon Costa <brendon...@gmail.com> wrote:
> Why do we need to decorate the declaration at all?
>
> Why cant we have:
> void f(int x, int y=0, int z=0);
>
> and be able to call it like:
> f(x:3, z:12);

Python has it and receives lots of criticisms. Parameter
names are implementation details unless explicitly exposed.

Diego Sánchez

unread,
Oct 8, 2013, 3:31:34 PM10/8/13
to std-pr...@isocpp.org

This is the second time you mention Python. Could you please ellaborate on what kind of problems have they found? We can always learn from other's mistakes.

Philipp Stephani

unread,
Oct 8, 2013, 4:02:37 PM10/8/13
to std-pr...@isocpp.org
One example is with unused parameters: Static code checkers often warn about these and to mark them as intentionally unused you can introduce a special convention, like prefixing them with "unused". However, that breaks in subtle ways if some caller uses named arguments.
In most programming languages, parameters and function-local variables are similar in their behavior, e.g. they have the same scope and you can rename them freely without breaking anything. If you generally allow named arguments for all parameters like Python does, this assumption breaks. This is not necessarily bad, but people need to be aware of it. For C++, it seems especially impossible to just enable named arguments for existing functions, because of ABI breakage (name mangling), assumptions about the type of functions and function pointers, and documentation vs. code issues (many standard library implementations use a double-underscore prefix for all parameters because users cannot legally redefine such names).
I like named arguments in Python, but for C++ I'd rather lean towards allowing record syntax ({ .attr = value }). This avoids breakage, is compatible with existing "options" parameter conventions, and is compatible with C. The amount of additional syntax required (dots plus a set of braces) seems sustainable as well.


2013/10/8 Diego Sánchez <dsd...@gmail.com>

This is the second time you mention Python. Could you please ellaborate on what kind of problems have they found? We can always learn from other's mistakes.

--

Richard Smith

unread,
Oct 8, 2013, 4:03:43 PM10/8/13
to std-pr...@isocpp.org
Yes, sorry, I meant API, not ABI. But depending on the exact rules, it could be both. For instance, the parameter names might need to be included in some mangled names, because they could affect which overload is selected. And in some corner cases, it will be an ABI break no matter what else we decide:

// library
int f(int a); // #1
double f(double d);

// user code
void g(decltype(f(d:0)) x);

Changing the parameter name of #1 from 'a' to 'd' changes the type of 'g'.

Fundamentally, it's a really really bad idea to take part of a declaration that was previously just documentation, and promote it to being part of the semantics without any kind of opt-in. We have no reason to think that existing parameter names are designed for this, and we'd suddenly be making them part of the interface.

Zhihao Yuan

unread,
Oct 8, 2013, 4:13:49 PM10/8/13
to std-pr...@isocpp.org
Python2 's arguments look like this:

def f(x, y):

you can call it through

f(3, 4)
f(3, y=4)
f(y=4, x=3)

When the argument list grows, this become unmaintainable because
you must do not break existing code (which may always uses
sequential calls). For example, if you have an argument list like this:

connect(database, timeout, detect_types, isolation_level,
check_same_thread, factory, cached_statements)

Then you can only extend the list by appending.

So there was a trick around, which captures all named arguments
passed in by a dict (with **kwargs) and forwards then to an internal
function, so that those parameters are "keyword-only".

But obviously it makes the implementation ugly, so Python 3 added
keyword-only arguments directly:

http://www.python.org/dev/peps/pep-3102/

So that

def f(a, b, *, c):

All parameters after the special '*' can only be called with names.

Another problem is that parameter names are unnecessarily
exposed. Basically, if you see a parameter in the standard
which is poorly named (like "s"), then that parameter is intended
to be called without name. There is also a trick to forward
sequential arguments, but Python has a tradition like relay
on naming, so such a trick has not become a language feature.
Instead, they have a whole library extension to solve those
confusions:

http://www.python.org/dev/peps/pep-0362/

Signature object. So that you can use
Parameter.POSITIONAL_ONLY,
Parameter.POSITIONAL_OR_KEYWORD, and
Parameter.KEYWORD_ONLY to annotate a function.
There we go.

Philipp Stephani

unread,
Oct 8, 2013, 4:26:02 PM10/8/13
to std-pr...@isocpp.org



2013/10/8 Zhihao Yuan <z...@miator.net>

On Tue, Oct 8, 2013 at 3:31 PM, Diego Sánchez <dsd...@gmail.com> wrote:
> This is the second time you mention Python. Could you please ellaborate on
> what kind of problems have they found? We can always learn from other's
> mistakes.

Python2 's arguments look like this:

  def f(x, y):

you can call it through

  f(3, 4)
  f(3, y=4)
  f(y=4, x=3)

When the argument list grows, this become unmaintainable because
you must do not break existing code (which may always uses
sequential calls).  For example, if you have an argument list like this:

  connect(database, timeout, detect_types, isolation_level,
     check_same_thread, factory, cached_statements)

Then you can only extend the list by appending.

So there was a trick around, which captures all named arguments
passed in by a dict (with **kwargs) and forwards then to an internal
function, so that those parameters are "keyword-only".

But obviously it makes the implementation ugly,

Also it means that missing or unknown arguments are not detected at the interface level, but somewhere deep in the implementation. It's not uncommon to see several levels where the kwargs are only passed around and then finally investigated by an internal functions. That means errors in the argument list lead to a stack trace where the actual problem is somewhere in the middle rather than at the top. (Similar to C++'s issues with unrestricted template parameters where compiler errors point to some internal function.)
 
so Python 3 added
keyword-only arguments directly:

  http://www.python.org/dev/peps/pep-3102/

So that

  def f(a, b, *, c):

All parameters after the special '*' can only be called with names.

Which makes passing around the kwargs to internal functions much more cumbersome. Often a dedicated options type (e.g. a namedtuple) is better than a large number of keyword arguments.
 

Another problem is that parameter names are unnecessarily
exposed.

Arguably that could be applied for other names as well. It looks like we treat parameter names as internal purely because of historical circumstances ("mathematics does it that way").

Zhihao Yuan

unread,
Oct 8, 2013, 4:42:08 PM10/8/13
to std-pr...@isocpp.org
On Tue, Oct 8, 2013 at 4:26 PM, Philipp Stephani <p.ste...@gmail.com> wrote:
> Often a dedicated options type (e.g. a namedtuple) is better
> than a large number of keyword arguments.

Lua uses this approach. And they even has a syntax
sugar to use func{ <table> } call instead of
func({ <table> }). I'm not particularly in favor of this;
sometimes a language extension makes things cleaner.
(And we don't have such convenient struct in C++ I guess).

>> Another problem is that parameter names are unnecessarily
>> exposed.
>
> Arguably that could be applied for other names as well. It looks like we
> treat parameter names as internal purely because of historical circumstances
> ("mathematics does it that way").

Good or bad, this tradition had better be kept, I think.
Otherwise, transitional code can quickly become
non-portable.

Diego Sánchez

unread,
Oct 8, 2013, 5:02:06 PM10/8/13
to std-pr...@isocpp.org

If I got it right, all those problems steem from the fact that in Python function definition is declaration.

In c++ the names of the arguments are not part of the signature, so you are free to define different names for them in your include files; even different default values in different compilation units.

Bengt Gustafsson

unread,
Oct 8, 2013, 5:05:03 PM10/8/13
to std-pr...@isocpp.org
I think that the strongest argument against allowing non-colon formals to be called by name has not been mentioned explicitly: The current standard library implementations don't consistently use the same name for the same parameter (right?). I assume that they don't and thus any code that uses one of those formal names would not be portable! The sheer volume of the standardisation work needed to define all parameter names in the standard would be over the top. And it would probably not be popular to just state that "but you can't use this feature for standard library functions". So, I retract my previous position, even though it would have been nice to just be able to start using named arguments at call sites...

On the ordering issue I strongly advocate against having to state the actuals in the same order as the formals. One of the main drivers of having named arguments is that you don't have to remember the order of the formal parameters. With this restriction you must remember (or look up) both the names and the order!

The idea that once you start naming actuals you must continue seems not to be so restricting, except that in some cases I would suspect that parameters exist in pairs or groups such as x and y, buf and count or r, g, b Then you may want to use a shorthand by only naming the first actual of the group.

The rule for declarations seems very natural, as you expect callers to use only a portion of the named parameters, so you would not place positional parameters after them. It is also consistent with the current rules for default values for parameters. Thus formal parameters fall into three groups: first positional, undefaulted parameters, then named undefaulted parameters and finally named defaulted parameters. positional defaulted parameters can only exist if there are no named parameters. Does this make sense?

A minor detail is repeated function declarations. Today each default expression can only be stated in one of a set of repeated declarations seen by a TU. It seems logical to extend this rule so that only one of the declarations can have a : on the name (and thus the name used in that declaration is the official name of the parameter!). Another possibility would be to ban declaration repetition if any of the repeats have a : in the parameter list. That would be fine with me.

So far I have been assuming that a function signature is still only the sequence of types for all its parameters. Is anyone contemplating to allow both f(int x:) and f(int y:) in the same scope? I hope not, that would be much larger departure from the current thinking... especially knowing which is which when defining the functions would be tricky and inconsistent with current lax rules of parameter names...







Den fredagen den 4:e oktober 2013 kl. 09:18:49 UTC+2 skrev Berkus Infinitus:
I've been thinking about named arguments support in C++ with a simple, non-intrusive syntax. I've posted a formatted version of the idea here https://github.com/berkus/cpp_named_args

I would like to hear what do you think is wrong with this approach and why would it not work, before I jump to patch up LLVM to support this feature?

Diego Sánchez

unread,
Oct 8, 2013, 5:07:00 PM10/8/13
to std-pr...@isocpp.org



2013/10/8 Richard Smith <ric...@metafoo.co.uk>

Yes, sorry, I meant API, not ABI. But depending on the exact rules, it could be both. For instance, the parameter names might need to be included in some mangled names, because they could affect which overload is selected. And in some corner cases, it will be an ABI break no matter what else we decide:

// library
int f(int a); // #1
double f(double d);

// user code
void g(decltype(f(d:0)) x);



You forgot to mention the include file:


// library
int f(int a) // #1
{
...
}
double f(double d)
{
...
}


// header

int f( int a);
double f( double d );

// user code
void g(decltype(f(d:0)) x);

 
You can now go and change your library parameter names to whatever you want; the function signatures will be the same.

Brendon Costa

unread,
Oct 8, 2013, 9:22:21 PM10/8/13
to std-pr...@isocpp.org
> I think that the strongest argument against allowing non-colon formals to be
> called by name has not been mentioned explicitly: The current standard
> library implementations don't consistently use the same name for the same
> parameter (right?). I assume that they don't and thus any code that uses one

If we introduce named params wether implicit or explicit, I think we
should also standardise STL interfaces anyway. Otherwise it seems
silly to have named args as a language feature and not allow users to
make use of STL with named args (at least on public interfaces).

Many public code interfaces (not STL) already define sensible names
for params in their public interface as a form of documentation. I
know all API's we produce at work do this. It is just a little
annoying that we would have to markup these interfaces to support
named params usage. Especially for the C interfaces being used from
C++ code. This is one reason why I still prefer not requiring
declarations to be marked up. But my point is that regardless, I think
we will want to standardize a lot of STL parameter names.


> On the ordering issue I strongly advocate against having to state the
> actuals in the same order as the formals. One of the main drivers of having

Agreed.

Philipp Stephani

unread,
Oct 12, 2013, 12:04:36 PM10/12/13
to std-pr...@isocpp.org



2013/10/8 Zhihao Yuan <z...@miator.net>

On Tue, Oct 8, 2013 at 4:26 PM, Philipp Stephani <p.ste...@gmail.com> wrote:
> Often a dedicated options type (e.g. a namedtuple) is better
> than a large number of keyword arguments.

Lua uses this approach.  And they even has a syntax
sugar to use func{ <table> } call instead of
func({ <table> }).  I'm not particularly in favor of this;
sometimes a language extension makes things cleaner.
(And we don't have such convenient struct in C++ I guess).


Not yet, we would at least have to adopt C's named initializers:

f(a, {.b = 1, .c = 2})

Then we could think of allowing leaving out the outer braces:

f(a, .b = 1, .c = 2)

For other uses, we can adopt C# and VB's feature of auto-generating anonymous record types:

f(a, new {.b = 1, .c = 2})  // creates a new struct type

Lots of things are still possible with only syntactic sugar and without modifying the type system in complex ways.

Vicente J. Botet Escriba

unread,
Oct 12, 2013, 6:30:44 PM10/12/13
to std-pr...@isocpp.org
Le 12/10/13 18:04, Philipp Stephani a écrit :
+1

Designated data member initialization + Non static data members initialization ~ Named  parameters.

Vicente



    
Reply all
Reply to author
Forward
0 new messages