4

Say you have a pretty complicated function signature, like this:

void **search( char *inSearch, int inNumToSkip, 
               int inNumToGet, int *outNumResults, int *outNumRemaining );

Say you want to write another function that accepts pointers to functions matching this "search" signature. In examples, I usually see such functions written like this:

void *getFirstResult( char *inSearch,
                      void ** (inSearchFunc*)( char *, int, int, int*, int* ) );

Suppose this function performs the search using the passed-in function and returns only the first result, cleanly destroying the rest, or whatever.

Now, looking at getFirstResult and trying to write a function to pass to it, it is totally non-self-documenting, because there's no information about the "inSearchFunc" parameters included in that definition. You do know it's a search function of some kind that's being passed in, but nothing else.

Note that that same format (parameter types with no names) is valid as a function prototype in a header or forward declaration, so the argument names are always optional, even in the actual function implementation (where they are clearly needed to use the arguments in the function body, but still optional).

I can't find it documented or mentioned anywhere, but this compiles and works fine:

void *getFirstResult( char *inSearch,
                      void ** (inSearchFunc*)(  char *inSearch, 
                                                int inNumToSkip, 
                                                int inNumToGet,
                                                int *outNumResults,
                                                int *outNumRemaining ) );

This is obviously much more clear and self-documenting.

Is this legal and safe C code? Where is this kind of optional syntax documented?

And finally, why doesn't everyone write their function pointer arguments this way for clarity? Especially in examples intended to explain function pointers to beginners, the nameless arguments always look totally opaque and confusing.

In fact, this super-opaque version is legal too:

void *getFirstResult( char *,
                      void ** (*)( char *, int, int, int*, int* ) );

Clearly, everyone would at least give the char * and the (*) a name for clarity, so why not give everything a name?

In my ancient, venerable C++ Primer, it says:

The parameter name is unnecessary in a function declaration. If it is present, its name should serve as a documentation aid.

and then

There is no language-imposed penalty for specifying a different name for a parameter in the declaration(s) and definition of the same function. A reader of the program, however, may become confused.

Jason Rohrer
  • 503
  • 4
  • 14
  • "*Clearly, everyone would at least give...*" No. – alk Jul 10 '15 at 18:44
  • "*Is this legal and safe C code?*" Yes. – alk Jul 10 '15 at 18:45
  • 1
    "*You do know it's a search function of some kind that's being passed in, but nothing else.*" Either read the sources, or the documentation. Or read both. If both isn't available, then do not use the function. – alk Jul 10 '15 at 18:46
  • The names of the parameters in the prototype declaration need not have any relation to those in the actual function definition, and can be omitted. For consistency's sake if nothing else, either name them all or name none of them. See also [Where to document functions in C?](http://stackoverflow.com/questions/3568052/where-to-document-functions-in-c/3568099#3568099) The header where the `getFirstResult()` function is declared should explain enough of how to use it, which would certainly involve an explanation of the callback function pointer. – Jonathan Leffler Jul 10 '15 at 19:33

2 Answers2

4

You can use a typedef to make this easier to read, and get the variable names where you want them as a documentation aid:

typedef void ** (*search_func_t)( char *inSearch,
                                  int inNumToSkip,
                                  int inNumToGet,
                                  int *outNumResults,
                                  int *outNumRemaining );
void *getFirstResult( char *inSearch, search_func_t inSearchFunc );
Ewan Mellor
  • 6,747
  • 1
  • 24
  • 39
1

Well... I personally prefer to name the arguments as well. The only real downside I can think of is that repeating yourself requires you to change both when it changes.

Beyond that... imho it's just clearer

Wolph
  • 78,177
  • 11
  • 137
  • 148