How can I identify the source of a cyclic dependency in C++
Can anyone tell me how I can identify which clas开发者_StackOverflow社区ses are causing the cyclic dependency issues that I am seeing?
The typical problem with a circular dependency occurs in such a situation:
// foo.h
#ifndef FOO_H
#define FOO_H
#include "bar.h"
struct Foo { void use(Bar); };
#endif // FOO_H
// bar.h
#ifndef BAR_H
#define BAR_H
#include "foo.h"
struct Bar { void use(Foo); };
#endif // BAR_H
// bar.cpp
#include "bar.h"
What happens when the preprocessor works on bar.cpp
is the following:
// bar.cpp
// #include "bar.h"
// BAR_H is not defined
// #define BAR_H
// #include "foo.h"
// FOO_H is not defined
// #define FOO_H
// #include "bar.h"
// BAR_H is defined, skip
// #endinclude "bar.h"
struct Foo { void use(Bar); }; // ERROR: Bar is not defined
// #endinclude "foo.h"
struct Bar { void use(Foo); }; // OK: Foo is defined
// #endinclude "bar.h"
This could, potentially, be detected using preprocessor tricks, to have a nice message at compile. However it is slightly complicated and adds to the include guards cruft that is already necessary... so most of the times, you just try to compile, and if it does not, and you cannot make sense of the errors, you can always get the preprocessed file using a compiler switch (-E on gcc).
Now, to get into the preprocessor tricks. The issue as we can see is that even though BAR_H
is defined, the file has not been included, yet.
The solution is to prepare another guard, to signal the fact that the whole file has been processed, and emit an error if ever we are in a situation when only one of the two guards is defined.
It looks like so:
// bar.h
#ifdef BAR_H
#ifndef BAR_H_2
# error "Cyclic inclusion involving bar.h"
#endif // BAR_H_2
#else
#include "foo.h"
struct Bar { void use(Foo); };
#define BAR_H_2
#endif // BAR_H
As you can see though, it doubles the cruft already necessary for header guards, for a relatively low frequency error. So it is usually not deemed worthy.
- Forward declare classes and structures in your headers
- Remove includes from your header files
Perform these two steps on all your headers to identify circular dependencies.
guard them like so:
#ifndef HEADER_FILE_IDENTIFIER
#define HEADER_FILE_IDENTIFIER
/* ...declarations... */
#else
#error "multiple inclusion"
#endif /* HEADER_FILE_IDENTIFIER */
(where HEADER_FILE_IDENTIFIER
is obviously a unique identifier)
精彩评论