1

I'm experimenting with generic-like code and I have a function like this (a lot of not relevant code removed):

typedef uint8_t (*struct_converter_t)(void *, char *);

void convert_struct(
        struct_converter_t converter, // this is a function
        const char * file_name
){
    some_struct_t * some_struct;
    converter(some_struct, some_string_buffer);
}

And when I try to assign a function that takes some_struct_t (not void *):

static uint8_t some_converter(some_struct_t * vd, char * s);

to my struct_converter_t like this:

struct_converter_t converter = some_converter;    // WARNING HERE

I'm getting this:

initialization of 'struct_converter_t' {aka 'unsigned char (*)(void *, char *)'} from incompatible pointer type 'uint8_t (*)(some_struct_t *, char *)' {aka 'unsigned char (*)(struct <anonymous> *, char *)'} [-Wincompatible-pointer-types]

I'm not experienced in C and I would like to know if there is a way to get rid of this warning elegantly.

Kamil
  • 13,363
  • 24
  • 88
  • 183
  • I think it's just telling you that the callback is expecting a void* type but you're passing in a struct type.. By the way are you compiling this as C or C++? Because I believe in C++ you're forced to explicitly cast but in C the compiler should implicitly do it for you – Irelia Feb 19 '22 at 21:11
  • 1
    How is `convert_struct` being called? How are the parameters being passed in declared? Please update your question with a [mcve] that contains all relevant details. – dbush Feb 19 '22 at 21:21
  • Well, this code is actually it. I have added compiler and machine information. I't "doesnt like" that I assign pointer of a function that accepts `struct *` as parameter into type that is a function pointer that accepts `void *`. – Kamil Feb 19 '22 at 21:29
  • 1
    The warning message seems not to appear in the code that you posted but in the line where you call the function "`convert_struct`". The warning message typically comes with a line number. Please check which line the warning message applies to. – Martin Rosenau Feb 19 '22 at 21:33
  • @Yuumi how can I explicitly cast pointer assignment? – Kamil Feb 19 '22 at 21:33
  • @MartinRosenau The warning is about the line where I assign function pointer, not where I call the function. – Kamil Feb 19 '22 at 21:35
  • @Kamil try `(void*)some_struct` to cast – Irelia Feb 19 '22 at 22:19
  • Sorry about missing code and pasted image. Now I fixed everything and I'm wondering about better title for this question. – Kamil Feb 20 '22 at 22:03

2 Answers2

4

The function that you're assigning to the function pointer type has parameters that are incompatible with the function pointer.

Your function takes a some_struct_t * as the first parameter but the function pointer type takes a void * as the first parameter. While any object pointer can be converted to/from a void *, that does not extend to function pointer parameters.

You need to change your function to take a void * for its first parameter to be compatible with the function pointer. Then inside the function you can convert that void * parameter to a some_struct_t *.

dbush
  • 205,898
  • 23
  • 218
  • 273
  • "that does not extend to function pointer parameters" - thanks, that explains everything. I have changed function that is being assigned to my defined function pointer. Inside I simply cast void * to some_struct_t *. – Kamil Feb 19 '22 at 21:57
1

In C, you can supply an argument of type X* to a function expecting a void*. But that's as far as the conversion goes. You cannot take a function whose first parameter is an X* and use it as the argument for a parameter which is a function whose first parameter is a void*.

The reason is that C does not guarantee that X* and void* have the same representation. It does guarantee that the compiler knows how to convert an X* into a void* in a way that does not destroy information, so that it can be later converted back to an X*. But the void* might look quite different.

So the compiler can insert code which changes the X* to a void*. But how does it change a char*(*)(X*) (a function whose parameter is an X*) to a char*(*)(void*)? If the function is expecting that arg has type char*(*)(void*), then it will mostly likely call arg(v) where v has type void*. What then happens if arg actually expects an X*?

In order for the compiler to allow that possibility, it would have to somehow wrap arg in what's usually called a trampoline; a function which accepts an X* and converts it into a void* in order to call a different function. That's not so easy -- for one thing, it would have to store the trampoline somewhere -- so C refuses to do it as an automatic conversion.

rici
  • 234,347
  • 28
  • 237
  • 341
  • `C refuses to do it` You mean doing it automatically by compiler, right? – Kamil Feb 19 '22 at 21:52
  • @Kamil: right. You'd have to do it yourself. Which is not a bad idea, since when you try to do it you will see how tricky it is to do in a general way (I added a couple of words to clarify what I meant.) – rici Feb 19 '22 at 21:54
  • nitpicking: the C standard guarantees that `char*` and `void*` have the same representation. Though casting `void(*)(char*)` to `void(*)(void*)` still requires a cast and it is technically an UB. – tstanisl May 04 '22 at 10:45
  • @tstanisi: the peculiar exception for `char*` really isn't relevant here, I don't think. In the context of the OP, `X` is `some_struct_t`, and inserting verbiage to attempt to explain the exception would be pedantic obscurantism. I guess I could have just changed `X` to `some_struct_t`, but I didn't, for various reasons. I did take the opportunity to fix the markdown typo, so thanks. – rici May 04 '22 at 13:43