Reputation: 16724
I have a header B
which contains types that A
header uses and in B
there's types which A
uses. So, if I include B
in A
to use B's
type compilation is fine. But if I do include of A
in B
I would have a circular depedence, but how all are using include guards
it doesn't happen but I get an error in B
header when try to use A's
types: they aren't know by compiler, resulting in undefined identifier
compiler error. I hope this is clear.
EDIT:
The types in question are struct
and ones created with typedef
e.g.,
A file:
struct foo {
struct baa *b; // <- this struct is defined in B file
};
B file:
struct baa {
//etc..
};
void f(struct foo* f); // this struct is defined in A
Upvotes: 0
Views: 141
Reputation: 754710
If you're not using C11, you have to make sure that typedef's only appear once. The way around that problem with minimal impact to code is to use the struct tag
notation for all structure types. This is something that the Linux Kernel Coding Style Guide demands.
You also need to pass (most) structures by pointer.
So, you might have:
#ifndef FILEA_H_INCLUDED
#define FILEA_H_INCLUDED
struct tag_from_fileA;
struct tag_from_fileB;
extern int alternative_compound(struct tag_from_fileA *arg1, struct tag_from_fileB *arg2);
#endif
#ifndef FILEB_H_INCLUDED
#define FILEB_H_INCLUDED
struct tag_from_fileA;
struct tag_from_fileB;
extern int compound_function(struct tag_from_fileA *arg1, struct tag_from_fileB *arg2);
#endif
Now, each of these files can be used in isolation, or jointly. In the implementation code, you might have:
#include "fileA.h"
#include "fileB.h"
struct tag_from_fileA { int x; double y; char z[32]; };
int alternative_compound(struct tag_from_fileA *arg1, struct tag_from_fileB *arg2)
{
...code here can access internals of arg1...
...but it cannot access the internals of arg2...
}
#include "fileB.h"
#include "fileA.h"
struct tag_from_fileB { char *str1; char *str2; char *str3; };
int compound_function(struct tag_from_fileA *arg1, struct tag_from_fileB *arg2)
{
...code here can access internals of arg2...
...but it cannot access the internals of arg1...
}
Now, if either fileA.c
or fileB.c
must access the internals of the structure defined in the other file, then you can place the definition of the structure in the headers. Indeed, you can do that for both structures. You then just have to ensure that fileA.h
has #include "fileB.h"
at the top (after the header guards, but before the internals of the structure type is needed), and that fileB.h
has #include "fileA.h"
at the top (as before).
This way, a client file (fileC.c
) can include either fileA.h
or fileB.h
(or both) and get all the definitions needed.
If the functions do not need actual structures (so you never pass struct tag_from_fileA value
, but only struct tag_from_fileA *ptr
), and the client files never need to create an instance of the structure types (they call 'constructors' that return a pointer to the structure type), and the client files never need to poke inside the structure (you have 'accessor' functions to do that job, and I mean functions, not macros, or inline functions), then you don't formally need the structure definitions in the header. You have opaque types. These are good; they help isolate the code from changes in the details of the structure. If the source files know about the internals of the structure, they have to be recompiled when the structure changes.
typedef
If you insist on using typedef
(and I definitely prefer it, though I recognize the wisdom of the Linux Kernel team), then with C89/90 and C99, you have to ensure that any individual typedef
only appears once. It is only when you have two mutually incestuous headers like the ones above that you really run into trouble. You can use macro hackery:
#ifndef TYPEDEF_TAG_FROM_FILEA
#define TYPEDEF_TAG_FROM_FILEA
typedef struct tag_from_fileA tag_from_fileA;
#endif
and include that in both headers (and the corresponding alternative for the other struct tag). Or place in a third header that is included from each of fileA.h
and fileB.h
(and used whenever the typedef
s are needed). If you enforce the rule that typedef struct tag tag;
is the only form you allow (rather than ever allowing typedef struct sometag SomethingUnrelated;
, you can code prototypes using the struct tag
notation but write the code using it with the tag
notation. It all gets a little convoluted, though.
C11 allows you to repeat typedef
s; C99 does not.
ISO/IEC 9899:1999 §6.7 Declarations
¶3 If an identifier has no linkage, there shall be no more than one declaration of the identifier (in a declarator or type specifier) with the same scope and in the same name space, except for tags as specified in 6.7.2.3.
ISO/IEC 9899:2011 §6.7 Declarations
¶3 If an identifier has no linkage, there shall be no more than one declaration of the identifier (in a declarator or type specifier) with the same scope and in the same name space, except that:
- a typedef name may be redefined to denote the same type as it currently does, provided that type is not a variably modified type;
- tags may be redeclared as specified in 6.7.2.3.
So, if you can reliably use C11 syntax, you can simply write the typedef
s as you need them. You still have to make sure there's only one definition of the structure body itself, though.
You can find information about headers and how to write them, and shared type definitions, in numerous other questions, including:
#include
in headers?extern
, but how?Upvotes: 1
Reputation: 14708
Solution depends on how you are using type A and B -- best bet is to use forward declarations.
Your example you describe in text where you create a field of type A in B, and then also uses B as a field in A is unsolvable -- you will need to use pointers like the example below
The example you actually gives as code, you just need to include B.h before you declare "foo" -- it is probably just your include guards which are gone wrong
For a pointer example;
A.h
struct B; // forward declaration
struct A {
struct B* my_b;
};
B.h:
struct A; // forward declaration
struct B {
struct A* my_a;
};
Upvotes: 2