2

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.

stanle
  • 307
  • 1
  • 9
  • 1
    You're right, that doesn't make any sense. What's the book, page number, etc? Can you include more context? – Nate Eldredge Nov 13 '21 at 00:02
  • 1
    *casting between "void *" and a "function pointer" is illegal.* this is true, but you don't have a `void*` in your code. You have 2 `void (*)()`. – Kevin Nov 13 '21 at 00:05
  • @NateEldredge Thanks, I've included the book and page number in a new edit. – stanle Nov 13 '21 at 00:19
  • @stanle: That new code does not compile and has too many errors to correct reasonably. – Eric Postpischil Nov 13 '21 at 00:33
  • @EricPostpischil here's a [git repo](https://github.com/shichao-an/ooc/tree/master/06) of the full code (file Object.c) . I've compiled and ran it successfully. Just wanted to know why it was done that way. – stanle Nov 13 '21 at 00:35
  • There is no "generic function pointer" - `void (*)()` is pointer to function taking unspecified arguments and returning `void` – M.M Nov 13 '21 at 00:39
  • 1
    @M.M I know that, which is why I put a quote-on-quote "generic", just using `void (*) ()` as a representative to them all. I think it works because all function pointers types can be freely casted to one another, as long as you don't call them under the wrong cast. – stanle Nov 13 '21 at 00:43
  • See also [Why can't I cast a function pointer to (void *)?](https://stackoverflow.com/q/36645660/3422102) – David C. Rankin Nov 13 '21 at 03:13

2 Answers2

3

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.

Kevin
  • 6,993
  • 1
  • 15
  • 24
  • But there are no data pointers in the question. – 0___________ Nov 13 '21 at 00:11
  • 2. Do not link the answers (which can be wrong) only cite the C standard – 0___________ Nov 13 '21 at 00:12
  • @0___________ I know, that's exactly what I'm saying: *But in your code you don't have a `void*`* – Kevin Nov 13 '21 at 00:12
  • It is not true that casting between `void *` and a pointer to a function type is illegal. The C standard omits any definition of the behavior, but it does not prohibit the conversion. The constraints of the cast operator (C 2018 6.5.4 2-4) allow it. – Eric Postpischil Nov 13 '21 at 00:18
  • Thanks @Kevin, I've updated the question with the full link to the book and some code excerpts, let me know if you still believe that I'm correct in this case! – stanle Nov 13 '21 at 00:30
  • @EricPostpischil I've updated the question with the actual quote from the book to clarify that. – stanle Nov 13 '21 at 00:33
1

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.

0___________
  • 60,014
  • 4
  • 34
  • 74