1

for example i have these 3 functions and function pointers:

char *(*f)(char, int);
char *(*g(char, int));
char **h(char, int);

If we translate them to English we get:

  1. declare f as pointer to function (char, int) returning pointer to char

  2. declare g as function (char, int) returning pointer to pointer to char

  3. declare h as function (char, int) returning pointer to pointer to char

Type 2. and 3. are equal. Can we check this, maybe like this:

if(g==h)
{
    do something;
}

in c?

Also does c allow something like g=h? (if yes what it actually does?)

  • 5
    These are just pointers, so yes you can compare them with `==` like any other variable. If they both point to the same function they'll be equal. And yes, you can also assign them, which again is simply assigning a pointer to equal another pointer. – Verpous May 03 '23 at 12:42
  • Do you want to know if they point to the same function or if both have the same type? The former one is easy, just us a `==`. – 12431234123412341234123 May 03 '23 at 13:03
  • well if you know they point to the same function doesn't that mean that they will must have the same type? – Perfectionist May 03 '23 at 13:05
  • @Perfectionist You can convert a pointers to function of one type to a pointer to a function of a different type so then you would have pointers to functions of different types pointing to the same function. If the pointer resulting from the conversion is converted back to the original type it will compare equal to the original pointer. Pointers to functions of different types can only be compared by explicitly converting them to a common type. Calling a function through a pointer to a function type not compatible with the function results in UB. – Ian Abbott May 03 '23 at 13:39
  • 1
    You cannot do `g=h;` because `g` is not an lvalue. If you define a pointer `char **(*e)(char, int);` then you can do `e=g;` or `e=h;` because `e` is an lvalue of type compatible with pointers to the functions `g` and `h`. – Ian Abbott May 03 '23 at 13:54
  • @IanAbbott if e was this time: **(*e)(char,float); would still be possible to do e=g; ? – Perfectionist May 03 '23 at 14:04
  • @Perfectionist Do you want to check if the funcitons are the same type? – chux - Reinstate Monica May 03 '23 at 14:14
  • Yes, `char **(*e)(char, float) = g;` is possible but it is not valid because it is a constraint violation, and that is likely to produce a warning from the compiler by default (same as for `f = g;` in dbush's [answer](https://stackoverflow.com/a/76164011/5264491)). The function call `e('a',42.0);` would result in *undefined behavior*. – Ian Abbott May 03 '23 at 14:21
  • @chux-ReinstateMonica I wanted to see if two functions are equal,like the same returning type and they take the exactly same arguments in this example (char,int). However as the guy below answered the if (g==h) compares their addresses which leads to always false.Correct me if i understood it wrong – Perfectionist May 03 '23 at 14:23
  • 1
    @Perfectionist You can not compare types in actual code. Because the type has to be known during compile time. I question the use case for this, can you explain a use case? Maybe generic macros could help to determine the type. – 12431234123412341234123 May 03 '23 at 14:26
  • @IanAbbott so this way g=e and h=e if i do if(g==h) this will return true right? – Perfectionist May 03 '23 at 14:28
  • @12431234123412341234123 I was curious to see what are you limits in c nothing else.I hate macros but yeah i can agree with that – Perfectionist May 03 '23 at 14:34
  • "Equal" suggests the equality operators, but equality operators operate on *values* not types. There is no standard way to compare the types themselves for equality, but some compilers provide special built-in functions to compare two types (e.g. GCC's `__builtin_types_compatible_p` function), and the `typeof` operator (which will be standard in the not-yet-published C23) which can convert a value to its type. – Ian Abbott May 03 '23 at 14:38
  • Please rephrase your question and title to make it a bit more clear. You don't want to compare if 2 functions are equal (that would be very hard) but if 2 functions or 2 function pointers have the same type. – 12431234123412341234123 May 03 '23 at 14:46
  • `g=e;` and `h=e;` are constraint violations because `g` and `h` are not lvalues, and there is no sensible way to handle that situation other than reporting an error. It's similar to doing `int a; 2 = a;`. No sensible way to handle that other than reporting an error. – Ian Abbott May 03 '23 at 14:48
  • @IanAbbott oh sorry i though it would work like that if the point to the same function but apparently it doesn't.Anyways thanks for everything so far! – Perfectionist May 03 '23 at 14:55
  • 1
    `g` and `h` are *converted* to pointers to the functions they designate, but the converted pointers are not lvalues (and neither are the functions they point to because all lvalues have *object types*, not *function types*). – Ian Abbott May 03 '23 at 14:57
  • 1
    @12431234123412341234123 by equal i mean: Same returning type and same arguments.Easier example would be void hello(); void test();. The have same returning type(void) and same arguments(void again). – Perfectionist May 03 '23 at 15:00
  • 1
    Using GCC's builtins, `__builtin_types_compatible_p(typeof(g), typeof(h))` will be 1 because `g` and `h` have compatible types. In this expression, `g` and `h` are *not* converted to pointers. So `__builtin_types_compatible_p(typeof(g), char **(*)(char, int))` will be 0 and `__builtin_types_compatible_p(typeof(g), char **(char, int))` will be 1. – Ian Abbott May 03 '23 at 15:16
  • Actually. in C (but not C++). `void hello();` and `void test();` declare functions with no information about the number or types of parameters, not functions with no arguments. So `void hello();` is compatible with `void test(int, double, char *);` for example. – Ian Abbott May 03 '23 at 15:38
  • 1
    However, although `void hello();` is compatible with `void test(int, double, char *);`, it is not compatible with `void test2(short, double, char *);` or with `void test3(int, float, char *);` due to default argument promotions. This confusion is all going away in C23. In the C23 standard, `void hello();` will be equivalent to `void hello(void);` to match C++. (This will probably break a lot of ancient code!) – Ian Abbott May 03 '23 at 15:50
  • Does this answer your question? [Reflection support in C](https://stackoverflow.com/questions/1353022/reflection-support-in-c) – Andreas is moving to Codidact May 03 '23 at 22:19

2 Answers2

6

A comparison like this is valid:

if(g==h)

Because a function name is automatically converted to a function pointer when used in an expression, and it is allowed to compare any two function pointers for equality. In this case it will evaluate to false because g and h are two different functions, so their addresses will always be different.

You can't however do either of these:

f = g;
f = h;

Because f is not compatible with &g or &h. Attempting to do so will likely generate a warning from your compiler:

x1.c:10:7: warning: assignment from incompatible pointer type [enabled by default]
     f = g;
       ^

If you changed the type to this:

char **(*f)(char, int);

Then they would be compatible, and you could do something like this:

f = g;
if (f==g) {   // true
    printf("f is g\n");
}

Also, g=h is not allowed because function names are not modifiable lvalues, which essentially means they can't be changed.

dbush
  • 205,898
  • 23
  • 218
  • 273
  • 2
    I think he wants to know how to compare the type of a function pointer rather then if they point to the same function. – 12431234123412341234123 May 03 '23 at 13:01
  • I see now it makes sense .Also i didn't know about the automatically converted to a function pointer when used in an expression so thank you! – Perfectionist May 03 '23 at 13:02
  • 1
    Note that since `g` and `h` are both functions — not just function pointers — the comparison `if (g == h)` is semantically valid but tautologously false: two separate functions have separate addresses. Also, you can repeat variations on the declaration for `g` in a single TU with different numbers of superfluous parentheses: `extern char (*(*g(char, int))); extern char *(*g(char, int)); extern char **g(char, int);` — all three declare the same function. The `extern` is not necessary but emphasizes that these are declarations of the function, not function pointers. – Jonathan Leffler May 03 '23 at 13:37
  • 1
    "function names are not *modifiable lvalues*" and not even *lvalues* at all for that matter! – Ian Abbott May 03 '23 at 14:00
5

To test types during compile time, you can use a macro with _Generic. I made a prove of concept:

#include <stdio.h>

#define GETTYPE(fp) _Generic                 \
  (                                          \
    (fp)                                     \
    , char   *(*)(char,  int)         :  1   \
    , int    *(*)(char,  int)         :  2   \
    , int    *(*)(char*, int)         :  3   \
    , char  **(*)(char,  int)         :  4   \
  )

extern char  *a(char,int);
extern int   *b(char,int);
extern int   *c(char*,int);
extern char **d(char,int);

extern char  *a1(char,int);

int main(int argc, char **argv)
  {
    (void)argc;
    (void)argv;
    printf("type a %i\n",GETTYPE(a));
    printf("type b %i\n",GETTYPE(b));
    printf("type c %i\n",GETTYPE(c));
    printf("type d %i\n",GETTYPE(d));

    printf("type a==a1? %i\n",GETTYPE(a)==GETTYPE(a1));
    printf("type b==a1? %i\n",GETTYPE(b)==GETTYPE(a1));
    printf("type c==a1? %i\n",GETTYPE(c)==GETTYPE(a1));
    printf("type d==a1? %i\n",GETTYPE(d)==GETTYPE(a1));
    return 0;
  }

This GETTYPE macro converts each listed type to a value. This value can then be used to store the type and compare it. A better version would use a enum to define the value, but you can do that yourself.

The macro has to be invoked while you still hold the original function pointer (or with the actual function that decays into a function pointer). Why this is should be obvious, but this limits its use cases. If you want to keep the information about the type, you need to assign each type a number and store it along a generic function pointer. When you need the function pointer, you can cast it back to the original type based on the type number. This requires a lot of handwork, since you need to handle every needed type individually.

  • Yeah this a way but much complicated but i respect your effort thanks. First time i have seen a macros like that so the (fp) and the "," is something like switch but for macros? or _Generic does this? – Perfectionist May 03 '23 at 15:10
  • The language a little unclear. `_Generic` is a keyword, not a macro, but perhaps you are referring to the macro that is using `_Generic`? – Ian Abbott May 03 '23 at 15:23
  • 1
    @Perfectionist The `,` is just a `,` maybe you are confusied because i placed it at the beginning of the line (i did it so it easy to add and remove new entries, by just copy+edit a line or delete a line, without thinking about the `,`). You can read about `_Generic` here https://en.cppreference.com/w/c/language/generic – 12431234123412341234123 May 03 '23 at 15:25
  • 2
    @Perfectionist This _is_ the way to compare functions types. If too complicated, then you are out of luck. – chux - Reinstate Monica May 03 '23 at 16:51
  • It isn't THAT complicated, you need the macro only once, just add every type that is needed. Also a switch case based on the type value, and casting it back to the original type, should be needed that often. I use a very similar macro but for data types, this is used in my `printf()`-like functions where the data type can be given as an argument (converted to a integer, with such a macro). It is very easy to use in the caller. – 12431234123412341234123 May 04 '23 at 08:42
  • @12431234123412341234123 your idea is good and etc but i wanted an answer like the first one like if by default c allows you to compare types of function and if yes how. – Perfectionist May 04 '23 at 11:53
  • @Perfectionist This is just the way it is in C, and yes, you can use it in a `if` statement. If you think it is too hard you could choose a different language or come up with a better idea, i don't have a better idea. – 12431234123412341234123 May 04 '23 at 13:45
  • @Perfectionist Not very nice for you how you treat people who trying to help you. – 12431234123412341234123 May 05 '23 at 09:09