
Named Parameters in C++20 - ibobev
https://pdimov.github.io/blog/2020/09/07/named-parameters-in-c20/
======
marvy
The title is slightly click-bait-y: C++20 doesn't have default parameters. It
talks about how to fake it with structs.

It's a pretty convincing fake, but it has one problem: the person who wrote
the function needs to have written it that way. What I wish more languages had
(C++, Java, etc.) is the following:

Suppose I write some function that take way too many parameters:

void foo(char a, short b, long c, float d, double e){}

What I want, as a caller, is for the language to let me get a struct
corresponding to the argument list, something like this:

    
    
        foo::args whatever;
        whatever.e = 3.14;
        // fill in the rest
    

And then add a bit of magic syntax so we can "unpack" that struct to actually
call foo. (Extending this proposal to handle varargs is left as an exercise to
the reader, because I have no idea.)

~~~
spanishgum
How about `std::make_from_tuple` for constructors or `std::apply` for
functions (C++17)?

It might be a bit verbose, but I think I would still prefer this over native
syntax for unpacking.

~~~
adrift
Why would you kill your compile times like that.

------
gumby
Having programmed in languages with named parameters (Smalltalk, ObjC and
Commonlisp) I’ve found them cumbersome.

Also they can lead to a poor pattern which is functions/methods that take too
many parameters.

They also tend to make generics harder to understand as the possible
parameters can vary. Of course you can make the same problem in C++ via, say,
`auto multiply(int x, int y)` and `auto multiply(double x, double y, enum
rounding rp = rounding::nearest)`

In general they impose the verbosity (in keyboarding and reading) in the
routine cases but don’t free you from having to look up the argument semantics
of lesser-used functions.

Side point: I used “keyboarding” because the obvious word, “typing” has a
homonym which made the sentence confusing!

~~~
kllrnohj
What named parameters fixes is making constant parameters readable. You _can_
API design around this (see for example
[https://wiki.qt.io/API_Design_Principles#The_Boolean_Paramet...](https://wiki.qt.io/API_Design_Principles#The_Boolean_Parameter_Trap)
). You _can_ also always use local constants.

But those are non-trivial or cumbersome in their own way, and often people
don't for their own internal functions. Named parameters neatly side-steps the
issue entirely, by rolling the "local constant" right into the function call
itself.

Like consider these two python snippets:

    
    
         ['Ford', 'BMW', 'Volvo'].sort(true)
    

vs.

    
    
          ['Ford', 'BMW', 'Volvo'].sort(reverse=true)
    
    

The latter is much more readable than the former, and there wasn't any "too
many parameters" issue. It's a way to help create self-documenting code that's
also compiler enforced. And it absolutely frees the reader up from needing to
go lookup what the boolean parameter on "sort" means.

~~~
vore
Another pattern especially for ambiguous Boolean parameters I also find
helpful is something like (in pseudo-ish code):

    
    
      enum SortDirection { FORWARD, REVERSE }
    
      sort(direction : SortDirection = SortDirection.FORWARD) { ... }
    
      ['Ford', 'BMW', 'Volvo'].sort()
      ['Ford', 'BMW', 'Volvo'].sort(SortDirection.REVERSE)

~~~
kllrnohj
Yup, that's the API design tweak that QT recommends as well. But it's a _lot_
more boilerplate, and that sort of boilerplate basically never happens for
internal-only methods. Named parameters is basically the middle ground
solution for the "quick" hacks that have a tendency to be rather permanent
hacks.

------
tasubotadas
It's a feature that I learned to use in Python and one that I consistently now
miss in other languages (rust, java, typescript, javascript).

~~~
reificator
In ts/js passing an object is not very much effort and doesn't add much noise
to reading the code.

~~~
wk_end
It's annoying in TS because (AFAIK?) you need to specify the type and unpack
the object:

    
    
      function foo({arg1, arg2, arg3}:{arg1: Thing1, arg2: Thing2, arg3: Thing3) {
        ...
      }

~~~
KarimDaghari
You could specify the type first (instead of doing it inline) and then unpack
(if you want)

------
jleahy
Unfortunately in many cases this will cause all of the parameters to be passed
via the stack rather than in registers. It’s a meaningful penalty for the kind
of people who would willingly select c++ as a language today (rather than,
say, python or java).

~~~
flohofwoe
From my experience with C99: if the struct size is 16 bytes or less, the
values will be packed into registers. But true as soon as the struct grows
bigger, the entire content will be passed on the stack.

IMHO this sort of named arguments "easter egg" is fine for big "option bag
structs" passed to functions that are not performance critical (or for small
struct <= 16 bytes). For other cases, regular args are better.

OTH it seems like passing normal args also spills over into the stack very
soon (only 4 registers used?):

[https://www.godbolt.org/z/Mz9MTG](https://www.godbolt.org/z/Mz9MTG)

The main difference seems to be that normals arguments "spill over", while
passing structs by value puts everything on the stack once the threshold size
is passed.

~~~
nikki93
Six of the parameters use registers here. the `lea` is putting the sum of the
first two into `eax`, then the rest accumulate more adds into `eax`. Only the
last 3 parameters are reading from stack memory (the `dword ptr [*]` stuff).

In any case you don't get to actually see the passing at the callsite because
the result `45` is inlined into `main()`. :)

~~~
flohofwoe
Thanks for the clarification (also to archgoon). To your last point, I should
have used -O1 instead of -O2, this also makes it clear what happens on the
callsite.

~~~
ddalcino
You don’t need to lower the optimization level to see what happens at the call
site: you can turn the function ‘blub’ into a function prototype. The
optimizer cannot in-line your code if it isn’t available.

[https://www.godbolt.org/z/sj11rc](https://www.godbolt.org/z/sj11rc)

------
mucholove
This is my favorite feature of Objective-C / Smalltalk.

It makes everything so readable. I can come back to my code a few weeks later
and I don’t really need to look up function signatures.

In particular, my favorite thing about this in Objective-C is that the C
functions have a different way of being called so you can easily distinguish
between the differences in paradigms.

I wonder what other languages force all calls to be via named parameters.
Would love to have another option :)

~~~
masklinn
Swift, kinda: named is the default but you can make parameters explicitly
positional (by setting the label to _).

I guess in Python you could l’ont your codebase to require keyword parameters
or opt-in positionals.

------
ambyjkl
This is quite interesting, a very elegant struct hack. Dlang is working on an
RFC for implementing this feature right into the language, which would
probably make it the first systems language to implement this without structs.

[https://github.com/dlang/DIPs/blob/master/DIPs/DIP1030.md](https://github.com/dlang/DIPs/blob/master/DIPs/DIP1030.md)

~~~
chiffre
Ada arguably is a system programming language and it supports named
parameters.

------
axilmar
Personally I'd like c++ to:

a) have default parameters in any parameter position. I.e. for this to be
legal:

    
    
        foo(int x = 0, int y, int z = 0) {}
    

b) to allow me to invoke a function like this:

    
    
        foo(,) //x and z are passed as default values:
    

c) or like this using named parameters:

    
    
        foo(x = 5, y = 1,)

------
randyrand
personally i’d just write a linter that adds names via comment

open(/ _port_ / 127)

------
cma
Is there a way to have initialization fail at compile time if all members
aren't included? So that if new members are added, all call sites become
invalid until updated?

~~~
OskarS
I don't think you'd want to. One of the primary usages of this kind of thing
would be like when you need to pass in some massive struct of options to some
dumb API function (you know the ones!), and then you'd absolutely want default
values for all the things you don't want to specify.

Also, some might argue that what you're describing (call sites not becoming
invalid when you update the function) is a feature, not a bug. This way, you
can add options to your functions and still have everything compile like it
should.

I wouldn't personally argue that though, i think it leads to bad practice
where functions take a bazillion options and they become horrible tech debt.
I'm working in a mixed C++ and Lua codebase, and the Lua codebase is
_littered_ with functions that take, like, 9 different arguments that change
the behavior of the function. When I asked one of my colleagues about this
style, he said it was one of his favorite features of Lua, that you can add
arguments to a function to change the behavior in certain cases but keep the
original functionality (guarded with default values or if's or whatever). It's
made much of the code a living nightmare!

So, I guess, in the process of writing this comment I've come over to your
side :) say no to adding arguments with default values!

~~~
cma
I don't mind it for thing like pyplot where you have tons of style options
hidden away like that.

But I'd definitely like to be able to say this one isn't optional or
implicitly default constructed (or worse uninitialized, maybe that is already
precluded by any amount of brace initialization, or maybe not if the class is
POD, I'll have to check).

Someone suggested below deleting default constructors, you could make a
template class that did that and forwarded other constructors, but I'd still
like to be able to allow explicit default initialization in the ones that
aren't considered optional.

------
amboo7
Bravo, it's time

