You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
If the term “feature envy” wasn’t already used to designate a refactoring, we would have employed it to talk about named arguments: it’s a feature that languages that don’t have it envy to the languages that do.
Named arguments consist in specifying at call site the parameter names of the arguments passed. For instance, consider this function:
void displayCoolName(std::string const& firstName, std::string const& lastName)
{
std::cout << "My name is " << lastName << ", " << firstName << ' ' << lastName << '.';
}
The call site of that function looks like this:
displayCoolName("James", "Bond");
(hey, want to try it out with your own name to see how cool your name sounds?)
With named arguments, the call site would rather look like that:
It has the advantage of being more explicit so that you don’t mix up the order of the parameters. Also, a reader of the call site doesn’t have to go check the function’s prototype to understand the meaning of the function’s parameters.
Some languages have this. Objective-C has it, Python has something not far, hell even C99 has something resembling it for initiating structures.
And us in C++? We stand here, envying the feature.
Envy no more, here is a technique to implement named arguments in C++.
I will present this in 3 steps:
step 1: strong types to differentiate parameters,
step 2: a trick to get the right syntax,
step 3: going further: passing the parameters in any order.
If you are a regular reader of Fluent C++, you probably have already encountered strong types.
Strong types consist in replacing a type with another one that adds meaning through its name. In our above example we could create two strong types, FirstName and LastName that both wrap a std::string to pin a specific meaning (like representing a first name, or a last name) over it.
Let’s reason about this syntax: we need the first argument to be of type FirstName. So we need to define an object firstName that has an operator= that takes a std::string (or something convertible to it) and that returns a FirstName.
Let’s implement the type of this object firstName. We call this type argument. Since argument must know FirstName, which is a template class, I think the most convenient is to implement argument as a nested class inside the class FirstName.
FirstName is an alias of NamedType, so let’s add argument inside NamedType:
template< /* the template args of NamedType */ >
class NamedType
{
public:
// ...
The NamedType library now has this feature available.
As a side note, since the firstName and lastName helpers are not supposed to be passed to a function, let’s delete the default-generated move and copy methods:
We’re going to see a way to implement this. However, I don’t think it is production-ready because of some readability drawbacks that we will see.
So from his point on we’re threading into the exploratory, and of course your feedback will be welcome.
Since we don’t know the types of the first and second parameter (either one could be FirstName or LastName), we are going to turn our function into a template function:
Now we need to retrieve a FirstName and a LastName from those arguments.
Picking an object of a certain type amongst several objects of different types sounds familiar: we can use std::get on a std::tuple like when we used strong types to return multiple values.
But we don’t have a std::tuple, we only have function arguments. Fortunately, there is nothing easier than packing function arguments into a std::tuple, thanks to the std::make_tuple function. The resulting code to pick a type looks like this:
One of the drawbacks that I see with this latest technique is that it converts our function into a template. So it needs to go to a header file (unless we do explicit instantiation of all the permutations of the arguments).
To mitigate this, we could extract a thin layer that picks the arguments and forwards them the the function as it was before:
Named Arguments in C++
https://ift.tt/0olJTta
Jonathan Boccara
Ah, named arguments!
If the term “feature envy” wasn’t already used to designate a refactoring, we would have employed it to talk about named arguments: it’s a feature that languages that don’t have it envy to the languages that do.
Named arguments consist in specifying at call site the parameter names of the arguments passed. For instance, consider this function:
The call site of that function looks like this:
(hey, want to try it out with your own name to see how cool your name sounds?)
With named arguments, the call site would rather look like that:
It has the advantage of being more explicit so that you don’t mix up the order of the parameters. Also, a reader of the call site doesn’t have to go check the function’s prototype to understand the meaning of the function’s parameters.
Some languages have this. Objective-C has it, Python has something not far, hell even C99 has something resembling it for initiating structures.
And us in C++? We stand here, envying the feature.
Envy no more, here is a technique to implement named arguments in C++.
I will present this in 3 steps:
I want to thank Reddit user /u/matthieum from which I got largely inspired when he commented on the Reddit thread of Strong types for strong interfaces.
Step 1: Strong types to differentiate parameters
If you are a regular reader of Fluent C++, you probably have already encountered strong types.
Strong types consist in replacing a type with another one that adds meaning through its name. In our above example we could create two strong types,
FirstName
andLastName
that both wrap astd::string
to pin a specific meaning (like representing a first name, or a last name) over it.For this we will use the
NamedType
library, of which you can get a overview on its GitHub page or in Strong types for strong interfaces, if you’re not familiar with it.These are two different types. They both wrap a
std::string
that they expose through their.get()
method.Let’s replace the naked
std::string
s in ourdisplayCoolName
function with those strong types:Now here is what a call site looks like:
That can play the role of named arguments, and it would already be reasonable to stop here.
But let’s wrap around the C++ syntax to get to those oh-so-enviable named arguments, with the equal sign and all.
Step 2: A trick to get the right syntax
We would like to be able to write a call site like this one:
Let’s reason about this syntax: we need the first argument to be of type
FirstName
. So we need to define an objectfirstName
that has anoperator=
that takes astd::string
(or something convertible to it) and that returns aFirstName
.Let’s implement the type of this object
firstName
. We call this typeargument
. Sinceargument
must knowFirstName
, which is a template class, I think the most convenient is to implementargument
as a nested class inside the classFirstName
.FirstName
is an alias ofNamedType
, so let’s addargument
insideNamedType
:We can now create the
firstName
andlastName
helpers to accompany our function:And now the call site of
displayCoolName
looks at last like this:Yay, named arguments!
The NamedType library now has this feature available.
As a side note, since the
firstName
andlastName
helpers are not supposed to be passed to a function, let’s delete the default-generated move and copy methods:Step 3: Going further: passing the parameters in any order
Since we indicate which argument corresponds to what parameter, do we really need a fixed order of arguments?
Indeed, it would be nice if any given call site had the choice to write either this:
or that:
and that it would have the same effect.
We’re going to see a way to implement this. However, I don’t think it is production-ready because of some readability drawbacks that we will see.
So from his point on we’re threading into the exploratory, and of course your feedback will be welcome.
Since we don’t know the types of the first and second parameter (either one could be
FirstName
orLastName
), we are going to turn our function into a template function:Now we need to retrieve a
FirstName
and aLastName
from those arguments.Picking an object of a certain type amongst several objects of different types sounds familiar: we can use
std::get
on astd::tuple
like when we used strong types to return multiple values.But we don’t have a
std::tuple
, we only have function arguments. Fortunately, there is nothing easier than packing function arguments into astd::tuple
, thanks to thestd::make_tuple
function. The resulting code to pick a type looks like this:Let’s use this to retrieve our
FirstName
andLastName
from the arguments:Now we can call either:
or:
And in both cases we get:
One of the drawbacks that I see with this latest technique is that it converts our function into a template. So it needs to go to a header file (unless we do explicit instantiation of all the permutations of the arguments).
To mitigate this, we could extract a thin layer that picks the arguments and forwards them the the function as it was before:
Another drawback is that the names of the parameters in the prototype lose all their meaning (“Arg0″…).
If you see other drawbacks, or if you see how to improve this technique to be able to pass function argument in any order, share it in a comment!
Don't want to miss out ? Follow:   
Share this post!
via Fluent C++
December 17, 2024 at 03:54PM
The text was updated successfully, but these errors were encountered: