A dilemna with circular reference of headers file

J

JCB

Hi,

I have two C file which are referencing in a circular way.

To simplify I use an example with car :

Suppose I have a program for cars.

I have car.c, structure car and functions prototypes of car.c are in a
file car.h
I have also engine.c, a structure engine and functions prototypes of
engin.c are in a file engine.h.


Now the structure car in car.c use an engine structure, so I have to
write in car.c :

#include car.h
#include engine.h

.... (code)



Note :in car.h
struct car {
struct engine engine ;
etc...
} ;





Up to there, it's standard and there is no problem.


The problem is if I have to write a function in engine.c which
reference a car, in this case, I will have to write in engine.c :

#include engine.h
#include car.h

The problem is that engine depends on car and car depends on engine,
so either I write
(case 1)
#include engine.h
#include car.h
or
(case 2)
#include car.h
#include engine.h

it's not appropriated :

case 1
Function of car.h use the structure engine.h, so it's good, but
engine.h use structure car in car.h, not yet defined (because #include
car.h is after #include engine.h)


case 2:
Functions of engine.h that use structure car it's good, because
defined before, but in car.h , I need a reference to struct engine,
not yet defined...



Some people kwow what to do in this particular case ?
 
A

Arthur J. O'Dwyer

car.h:
#ifndef __INCLUDED_CAR_H__
#define __INCLUDED_CAR_H__

Constraint violation. I used to be lax about this
case myself, until repeated admonitions in c.l.c gradually
changed my style. Now, I write instead:

#ifndef H_CAR
#define H_CAR

Shorter to type, and portable to all ISO C compilers.
Everyone wins.

Also N.B.: I have found that some circular references are
not fully defused until one has put all type declarations
above the other headers. I forget in what circumstances this
bit me, but now I also write:

engine.h:
#ifndef H_ENGINE
#define H_ENGINE

typedef struct engine Engine;

#include "car.h"

struct engine
{
Car *my_owner;
};

#endif

car.h:
#ifndef H_CAR
#define H_CAR

typedef struct car Car;

#include "engine.h"

struct car
{
Engine *my_engine;
};

#endif


-Arthur
 
E

Emmanuel Delahaye

In 'comp.lang.c' said:
#ifndef __INCLUDED_CAR_H__
#define __INCLUDED_CAR_H__

Please stop invading the implementation name space (_[A-Z_]xx identifiers
are reserved). The idiomatic form is:

#ifndef H_INCLUDED_CAR
#define H_INCLUDED_CAR
 
A

Arthur J. O'Dwyer

[stupid octothorpe quoting fixed]
If car and engine are so incestuous that they need to know each other's
structures, you should move them into one file. They are tightly coupled
and the program organisation should reflect that.

IMHO, not if the concept of "engine" is distinct from the concept of "car"
(which in this trivial example it is). A car "has-a" engine, but so do
other things, and maybe someday you'll want to change your "engine" class
to work with "lawnmowers", without having to weed out all the intermixed
declarations and definitions of "car"-related methods.

Of course, any beginner's textbook would tell you to use inheritance from
an "Engine-Powered Vehicle" class in the first place. But I don't listen
to that kind of textbook. ;-)

My advice: Keep the separate files. Just put them in the same directory.
IMHO.
You can couple them more loosely by using pointers to incomplete
structures and functions.
car.h
#ifndef car_h
#define car_h
#include "engine.h"
typedef struct car car;
t functionname(engine *...);
t functionname(...);
...
#endif

Ahh. *This* is where the problem I mentioned earlier bites people.
Look at what happens when this file is preprocessed:
#ifndef car_h
#define car_h
#include "engine.h"
"car_h" is not defined, so "car_h" gets defined.
Then we #include "engine.h" as follows:
> #ifndef engine_h
> #define engine_h
> #include "car.h"
"engine_h" is not defined, so "engine_h" gets defined.
Then we #include "car.h" as follows:
> #ifndef car_h
"car_h" is defined, so we skip to the corresponding
....and return to processing "engine.h".
> typedef struct engine engine;

Now we have a typedef for "engine". Good.
> t functionname(car *...);

Now we have a function taking a "car *". Bad.
The identifier "car" has not been declared to mean
anything anywhere. The compiler chokes.

The solution to this problem is left as an exercise
for Derk. (I explained it, albeit less lucidly, earlier
in the thread.)

-Arthur
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,744
Messages
2,569,481
Members
44,900
Latest member
Nell636132

Latest Threads

Top