0

I am trying to define a struct type (call it thing) that has a function pointer in it (call it bar), and I want that function to take an argument that is a pointer to an object of type thing.

I know from here that I can either do a forward declaration or declare the struct type in the argument list of the function pointer as shown here:

// In header

typedef struct thing thing_type;

typedef struct
{
    int a;
    int b;

    double (*bar)(thing_type *t)
} thing;

OR

// In header
typedef struct
{
    int a;
    int b;

    double (*bar)(struct thing *t)
} thing;

But when I go to initialize these values, I always get an incompatible pointer type warning.

double bar1(thing *t);
double bar2(thing *t);

// In main()

thing foo = {1, 2, bar1}; // WARNING

initialization of ‘double (*)(struct thing *)’ from incompatible pointer type ‘double (*)(thing *)’ [-Wincompatible-pointer-types]

foo.bar = bar2; // WARNING

passing argument 1 of ‘foo->bar’ from incompatible pointer type [-Wincompatible-pointer-types]

My code works the way I intend it to as far as I can tell through testing, but I would like to understand if I am doing anything "wrong" here or if there is a way to do what I want without throwing warnings for every assignment.

Note that thing foo = {1, 2, &bar1 }; (passing the address of a function pointer) makes no difference for this warning.

M Tut
  • 69
  • 7
  • 2
    `double (*bar)(struct thing *t)` Neither of your examples has a structure called `struct thing`. They have a type called `thing` but that is not the same as `struct thing`. – kaylum Oct 03 '22 at 04:55
  • OK, @kaylum but if I define the functions as with `struct thing` argument types and try to actually use those structs in the function, like `double bar1(struct thing *t) = { return (double)( t->a + t->b) }` then I get an error: `pointer to incomplete class type "struct thing" is not allowed`. I guess I should say that the function bar needs to be able to access attributes of the struct. – M Tut Oct 03 '22 at 05:04
  • 1
    You don't have a complete `struct thing` type anywhere. That's the point. You can't dereference a type that you have not completely defined. If you aren't convinced about that then please tell us which lines of code define `struct thing`. – kaylum Oct 03 '22 at 05:08
  • @kaylum, Is `struct thing` not defined in `typedef struct { int a; int b; double (*bar)(struct thing *t) } thing;` ? – M Tut Oct 03 '22 at 05:11
  • 2
    No it isn't. That is an unnamed struct that is typedefed to thing. – Avi Berger Oct 03 '22 at 05:13
  • 2
    Nope. That defines a type called `thing` which is not the same as `struct thing`. Try: `typedef stuct thing { .. } thing;` – kaylum Oct 03 '22 at 05:13
  • 1
    In C, struct tags are in a separate namespace. – Avi Berger Oct 03 '22 at 05:16
  • Thanks guys, I knew I was missing something pretty fundamental. Glad I asked! – M Tut Oct 03 '22 at 05:16
  • I wish people first learned to use structs without typedefs, because *this is always possible*. If typedefs are used to "type less", I consider this reason to be largely invalid. The two valid reasons I would accept are: 1) hiding types in an API, and 2) reducing complexity with function pointers. – Cheatah Oct 03 '22 at 05:18

1 Answers1

1

In this code:

typedef struct thing thing_type;

typedef struct
{
    int a;
    int b;

    double (*bar)(thing_type *t)
} thing;

the first typedef defines thing_type to be an alias for struct thing, and the second typedef defines thing to be an alias for a structure with no tag. This second alias, thing, has no relationship with the first alias, thing_type, or with its structure type, struct thing. In particular, there is no connection between the two types thing and struct thing.

Similarly, in this code:

typedef struct
{
    int a;
    int b;

    double (*bar)(struct thing *t)
} thing;

the typedef defines thing to be an alias for a structure with no tag, and bar is declared to be a pointer to a function with a parameter type of struct thing *. Again, there is no connection between the two types thing and struct thing.

To refer to the same structure type in multiple places, you must use a tag for it. This is as simple as:

// Use the structure tag while defining the structure:
typedef struct MyTag
{
    int a;
    int b;
    double (*bar)(struct MyTag *);
} MyAlias;

or:

// Define an alias first, then the structure definition can use the alias:
typedef struct MyTag MyAlias;
struct MyTag
{
    int a;
    int b;
    double (*bar)(MyAlias *);
};
Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312