tags:

views:

991

answers:

2

Is there a compiler directive in order to ignore the "initialization from incompatible pointer type" warnings in Hardware_MouseDrivers_GPM_Methods and Hardware_MouseDrivers_DevInput_Methods? Turning off warnings globally is not an option though.

#include <stdio.h>

/* Mouse driver interface */

typedef struct _Hardware_MouseDriver {
        int (*open)(void*, char *);
        int (*close)(void*);
        int (*poll)(void*);
} Hardware_MouseDriver;

/* GPM */

typedef struct _Hardware_MouseDrivers_GPM {
        char *path;
} Hardware_MouseDrivers_GPM;

static int Hardware_MouseDrivers_GPM_Open(Hardware_MouseDrivers_GPM *this, char *path);
static int Hardware_MouseDrivers_GPM_Close(Hardware_MouseDrivers_GPM *this);
static int Hardware_MouseDrivers_GPM_Poll(Hardware_MouseDrivers_GPM *this);

static int Hardware_MouseDrivers_GPM_Open(Hardware_MouseDrivers_GPM *this, char *path) {
        printf("GPM: Opening %s...\n", path);
        this->path = path;
}

static int Hardware_MouseDrivers_GPM_Close(Hardware_MouseDrivers_GPM *this) {
        printf("GPM: Closing %s...\n", this->path);
}

static int Hardware_MouseDrivers_GPM_Poll(Hardware_MouseDrivers_GPM *this) {
        printf("GPM: Polling %s...\n", this->path);
}

Hardware_MouseDriver Hardware_MouseDrivers_GPM_Methods = {
        .open  = Hardware_MouseDrivers_GPM_Open,
        .close = Hardware_MouseDrivers_GPM_Close,
        .poll  = Hardware_MouseDrivers_GPM_Poll
};

/* DevInput */

typedef struct _Hardware_MouseDrivers_DevInput {
        char *path;
} Hardware_MouseDrivers_DevInput;

static int Hardware_MouseDrivers_DevInput_Open(Hardware_MouseDrivers_DevInput *this, char *path);
static int Hardware_MouseDrivers_DevInput_Close(Hardware_MouseDrivers_DevInput *this);
static int Hardware_MouseDrivers_DevInput_Poll(Hardware_MouseDrivers_DevInput *this);

static int Hardware_MouseDrivers_DevInput_Open(Hardware_MouseDrivers_DevInput *this, char *path) {
        printf("DevInput: Opening %s...\n", path);
        this->path = path;
}

static int Hardware_MouseDrivers_DevInput_Close(Hardware_MouseDrivers_DevInput *this) {
        printf("DevInput: Closing %s...\n", this->path);
}

static int Hardware_MouseDrivers_DevInput_Poll(Hardware_MouseDrivers_DevInput *this) {
        printf("DevInput: Polling %s...\n", this->path);
}

Hardware_MouseDriver Hardware_MouseDrivers_DevInput_Methods = {
        .open  = Hardware_MouseDrivers_DevInput_Open,
        .close = Hardware_MouseDrivers_DevInput_Close,
        .poll  = Hardware_MouseDrivers_DevInput_Poll
};

/* Test drivers */

void TestDriver(Hardware_MouseDriver driver, void *data) {
        /* Access the driver using a generic interface
         * (Hardware_MouseDriver) */
        driver.poll(data);
}

void main() {
        Hardware_MouseDrivers_GPM gpm;
        Hardware_MouseDrivers_DevInput devinput;

        Hardware_MouseDrivers_GPM_Open(&gpm, "/dev/gpmctl");
        Hardware_MouseDrivers_DevInput_Open(&devinput, "/dev/input/mice");

        TestDriver(Hardware_MouseDrivers_GPM_Methods, &gpm);
        TestDriver(Hardware_MouseDrivers_DevInput_Methods, &devinput);

        Hardware_MouseDrivers_GPM_Close(&gpm);
        Hardware_MouseDrivers_DevInput_Close(&devinput);
}
+1  A: 

I guess the obvious answer to this is the question "why not fix the code to use the right pointer type"?

EDIT:

OK, I can understand that you don't want to complicate the code unnecessarily, but I don't think it's that much of a complication, or even an unneccessary one.

Let's look at the field open in the struct Hardware_MouseDriver, which is supposed to be a pointer to a function that takes a pointer to void as its first argument.

To initialize this field, you use a pointer to the function Hardware_MouseDrivers_GPM_Open, and at another place a pointer to the function Hardware_MouseDrivers_DevInput_Open. None of these take a pointer to void as their first argument, and this is of course what the compiler warns about.

Now, if a void pointer is the same size as these pointers, and there are no other surprising differences between how they are stored and handled, calls to these functions through the open pointer will work as expected. It is likely that it will, and I guess that with this type of low-level code it is unlikely that someone will port it to TOPS-20 or something. But there is no guarantee that it will work, and it looks (to me) strange. (And to the compiler, obviously!)

So my suggestion would be to change code like this:

static int Hardware_MouseDrivers_GPM_Open(Hardware_MouseDrivers_GPM *this, char *path) {
    printf("GPM: Opening %s...\n", path);
    this->path = path;
}

to the just slightly more complicated:

static int Hardware_MouseDrivers_GPM_Open(void *arg1, char *path) {
    Hardware_MouseDrivers_GPM *this = arg1;
    printf("GPM: Opening %s...\n", path);
    this->path = path;
}

I think this change would be easier and less complicated than (1) turning off the warnings, (2) documenting it so readers can understand why that warning isn't supposed to be important here, (3) documenting it some more so your readers actually believe that you know what you are doing, and (4) handling the problems that will occur if someone actually does port your code to TOPS-20.

Thomas Padron-McCarthy
Hm, are not all pointers of the same size? I do understand the reasons why you prefer your approach but there is no added value through the additional cast. I guess, the assembler code would not even differ. It is just a message the compiler prints in order to warn you that there *might* be an unintentional error in your code. The question is whether the added code, whose only purpose is to satisfy the compiler, is really worth the effort. I do not think so.
@Timn: Well, it's like that with warnings. Either you get them or turn them off. And if you leave them on, you will have to add some kind of code either way (either C code or compiler pragmas) to tell the compiler that in this place you know what you're doing.Either you bang the type into place with the assignment (like in my solution) or you give the compiler the right function type (Thomas' solution) and bang the pointer type into your desired form later.One way or another there will be additional code.
Nicholaz
@Timn: Yes, on most (all?) systems today, all pointers are of the same size, and on such a system the assembler will probably be the same. But it is not guaranteed by the standard, so next year or so someone at Intel might decide that splitting the address space into segments with different-sized pointers will make it more efficient, and then void pointers might be 64 bits while your struct pointers might be 32 bits. And your code will break. You will then have a hard time finding the problem, since you've either hidden it with explicit casts, or turned off the warnings... :)
Thomas Padron-McCarthy
+2  A: 

Cast the assignments to the proper types (function pointers with void * rather than your instance pointer):

 .open= (int (*)(void*, char *))Hardware_MouseDrivers_GPM_Open;

Or make a type and use it in the definition and initialization of the struct:

typedef int (*openfcnt_t)(void*, char *);

typedef struct _Hardware_MouseDriver {
        openfnct_t open;
} Hardware_MouseDriver;

and then

 .open= (openfnct_t)Hardware_MouseDrivers_GPM_Open;


EDIT:

Upon further thought the easiest and least fiddly way for a C program will be:

 .open= (void *)Hardware_MouseDrivers_GPM_Open;
Nicholaz
That is what I had in mind as well. However, introducing a typedef for each function makes the code much larger without providing any real benefits. In comparison to (int (*)(void*, char *)), the openfcnt_t cast is shorter and less error-prone but both solutions still require to add lots of extra code unnecessarily. They are not exactly what I was looking for. Are not there any compiler directives like #ifdef, #define etc. to turn off all pointer-type-related warnings not globally but temporarily for a certain code block?
Maybe this one will help: http://stackoverflow.com/questions/965093/selectively-disable-gcc-warnings-for-only-part-of-a-translation-unit
Nicholaz
I don't think this is such a good idea. The function that you actually call will not match the prototype used in the call, which can be a dangerous thing to do. The cast here hides that possible problem even better than turning off the warnings, and a possible problem isn't something you _want_ to hide.
Thomas Padron-McCarthy
@Thomas: A type cast always means 'the types appear to be non-matching but I know what I'm doing', so your comment would apply to basically every kind of cast. Warnings are just that ... a warning ... and the cast is your way of telling the compiler that it's okay anyway.
Nicholaz
@Nicholaz: Agreed about what type casts mean here. But, on the other hand, you need to be really sure that you actually _do_ know what you are doing, and if someone ports this to some architecture with different-size pointers (near! far!) then perhaps you no longer know... So, why not do it the _right_ way, the way that perhaps not God but at least the ANSI committee intended!
Thomas Padron-McCarthy
Another comment: From what I remember from reading the standard, void* is guaranteed to be able to hold a pointer to any data object, but there is no guarantee that it can hold function pointers.
Thomas Padron-McCarthy