views:

247

answers:

7

I roughly understand the rules with what #include does with the C preprocessor, but I don't understand it completely. Right now, I have two header files, Move.h and Board.h that both typedef their respective type (Move and Board). In both header files, I need to reference the type defined in the other header file.

Right now I have #include "Move.h" in Board.h and #include "Board.h" in Move.h. When I compile though, gcc flips out and gives me a long (what looks like infinite recursive) error message flipping between Move.h and Board.h.

How do I include these files so that I'm not recursively including indefinitely? Thanks.

+14  A: 

You need to look into forward declarations, you have created an infinite loops of includes, forward declarations are the proper solution.

Here's an example:

Move.h

#ifndef MOVE_H_
#define MOVE_H_

struct board; /* forward declaration */
struct move {
    struct board *m_board; /* note it's a pointer so the compiler doesn't 
                            * need the full definition of struct board yet... 
                            * make sure you set it to something!*/
};
#endif

Board.h

#ifndef BOARD_H_
#define BOARD_H_

#include "Move.h"
struct board {
    struct move m_move; /* one of the two can be a full definition */
};
#endif

main.c

#include "Board.h"
int main() { ... }

Note: whenever you create a "Board", you will need to do something like this (there are a few ways, here's an example):

struct board *b = malloc(sizeof(struct board));
b->m_move.m_board = b; /* make the move's board point 
                        * to the board it's associated with */
Evan Teran
this looks like a very good solution, unfortunately I could not get it to work (I need to look into #ifndef and how it works more to fully understand this). Thanks for the answer though.
twolfe18
When you say "didn't work", what do you mean? Also, do your Move and Board types reference each other, or are the types used in the two header files but not necessarily in the struct definitions? (Shameless plug: Also see my answer for some pointers.)
Alok
yes, please specify the way it "didn't work", this code (minus the main function) should work pretty much as-is.
Evan Teran
Neither my Move or Board are composed of each other, I just need the type defined for some methods that take both a Board and a Move. When I say "it didn't work", it gave me some weird error about something methods being previously defined elsewhere. I tried a few modifications on what you proposed, but I couldn't get any of them to work. Not to be a drag, but I'm happy with the solution I have now which is to have the typedefs in a separate header file (Greg D's idea).
twolfe18
A: 

You need to have one of them first. Make a forward decl in one of them and have that one for for example

    #ifndef move
    struct move;
    #endif

could be part of the board.h file.

and

    #ifndef board
    struct board;
    #endif

could be part of the move.h file

then you could add them in either order.

edit As was pointed out in the comments... I was assuming the use of the typedef construct as follows for the board struct

   typedef struct {…} board;

since I've never seen anyone using structs in C without a typedef I made this assumption... maybe things have changed since the last time I coded in C (yikies.... it was like 15 years ago)

Hogan
you can't test if a struct exists using the pre-processor. If you are implying he should make a macro named move as well, you should make that clear.
Evan Teran
@Evan: good point, see my edit above :D
Hogan
you can't test for typedefs with the pre-processor either... you would need to literally add something like `#define board` somewhere.
Evan Teran
hmm.. microsoft's old C compiler used to allow it, geeze I wonder if I could even find a reference to that. Well, no matter -- many others gave good answers.
Hogan
+2  A: 

Like so:

//Board.h
#ifndef BOARD_H
#define BOARD_H
strunct move_t; //forward declaration
typedef struct move_t Move;
//...
#endif //BOARD_H

//Move.h
#ifndef MOVE_H
#define MOVE_H
#include "Move.h"
typedef struct board_t Board;
//...
#endif //MOVE_H

This way Board.h can be compiled without dependency on move.h and you can include board.h from move.h to make its content available there.

Igor Zevaka
+3  A: 

Include guards would be part of the solution to this issue.

Example from wikipedia:

#ifndef GRANDFATHER_H
#define GRANDFATHER_H

struct foo {
    int member;
};

#endif

http://en.wikipedia.org/wiki/Include_guard

The other part as noted by several others is forward referencing. (http://en.wikipedia.org/wiki/Forward_Reference)

You can partially declare one of the structures above the other one like so:

#ifndef GRANDFATHER_H
#define GRANDFATHER_H

struct bar;
struct foo {
    int member;
};

#endif
Steve Strickland
A: 

From K&R The C Programming Language (p 91 "Conditional Inclusion" in my copy), with some tweaks for you:

#if !defined (BOARD_H)
#define BOARD_H

/* contents of board.h go here */

#endif

and the same for Move.h

In this way, once a header has been included once, it will not be included again, as the 'BOARD_H' name has already been defined for the preprocessor.

Nij
macros starting with a double underscore are reserved, you shouldn't use them. Additionally, an underscore followed by a capital letter is also reserved.
Evan Teran
@Evan - corrected the above thank you for the comment. I thought my edit-comment would appear above but apparently not!
Nij
+2  A: 
Alok
A: 

Circular dependencies are a pain in the ass and should be eliminated wherever feasible. In addition to the forward declaration suggestions given so far (Alok's is the best example), I'd like to throw another suggestion into the works: break the mutual dependency between Board and Move by introducing a third type (call it BoardMoveAssoc for illustration; I'm sure you can come up with a less sucky name):

#ifndef H_BOARD_MOVE_ASSOC
#define H_BOARD_MOVE_ASSOC

#include "Move.h"
#include "Board.h"

struct BoardMoveAssoc {
    Move m;
    Board b;
};

...
#endif

Under this scheme, Board and Move don't have to know anything about each other; any associations between the two are managed by the BoardMoveAssoc type. The exact structure will depend on how Move and Board are supposed to be related; e.g., if multiple moves are mapped to a single board, the structure may look more like

 struct BoardMoveAssoc {
     Move m[NUM_MOVES] // or Move *m;
     Board b;
 };

This way, you don't have to worry about forward declarations or incomplete types. You are introducing a third type into the mix, but I believe this will be easier to understand and maintain.

John Bode