C Structure typedef with Forward Declarations

c

Solution

Remember that a `typedef` is just an alternative name for a type. There's a reason the Linux kernel doesn't use `typedef`s for structure types, and you're demonstrating it.

There are a couple of ways around your problem. I note that C11 does allow multiple occurrences of the same `typedef`, but I'm assuming you're stuck with an older compiler that does not support that.

TL;DR

Even though the Linux kernel doesn't use `typedef`s, I usually do use `typedef`s, but I mostly avoid mutually referential structure types (I can't think of any code where I used such a type). And, like Jens Gustedt notes in his answer, I almost invariably use the notations:

typedef struct SomeTag SomeTag;

so that the type name and the structure tag are the same (they're in different namespaces). This operation is not necessary in C++; when you define `struct SomeTag` or `class SomeTag`, the name `SomeTag` becomes a type name without the need for an explicit `typedef` (though a `typedef` does no harm other than revealing that the author is more experienced in C than C++, or the code originated as C code).

I also observe that names starting with an underscore are best treated as 'reserved for the implementation'. The rules are a little more complex than that, but you run a risk of the implementation usurping your names — and being within its rights to usurp your names — when you use names starting with an underscore, so don't. Likewise, POSIX reserves type names ending `_t` for the implementation if you include any POSIX headers (such as `<stdio.h>` when you aren't compiling in strict Standard C only mode). Avoid creating such names; they'll hurt you sooner or later. (I've not fixed the code below to deal with either of these issues: caveat emptor!).

In the code fragments below, I'm mostly ignoring the code that prevents multiple inclusions (but you should have it in your code).

Extra header

typedefs.h:

typedef struct Object Object_t;
typedef struct Klass  Klass_t;

klass.h

#include "typedefs.h"

struct Klass
{
    void (*_initialize)(Object_t *object, Klass_t *klass);
};

object.h

#include "typedefs.h"

struct Object
{
    Klass_t *_klass;
};

This works because the two type names `Klass_t` and `Object_t` are declared before they're used.

Use `struct Object` in prototype

klass.h

typedef struct Klass Klass_t;
struct Object;

struct Klass
{
    void (*_initialize)(struct Object *object, Klass_t *klass);
};

Or, for consistency, it might even use:

    void (*_initialize)(struct Object *object, struct Klass *klass);

object.h

#include "klass.h"

struct Object
{
    Klass_t *_klass;
};

This works because (within broad limits — basically, if the types are defined at file scope, not inside a function) `struct Object` always refers to the same type, regardless of whether all the details are fully defined yet.

GCC 4.8.2

Under all of `-std=c89`, `-std=c99` and `-std=c11`, GCC 4.8.2 accepts replicated `typedef`s, as in the code below. It requires `-std=c89 -pedantic` or `-std=c99 -pedantic` to get errors about the repeated `typedef`s.

Even without the `-pedantic` option, GCC 4.5.2 rejects this code; however, GCC 4.6.0 and later versions accept it without the `-pedantic` option.

klass.h

#ifndef KLASS_H_INCLUDED
#define KLASS_H_INCLUDED

typedef struct Klass Klass_t;
typedef struct Object Object_t;

struct Klass
{
    void (*_initialize)(Object_t *object, Klass_t *klass);
};

#endif /* KLASS_H_INCLUDED */

object.h

#ifndef OBJECT_H_INCLUDED
#define OBJECT_H_INCLUDED

typedef struct Klass Klass_t;
typedef struct Object Object_t;

struct Object
{
    Klass_t *klass;
};

#endif /* OBJECT_H_INCLUDED */

consumer.c

#include "klass.h"
#include "object.h"

Klass_t k;
Object_t o;

You'll have to decide whether that's a risk you're willing to take for your code — how important is portability, and to which versions of C (and which C compilers) must it be portable.

Problem

I was using typedef for structures everywhere in my application. I then started to refactor into several header files when it started to get clunky. I noticed I needed to forward declare Object, and Klass. Well, to my surprise, I couldn't forward declare Object, or Klass. This is because, as you can see in the Object and Klass structure, I'm using a typedef of Object and Klass. ``` //Klass.h typedef struct Klass Klass_t; struct Klass { void (*_initialize)(Object_t* object, Klass_t* klass); }; //Object.h typedef struct Object Object_t; struct Object { Klass_t* _klass; }; ``` At first, using typedef was great. But trying to forward declare Object: ``` struct Object_t; ``` Doesn't work as I would need to rewrite by function declarations as: ``` void (*_initialize)(struct Object_t* object, Klass_t* klass); ``` So I decided to just typedef Object inside the Klass.h header file: ``` typedef struct Object Object_t; ``` Well when all header files are included into my Main.c file, it complians: ``` Object.h:5: error: redefinition of typedef 'Object_t' ``` So, I then decided to just drop all struct typedefs and explicity declare my structures. Is there a way to typedef a structure and forward declare in another file without explicitly using struct Object? I want to keep structure typedefs inside the header file where the structure is declared. If I have to group all typedefs inside one header file then I would rather not use typedef at all. Anyways, thanks for your time.

Original source