2

I have a question related to void pointer conversions. Instead of casting between void* and non-void pointers, my question is about casting between function pointer types, one of which has void* as parameter type and another has a pointer to some particular data type.

Here's the code which allows to reproduce the warning messages:

#include <stdio.h>

typedef void (*module_outputMessage)(void *param, const char *msg);

void module_function(module_outputMessage outputFunc, void *output_param, int msgid, const char *msg1, const char *msg2)
{
    if (msgid == 0)
        outputFunc(output_param, msg1);
    else
        outputFunc(output_param, msg2);
}



struct main_state
{
    int msgid;
};

void main_outputMessage(struct main_state *state, const char *str)
{
    printf("Message %d: %s\n", state->msgid, str);
    state->msgid++;
}

int main(int argc, char *argv[])
{
    struct main_state state;
    const char *msg1 = "abc", *msg2 = "def";

    state.msgid = 0;

    module_function(&main_outputMessage, &state, 0, msg1, msg2);
    module_function(&main_outputMessage, &state, 0, msg1, msg2);
    module_function(&main_outputMessage, &state, 1, msg1, msg2);
    module_function(&main_outputMessage, &state, 0, msg1, msg2);
    module_function(&main_outputMessage, &state, 1, msg1, msg2);

    return 0;
}

This is it, the program is made of two parts, main and module. module outputs the text, but it shouldn't deal with all output specifics - instead, main is the one to handle the output. Since main is dependent on module and not vice versa, module does not know what's going on in main, and to output the messages, it needs an output function to be passed as a parameter. In order for output to know which state object it's dealing with, that object needs to be passed along with the output function. And that's where conversion comes into play: module doesn't know and shouldn't care about the implementation of main, so instead of using struct main_state* as function parameter, it accepts void* which it merely passes to output function.

So it all boils down to conversion between these types:

void (*)(void*              , const char*)
void (*)(struct main_state *, const char*)

The program gives the expected results:

Message 0: abc
Message 1: abc
Message 2: def
Message 3: abc
Message 4: def

However, GCC complains about incompatible pointer types (I get five messages like this, one for each function call):

funcpointvoid.c: In function ‘main’:
funcpointvoid.c:33:2: warning: passing argument 1 of ‘module_function’ from incompatible pointer type
  module_function(&main_outputMessage, &state, 0, msg1, msg2);
  ^
funcpointvoid.c:5:6: note: expected ‘module_outputMessage’ but argument is of type ‘void (*)(struct main_state *, const char *)’
 void module_function(module_outputMessage outputFunc, void *output_param, int msgid, const char *msg1, const char *msg2)
      ^

So even though it works fine for me, with these warnings I'm not sure if this 'architecture' can be relied upon. But as I see it myself, the only difference is pointers to void and non-void, and it's just one way to use generic pointers for whatever purpose they exist. Is this a bug of GCC or have I missed something?

3
  • 3
    Maybe the simplest way would be to change main_outputMessage prototype to void (void *, const char*) and then cast the void* param to struct main_state*. Commented Jul 15, 2014 at 11:58
  • 2
    A much shorter code that will reproduce the same warnings/errors, passing a int (*pfn)(Type const*, Type const*) to the standard library function qsort for sorting a sequence of Type. I've yet to meet an engineer that has, on initial use of learning that api, not run in to this same condition. Commented Jul 15, 2014 at 12:07
  • @Holt that isn't just the simplest way to address this, it is the correct way to do so.+1. Commented Jul 15, 2014 at 17:06

1 Answer 1

4
void (*)(void*              , const char*)
void (*)(struct main_state *, const char*)

are two different types and as there are no implicit conversion between function pointer types you need to make the conversion explicit by using a cast:

Change:

module_function(&main_outputMessage, &state, 0, msg1, msg2);

to

module_function((module_outputMessage) main_outputMessage,
    &state, 0, msg1, msg2);

but be aware that the function call outputFunc technically invoke undefined behavior as void (*)(void* , const char*) and void (*)(struct main_state *, const char*) are not compatible types.

Sign up to request clarification or add additional context in comments.

5 Comments

I'm not sure this work on architecture where pointers to different types have not the same size? The C standard does not specify anything about but it's not a common case.
@Holt in any case it is undefined behavior as void * and struct main_state are not compatible types even if there is an implicit conversion between struct main_state * and void *.
Well, I think I get something, but not everything. If, as @Holt suggested, struct main_state* is converted to void*, passed around using function calls and then converted back to struct main_state*, and no conversions between different function pointers are done, then there's no undefined behavior, is there?
@CrystalMV the undefined behavior comes from the outputFunc function call as C says (c99, 6.3.2.3p8) "If a converted pointer is used to call a function whose type is not compatible with the pointed-to type, the behavior is undefined." and the two types are not compatible (even if void * and struct main_state * are "assignment compatible").
@CrystalMV: main() converts a struct main_state to void * and passes it to module_function(). module_function() passes the void * on to main_outputMessage() (through outputFunc()). So far, so good. The problem is that main_outputMessage() actually believes that it has been passed a struct main_state. There is no conversion back from void *.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.