Question

There's the following declarations:

void qsort(void *lineptr[], int left, int right, int (*comp)(void *, void *));
int numcmp(char *, char *);
int strcmp(char *s, char *t);

Then, somewhere in the program there is the following call:

  qsort((void**) lineptr, 0, nlines-1, 
                    (int (*)(void*,void*))(numeric ? numcmp : strcmp));

(Ignore the first three arguments and numeric).

I ask what is this:

(int (*)(void*,void*))(numeric ? numcmp : strcmp)

I understand that qsort is expecting a "pointer to function that gets two void pointers and returns an int" as it's 4th argument but how what's written above satisfies that? It seems to me like some sort of cast because it is made of two parentheses, but that would be a very odd cast. Because it takes a function and makes this function a "pointer to function that gets two void pointers and returns an int". Which is meaningless.
(I followed here the rule that a type type in parenthesis before a variable promotes the variable to that type).

So I think I just get it wrong, maybe someone can tell me how to read this, what's the order?

Was it helpful?

Solution

What's happening here is indeed a cast. Lets ignore the ternary for a second and pretend that numcmp is always used. For the purpose of this question, functions can act as function pointers in C. So if you look at the type of numeric it is actually

(int (*)(int*,int*))

In order for this to be properly used in qsort it needs to have void parameters. Because the types here all have the same size with respect to parameters and return types, it's possible to substitute on for the other. All that's needed is a cast to make the compiler happy.

(int (*)(void*,void*))(numcmp )

OTHER TIPS

You've missed the trick here - the portion

(numeric ? numcmp : strcmp)

is using the ternary operator to choose which function is being called inside of qsort. If the data is numeric, it uses numcmp. If not, it uses strcmp. A more readable implementation would look like this:

int (*comparison_function)(void*,void*) = 
    (int (*)(void*,void*))(numeric ? numcmp : strcmp);
qsort((void**) lineptr, 0, nlines-1, comparison_function);

You can do it without the function pointer cast. Here's how. In my experience, in most places, if you are using a cast, you are doing it wrong.

Note that the standard definition of qsort() includes const:

void qsort(void *base, size_t nmemb, size_t size,
           int (*compar)(const void *, const void *));

Note that the string comparator is given two 'char **' values, not 'char *' values.

I write my comparators so that casts are unnecessary in the calling code:

#include <stdlib.h>    /* qsort() */
#include <string.h>    /* strcmp() */

int num_cmp(const void *v1, const void *v2)
{
    int i1 = *(const int *)v1;
    int i2 = *(const int *)v2;
    if (i1 < i2)
        return -1;
    else if (i1 > i2)
        return +1;
    else
        return 0;
}

int str_cmp(const void *v1, const void *v2)
{
    const char *s1 = *(const char **)v1;
    const char *s2 = *(const char **)v2;
    return(strcmp(s1, s2));
}

Forcing people to write casts in the code using your functions is ugly. Don't.

The two functions I wrote match the function prototype required by the standard qsort(). The name of a function when not followed by parentheses is equivalent to a pointer to the function.

You will find in older code, or code written by those who were brought up on older compilers, that pointers to functions are used using the notation:

result = (*pointer_to_function)(arg1, arg2, ...);

In modern style, that is written:

result = pointer_to_function(arg1, arg2, ...);

Personally, I find the explicit dereference clearer, but not everyone agrees.

Whoever wrote that code snippet was trying to be too clever. In his mind, he probably thinks he is being a good programmer by making a clever "one-liner". In reality, he is making code that is less readable and is obnoxious to work with over the long term and should be rewritten in a more obvious form similar to Harper Shelby's code.

Remember the adage from Brian Kernighan:

Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it.


I do lots of performance critical coding with hard real time deadlines... and I have still not seen a place where a dense one-liner is appropriate.

I have even messed around with compiling and checking the asm to see if the one-liner has a better compiled asm implementation but have never found the one-liner to be worth it.

As others have pointed out, for

(int (*)(void*,void*))(numeric ? numcmp : strcmp)

then the following is a type cast

(int (*)(void*,void*))

and the expression is

(numeric ? numcmp : strcmp)

C declarations can be quite difficult to read, but it is possible to learn. The method is to start at the inner part and then go right one step, then left one step, continuing right, left, right, left, etc outwards until finished. You do not cross outside a parenthesis before everything inside has been evaluated. For instance for the type cast above, (*) indicates this is a pointer. Pointer was the only thing inside the parenthesis so then we evaluate to the right side outside it. (void*,void*) indicates that is a pointer to a function with two pointer arguments. Finally int indicates the return type of the function. The outer parenthesis makes this a type cast. Update: two more detailed articles: The Clockwise/Spiral Rule and Reading C Declarations: A Guide for the Mystified.

However, the good news is that although the above is extremely useful to know, there is an extremely simple way to cheat: the cdecl program can convert from C to English description and vice versa:

cdecl> explain (int (*)(void*,void*))
cast unknown_name into pointer to function (pointer to void, pointer to void) returning int
cdecl> declare my_var as array 5 of pointer to int
int *my_var[5]
cdecl>

Exercise: What kind of variable is i?

int *(*(*i)[])(int *)

Answer in rot13 in case you do not have cdecl installed on your machine (but you really should!):

pqrpy> rkcynva vag *(*(*v)[])(vag *)
qrpyner v nf cbvagre gb neenl bs cbvagre gb shapgvba (cbvagre gb vag) ergheavat cbvagre gb vag
pqrpy>

I would probably read it like this:

typedef int (*PFNCMP)(void *, void *);

PFNCMP comparison_function;

if (numeric)
{
    comparison_function =  numcmp;
}
else
{
    comparison_function = strcmp;
}

qsort((void**) lineptr, 0, nlines-1, comparison_function);

The example in the question has an explicit case.

Your logic is correct i think. It is indeed casting to "pointer to function that gets two void pointers and returns an int" which is the required type by the method signature.

Both numcmp and strcmp are pointers to functions which take two char* as parameters and returns an int. The qsort routine expects a pointer to a function that takes two void* as parameters and returns an int. Hence the cast. This is safe, since void* acts as a generic pointer. Now, on to reading the declaration: Let's take your strcmp's declaration:

 int strcmp(char *, char *);

The compiler reads it as strcmp is actually:

 int (strcmp)(char *, char *)

a function (decaying to a pointer to a function in most cases) which takes two char * arguments. The type of the pointer strcmp is therefore:

 int (*)(char *, char *)

Hence, when you need to cast another function to be compatible to strcmp you'd use the above as the type to cast to.

Similarly, since qsort's comparator argument takes two void *s and thus the odd cast!

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top