Friday, July 18, 2014

Is the non-pointer syntax for declaring function pointer parameters not worth mentioning?

In my view, one of the most important favors a technical writer can do for his or her readers is shield them from information they don't need to know. One of my standard criticisms of authors is "S/he knows a lot. S/he wrote it all down." (Yes, I know: the "his or her" and "s/he" stuff is an abomination. Please suffer in silence on that. There's a different fish I want to fry in this post.)

I try not to commit the sin of conveying unimportant information, but one of the hazards of spending decades in this business is that you learn a lot. After a while, it can be hard to evaluate what's worth knowing and what's best left unsaid. With that in mind, the current draft of Effective Modern C++ contains this passage (more or less):
Suppose our function f can have its behavior customized by passing it a function that does some of its work. Assuming this function takes and returns ints, f could be declared like this:
     void f(int (*pf)(int));         // pf = "processing function"
It’s worth noting that f could also be declared using a simpler non-pointer syntax. Such a declaration would look like this, though it’d have the same meaning as the declaration above:
     void f(int pf(int));            // declares same f as above

One of my reviewers argues that the second way of declaring a function pointer parameter is not only not worth noting, it's not worth knowing. That means I should omit it. But I find myself reluctant to do that. I like the fact that function pointer parameters can be declared without throwing in additional parentheses and figuring out which side of the asterisk the parameter name has to go on. But maybe that's just me.

So what do you think? Should I jettison the aside about the asterisk-free way to declare the parameter pf, or should I keep it?




datgame said...

Since this makes code easier to read, I think it is worth knowing about.
It's that simple.

C. Keith Ray said...

I'd put the version with the asterisk character into a footnote, mentioning this was the only form available in "the dark ages" before the pointer-free version became part of a C or C++ standard.

Imdex said...

Maybe better use std::function in this cases?

Anonymous said...

I agree with Keith. The latter is simpler and cleaner and less likely to cause typos in the parentheses or asterisk, so should be preferred.

Anonymous said...

Isn't the 2nd form what makes the most vexing parse possible (not in this case, but in general)?

Júlio César said...

The second way is more clean to read. Make it simple imho.

Randy Gaul said...

It's a valid part of the language. At least mentioning it is important. It's definitely more common to use parentheses and and an asterisk, and it is valuable to also mention this.

For example: sometimes I accidentally declare function pointers when I mean to call constructors; this information is helpful to understand specific compiler errors.

Unknown said...
This comment has been removed by the author.
Geoff said...

My initial reaction was basically OMG WHY DID NOBODY EVER TELL ME THIS? However, I'm not sure it actually matters to a general C++ audience.

As I assume you go on to say after that excerpt, first-class function parameters should almost always be generic function objects in modern C++ (either a template type, or type-erased with std::function) rather than function pointers. That being the case, your readers probably don't need to know how to write function pointer parameters. They may sometimes need to know how to read them, in order to deal with legacy code, but given the seeming rarity of that syntax, it's probably not worth teaching for those purposes either.

grey wolf said...

One thing that would have saved me some mental anguish in the past is this simple fact:

The pointer-free syntax informs the common syntax for function types in template definitions, such as:

std::function<int(int)> pf;

Leaving out this nugget makes explaining templated declarations like this more confusing.

Anonymous said...

I prefer the pointer syntax in that case since the two syntaxes aren't interchangeable in general and the pointer syntax I believe avoids some confusion.

For example, suppose I declare types like this:

using T1 = void (*)();
using T2 = void ();

and I have a function void f();

Then I can write T1 x = f, but not T2 x = f.

Will Hoie said...

As someone who learned C (and C++) a long time ago, and uses them daily, I literally didn't know about the 2nd form, which I think is better, until your post.

So, chalk up another vote for "keep it in."

Matt Calabrese said...

I think it could at least be mentioned, so as to point out:

struct bar {};
struct foo { foo(bar) {} };

int main()
// This is a function declaration
foo foobar( bar() );

// This is an object
foo foobar( (bar()) );

Matt Calabrese said...

@grey wolf, the type passed to std::function in your example isn't a function pointer type, you are passing a function type. The type-decay from function to function pointer just occurs in function parameter declarations.

seth said...

I agree with the reviewer and would further say that the automatic 'adjustments' to argument types, as the C++ spec calls them, are an abomination and they should not be used. This is definitely one of those things inherited for C compatibility that makes C++ worse.

grey wolf said...
"The pointer-free syntax informs the common syntax for function types in template definitions, such as:

std::function pf;"

No, in fact the template syntax is doing what it should do precisely because does not involve the the 'adjustments' that are specified for function parameters. Knowing about the adjustments should have no bearing on knowing how to write a function's type.

Anonymous said...

auto f(auto pf(int) -> int) -> void;


Unknown said...

My vote is to be complete and leave it in. It is much better since it will answer the question that will come up as a result of omitting it.

Unknown said...

Are function references commonly used? If so, the explicit pointer (with the asterisk) is preferable to me.

Tom Ritchford said...

I never knew!

Leave it in, I was happy to learn this.

cubbi said...

Saying that's not worth knowing is exactly like saying "f(int a[10])" is not worth knowing or "f(const int n)" is not worth knowing.

Anonymous said...

Well, I've been doing C++ for a couple decades and didn't know about this one. I guess I should feel bad about that but I've never seen it used either. I think it's clearer, so its lack of popularity may simply be an example of a widespread ignorance.

So yeah, I'd say it's worth mentioning.

Scott Meyers said...

Thanks to everybody for their comments. I've decided to keep the comment in the draft book.

FWIW, many of the other issues that have been raised here (e.g., the most vexing parse, function pointer types vs. function types during template type deduction,specifying function types for use with std::function, etc.) are discussed elsewhere in the book.

Max Kaehn said...

This is the first time I ever saw the non-pointer syntax (I've been on a detour through Java for a few years), so I find it worthwhile to see both.

Charlie said...

In either case, I think it's nicer to have a 'using' beforehand and avoid the C gibberish, that way the name of the parameter isn't buried in a forest of parentheses

using F = int(*)(int);
F f = ::abs; // old C abs

Anonymous said...

Another vote in the "I've used C++ for ages and I sure wish I'd known about that syntax earlier!" bin. That's nifty, keep it! :-)

Amber said...

Don't suggest `std::function<>` (it's shooting a fly with a canon).

Just use

template <typename Sig> using fptr = Sig*;

Simple and succinct. It's also the kind of thing to use to defuse array-ref parameters/return types.

Kola said...

Non-pointer syntax is worth knowing, but the sad thing is that there is no analogue for member functions (at least I was unable to find the correct syntax, is there any?).

Scott Meyers said...

@cola: As far as I know, there is only one syntax for expressing member function pointers, though of course you can create a typedef or type alias.

Unknown said...

The second syntax seemed cool at first glance. But it doesn't work if the return type is a pointer :( This means that you have to think what syntax to use depending on the return type, and that's a reason enough not to use or mention it.

Anonymous said...

I think its worth mentioning... plus the book won't get any more expensive now since I've already paid for it.