1

While executing this code I get [Error] cast specifies function type at line ptr = (void* (void*, void*))sumx;

I am trying to declare a generic pointer ptr to a function called sumx

Any help would be appreciated. Thank you.

# include <stdio.h>

int sum(int a, int b) {
    return a+b;
}

int* sumx(int *a, int *b) {
    int *p;
    *p = *a+*b;
    return p;
}

int main() {
    int (*p) (int, int);
    void* (*ptr)(void*, void*);

    p = sum;
    ptr = (void* (void*, void*))sumx;


    int s = (*p)(5, 6);

    int a = 10;
    int b = 20;
    int *y = (*ptr)(&a, &b);

    printf("%d\n", s);
    printf("%d\n", *y);
    return 0;
}
Infinity
  • 153
  • 2
  • 10

4 Answers4

3

You are missing a (*) in the cast, which should be this:

ptr = ( void* (*)(void*, void*) )sumx;

However, when using such function pointers, it is generally a lot clearer to use typedef statements:

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

int sum(int a, int b)
{
    return a + b;
}

int* sumx(int* a, int* b)
{
    int* p = malloc(sizeof(int)); // Need to allocate some memory
    *p = *a + *b;
    return p;
}

typedef int (*ifncptr)(int, int);
typedef void* (*vpfncptr)(void*, void*);

int main()
{
    ifncptr p;
    vpfncptr ptr;
    p = sum;
    ptr = (vpfncptr)sumx;

    int s = p(5, 6); // You can just use the fn-ptr for this call ...

    int a = 10;
    int b = 20;
    int* y = ptr(&a, &b); // ^ ... and here, also!

    printf("%d\n", s);
    printf("%d\n", *y);
    free(y); // Don't forget to free the memory.
    return 0;
}
Adrian Mole
  • 49,934
  • 160
  • 51
  • 83
  • `typedef void* (*fncptr)(void*, void*);` --> `typedef int* (*fncptr)(int*, int*);` – David Ranieri May 20 '20 at 08:04
  • So what, compiling `void *(*ptr)(void *, void*) = sumx;` with warnings gives: `warning: initialization from incompatible pointer type` – David Ranieri May 20 '20 at 08:12
  • `typedef void* (*vpfncptr)(void*, void*);` is the signature you are using, isn't it?, so yes, you are doing that. Have you compiled with warnings? `-Wall -pedantic`? – David Ranieri May 20 '20 at 08:16
  • @DavidRanieri Using a direct initialization (`vpfncptr ptr = sumx;`) gives the warning, but using the *explicit cast* (as my code and the OP's) does not! – Adrian Mole May 20 '20 at 08:19
  • Of course it doesn't, the cast hides the warning but the problem is still there :) – David Ranieri May 20 '20 at 08:23
  • @DavidRanieri I agree with you, actually. But note that trying with `ptr = (vpfncptr *)sumx;` **does** generate that exact warning (so the cast doesn't hide everything). – Adrian Mole May 20 '20 at 08:28
  • If I use ```int* (*ptr)(int*, int*);``` directly, why is that extra ```*``` not required? – Infinity May 20 '20 at 13:58
  • @Infinity The extra `(*)` **is** there - it's surrounding the `ptr` name. – Adrian Mole May 20 '20 at 14:26
  • thank you, I get it now. Actually, I was confused about the type conversion. I wasn't aware it is almost similar to the declaration of the function pointer. – Infinity May 20 '20 at 14:38
2
int* sumx(int *a, int *b) {
    int *p;
    *p = *a+*b;
    return p;
}

You are writting to an uninitialized pointer, use malloc to reserve space:

    int *p = malloc(sizeof(*p));

And answering your question:

void* (*ptr)(void*, void*);

is not a valid signature for sumx, instead:

int *(*ptr)(int *,  int*);
David Ranieri
  • 39,972
  • 7
  • 52
  • 94
  • The 'generic' pointer type compiles with `clang-cl` (once the missing `(*)` is added). But good spot for the unallocated `p` in `sumx`. – Adrian Mole May 20 '20 at 07:57
  • 2
    I'm with @David not for the _alignment issue_ but because of the cleaner solution. I honestly cannot think to a scenario in which this strange casting is useful. I would rather define the proper pointer to function and just cast the result (but AFAIK there's no need to cast to void *, so...). – Roberto Caboni May 20 '20 at 08:09
  • @RobertoCaboni that is, and compiling with `void *(*ptr)(void *, void*) = sumx;` with warnings gives: `warning: initialization from incompatible pointer type` – David Ranieri May 20 '20 at 08:10
  • @RobertoCaboni There *may* be a case for using such 'generic' pointers; for example, a number of `WinAPI` calls return a generic `FARPROC` function pointer, as I have pondered here: https://stackoverflow.com/q/57973305/10871073 (though that Q is about C++). – Adrian Mole May 20 '20 at 08:33
1

Why is there an error while declaring a generic pointer to a function?

Because there exist no such thing as a generic pointer to a function in the C language. void* is a generic pointer to an object (a variable). void* can only be used generically with object pointers, never with function pointers.

It is possible however to cast between one function pointer to another, but when you call the actual function, you must do so through the correct function pointer type.


You have various other mistakes regarding not allocating memory etc that are addressed by other answers. But lets ignore all of that, as well as the use of function pointers which doesn't really fill a purpose in this case. What you actually want to do can be achieved with modern standard C in the following manner:

#include <stdio.h>

int sum_int(int a, int b) {
  return a + b;
}

int sum_intp(const int *a, const int *b) {
  return *a + *b;
}

#define sum(a,b) ( (void)_Generic((b), int:0, int*:0),/*NOTE: comma operator*/ \
  _Generic((a),  \
  int:  sum_int, \
  int*: sum_intp)(a,b) )

int main(void) 
{
  int a=10;
  int b=20;
  printf("%d\n", sum(a, b));
  printf("%d\n", sum(&a, &b));
  //printf("%d\n", sum(a, &b)); //compiler error
  //printf("%d\n", sum((float)a, b)); //compiler error
  return 0;
}

The C11 _Generic macro determines types at compile-time. The pseudo code for the macro is:

  • Check if argument b is a valid type.
  • Discard the value from this argument b check - it's just there to generate a compiler error in case of wrong types.
  • Comma operator.
  • "Check if argument a is valid too, then call the correct function based on the type of a.

This is type safe and removes the need for void* or function pointers.

Lundin
  • 195,001
  • 40
  • 254
  • 396
  • can you please explain how `(void)_Generic((b), int:0, int*:0),` checks if argument b is a valid type? – David Ranieri May 20 '20 at 09:53
  • @DavidRanieri If the argument `b` is neither `int` nor `int*`, you'll get a compiler error along the lines of "not matching any type in the generic association list". Please note the lack of `default:`. If it matches, the expression returns a dummy `0` which is then discarded with `(void)`. I had to do it this way with the comma operator in order to guarantee type safety of both arguments. – Lundin May 20 '20 at 09:55
  • Oh, I see, then in abscence of a `default` it expands to `(void),` if none of those types are provided, isn't it? very nice trick!! – David Ranieri May 20 '20 at 09:57
  • 1
    @DavidRanieri Technically the left op of the comma operator is a void expression, but gcc/clang think that the left expression does nothing meaningful and warns accordingly. The (void) cast is strictly speaking not needed, it's just there to keep the compilers happy. – Lundin May 20 '20 at 09:59
1

Main Question

You cannot actually use functions in expressions. When a function is used in an expression, it is automatically converted to a pointer to the function.1

This means your cast should not attempt to convert to a function type like void *(void *, void *) but should convert to a pointer-to-a-function type like void *(*)(void *, void *).

Two Problems

C allows converting pointers-to-functions to different types of pointers-to-functions, but, when the function is called, the pointer used for the call should be obey certain rules about compatibility with the actual function definition. Changing parameter or return types from int * to void * does not obey these rules. So, with the types you have, (*ptr)(&a, &b) is not a proper call.

In sumx, p is not initialized before *p = *a + *b;, so it cannot be expected to point to anything. You should initialize it, possibly with p = malloc(sizeof *p);. After calling malloc, you should test the result. If it equals NULL, your program should print an error message and exit or otherwise handle the failure to allocate memory.

Footnote

1 There are two exceptions to this. One is when you take the address of a function manually with &, there is no automatic conversion. The other is there is no conversion when sizeof is applied to a function, but then there is a constraint violation. The C standard does not define the behavior of applying sizeof to a function.

Community
  • 1
  • 1
Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312