Why do I need to avoid "casting a function pointer to a generic pointer" in this case?

Viewed 70

assume that I have a struct that has a function pointer as a member:

struct Class
{
    void (*function)();
}

I then want to assign a function to this member. I wanted to do this:

typedef void (*func_pointer)();
func_pointer method = va_arg(arg_list, func_pointer);
struct Class foo;

since the type of both foo.function and method are void (*)() (a "generic" function pointer) I assume that I can just equate them together, something like foo.function = method.

However, the book I'm reading mentioned that ANSI C "does not let us" cast between "void *" and a "function pointer". (Object Oriented Programming with ANSI C, chapter 6, page 64) Therefore they did something like this:

*(func_ptr*) &foo.function = method;

Why though? why is it necessary? I do not see any void * pointer here. Why can't I just copy the value of method to foo.function?

Since the code in the book is fairly involved I tried to simplify it but that may have changed the context, here's the real code: (full code is in a git repo if you wanted to take a look, file Object.c.)

struct Class
{
    const struct Object _;
    const char * name;
    const struct Class * super;
    size_t size;
    void * (*ctor)(const void * self, va_list * arg_ptr);
    void * (*dtor)(const void * self);
    int (*differ)(const void * self, const void * other);
    int (*puto)(const void * self, FILE * file_ptr);
};
{
        typedef void (*voidf) (); /* generic function pointer */
        voidf selector;
        va_list ap = *app;
        while ((selector = va_arg(ap, voidf)))
        {
            voidf method = va_arg(ap, voidf);
            if (selector == (voidf) ctor)
                *(voidf *) &self — > ctor = method;
            else if (selector == (voidf) dtor)
                *(voidf *) &self — > dtor = method;
            else if (selector == (voidf) differ)
                *(voidf *) &self — > differ = method;
            else if (selector == (voidf) puto)
                *(voidf *) &self — > puto = method;
        }
        return self;
    }

In this case my thinking would be that it is sufficient to just do a self->ctor = method and call it a day.

2 Answers

There's no problem with your code, and there's also no problem with that passage in the book. The "problem" is that the passage doesn't apply to your code.

the book I'm reading mentioned that casting between "void *" and a "function pointer" is illegal.

This is true. See this question/answer.

But in your code you don't have a void*. foo.function is a void(*)() (a pointer to a function taking any number of arguments and returning void), which is the same type as func_pointer. So there's no need to do any sort of casting. foo.function = method; is correct.

Edit

From the clarifications you've added to your question, it seems like the book is saying something along the lines of: "With a void* we'd be able to avoid a lot of ugly casts between types, but since ANSI C doesn't allow void* to hold function pointers we can't avoid these casts."

Simplifying one of the examples from the book:

struct Class {
    void * (*ctor)(const void * self, va_list * arg_ptr);
};
struct Class self;

typedef void (*voidf)();
voidf method = ...;

*(voidf*)&self.ctor = method;

That cast is necessary because self.ctor is a void* (*)(const void*, va_list*), not a void(*)().

If you could use a void* here, you could instead do:

void *method = ...;
self.ctor = method;

Without doing any casts.

I would not hide any pointers behind typedefs (even function pointers). It makes code difficult to read and error-prone.

typedef void func();

struct Class
{
    func *fptr;
};

struct Class bar(int x, ...)
{
    va_list va;
    func *fp;
    struct Class c;

    va_start(va, x);
    fp = va_arg(va, func *);
    c.fptr = fp;

    return c;
}

You do not need any casts.

Related