What, if any, is a technical reason to lead with a function pointer argument and then arguments for that function or the other way around?

Perhaps based deeper on VLAs, debuggers, _Geneirc_, some presently proposed feature of the next C, or some other corner of C, the question:

Is there a coding advantage (objective reason) that prefers one function signature over the other when functions pointers are involved?

I have found none.


Typically the order of arguments in a function signature is simply a style choice. I am looking though for a reason why in a non-(...) function with a function pointer argument an advantage may exist for a certain order.

I could define a function containing a function pointer as

// Function pointer before its arguments a1,b1 it will eventually use
void foo1(void (*fun)(int a0, double b0), int a1, double b1) {
  fun(a1, b1); 
  fun(a1,-b1);
} 

or

// Function pointer after the a2,b2 arguments
void foo2(int a2, double b2, void (*fun)(int a0, double b0)) {
  fun(a2, b2); 
  fun(a2, -b2);
} 

Researching the standard C library offers 2 opposite examples.

// Function pointer before `context`.
errno_t qsort_s(void *base, rsize_t nmemb, rsize_t size,
    int (*compar)(const void *x, const void *y, void *context), void *context); 

// Function pointer last.
void (*signal(int sig, void (*func)(int)))(int);

So far, this certainly is a style choice.


Order is important with VLA

I considered VLAs where the arguments before arr2 are needed and somehow the signature of fun2() might be based on row2, col2, arr2 and derive some benefit. This would offer an advantage for the function pointer to trail.

int foo2(int row2, int col2, char arr2[row2][col2], void (*fun2)(TBD_Signature);

But I came up with no useful example.


[Edit] Perhaps another way to look at one aspect of this question:

Can the signature of the function pointer derive from prior arguments of the function in a useful manner?

 int bar(int some_arg, other_args, 
     (*f)(signature based on some_arg, other_args or their type));

Solution 1:

GCC lets you write:

int function1(int arg1, int arg2, int (*function)(int array1[arg1], int array2[arg2]));

which declares a function taking a pointer to function with VLA arguments — but I don't think that pointer to function is usable because the size information isn't available to the actual function, so it has no idea about the size of the two VLAs purportedly passed to it. So you can't write the code for the function to be passed as a pointer.

Now, you could perhaps use:

int function2(int arg1, int arg2, int (*function)(int, int, int array1[arg1], int array2[arg2]));

But that is equivalent to:

int function2(int arg1, int arg2, int (*function)(int, int, int array1[*], int array2[*]));

which you can verify by including both those declarations in a single file and noting that there is no conflict reported. The * notation in subscripts is only allowed in function declarations (not in function definitions).

In the same way, the declaration of function1() above is equivalent to:

int function1(int arg1, int arg2, int (*function)(int array1[*], int array2[*]));

And it isn't really clear that it is different from:

int function1(int arg1, int arg2, int (*function)(int array1[], int array2[]));

All three declarations can coexist in a single source file. The function called via the pointer must have some way to determine the size of the two arrays it is given.

An attempt to use [*] in a function definition yields:

error: ‘[*]’ not allowed in other than function prototype scope

Here's a demonstration of the second function (at work:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int function1(int arg1, int arg2, int (*funcptr)(int array1[arg1], int array2[arg2]));

int function2(int arg1, int arg2, int (*funcptr)(int, int, int array1[arg1], int array2[arg2]));
int function2(int arg1, int arg2, int (*funcptr)(int, int, int array1[*], int array2[*]));

static void dump_array(const char *tag, int n, int array[n])
{
    printf("%s (%d):\n", tag, n);
    int length = 0;
    const char *pad0 = "    ";
    const char *pad = pad0;
    for (int i = 0; i < n; i++)
    {
        length += printf("%s%d", pad, array[i]);
        if (length > 70)
        {
            length = 0;
            pad = pad0;
            putchar('\n');
        }
        else
            pad = ", ";
    }
    if (length > 0)
        putchar('\n');
}

static int function(int s1, int s2, int array1[s1], int array2[s2])
{
    dump_array("array1", s1, array1);
    dump_array("array2", s2, array2);
    return s1 + s2;
}

int function2(int arg1, int arg2, int (*funcptr)(int, int, int array1[*], int array2[*]))
{
    int a1[arg1];
    for (int i = 0; i < arg1; i++)
        a1[i] = rand() % 100;
    int a2[arg2];
    for (int i = 0; i < arg2; i++)
        a2[i] = rand() % 100 + 100;
    return (*funcptr)(arg1, arg2, a1, a2);
}

int main(void)
{
    srand(time(0));
    function2(32, 16, function);
    return 0;
}

When run, it might produce:

array1 (32):
    47, 23, 52, 60, 42, 48, 54, 55, 65, 6, 66, 57, 63, 77, 22, 96, 72, 98
    75, 0, 50, 33, 39, 30, 62, 82, 1, 87, 73, 24, 55, 20
array2 (16):
    148, 159, 133, 142, 107, 187, 197, 172, 145, 163, 130, 160, 141, 104
    156, 165