Avoiding Circular Dependencies of Header Files

Avoiding Circular Dependencies of header files

If you have circular dependency then you doing something wrong.

As for example:

foo.h
-----
class foo {
public:
bar b;
};

bar.h
-----
class bar {
public:
foo f;
};

Is illegal you probably want:

foo.h
-----
class bar; // forward declaration
class foo {
...
bar *b;
...
};

bar.h
-----
class foo; // forward declaration
class bar {
...
foo *f;
...
};

And this is ok.

General rules:

  1. Make sure each header can be included on its own.
  2. If you can use forward declarations use them!

How can I avoid circular dependencies in a header only library?

struct Sub
{
void g(Holder& h);
};

void Sub::g(Holder& h)
{
h.f();
}

Non-inline functions won't work well in header-only libraries, because the headers are typically included into more than one translation unit. You should use inline functions instead.



How can I avoid circular dependencies in a header only library?

You'll have to separate the definition of the functions from the definition of the class. I mean, they are in separate files already, but the header defining the class cannot include the function definitions. That allows breaking the dependency cycle.

This may be a matter of taste, but I also dislike "ipp" headers that don't work standalone.

Example:

detail/holder_class_only.h

#pragma once
#include "detail/sub_class_only.h"
struct Holder
{
inline void f(); // note inline
Sub s;
};

detail/sub_class_only.h

#pragma once
struct Holder;
struct Sub
{
inline void g(Holder& h); // note inline
};

detail/holder_functions.h

#pragma once
#include "detail/holder_class_only.h"
void Holder::f()
{
}
#include "detail/sub_functions.h"

detail/sub_functions.h

#pragma once
#include "detail/sub_class_only.h"
#include "holder.h"
void Sub::g(Holder& h)
{
h.f();
}

sub.h

#pragma once
#include "detail/sub_class_only.h"
#include "detail/sub_functions.h"

holder.h

#pragma once
#include "detail/holder_class_only.h"
#include "detail/holder_functions.h"

Note: Unstested, may contain bugs.

Circular dependency between C header files

The solution is to simply use some manner of program design. Each "object"/"module" in your program should consist of one h file and one c file. The h file is the public interface. Each such object should only be concerned with its own designated task. It should only include the resources needed to perform that task.

With such a design, there should never be any circular dependencies, or the design is flawed. You should not fix a bad design with various code tricks, you should re-do the design.

But of course the same resource could be included multiple time from different parts of the code. This is why we always use header guards in every single h file.

C++ Circular Dependency in Header Files

You could use this:

A.h

#include <B.h>
#ifndef A_H
#define A_H

class A
{
public:
B b1;
int m_a;
};

#endif // A_H

B.h

#ifndef B_H
#define B_H

class A;

class B
{
public:
int f(A &a);
};

#include <A.h>

inline int B::f(A &a)
{
return a.m_a;
}

#endif // B_H

main.cpp

#include <iostream>
#include <A.h> // these could be in any order
#include <B.h>

int main()
{
A a;
B b;

std::cout << "Calling b.f(a): " << b.f(a) << std::endl;

return 0;
}

Fixing circular dependencies c++17 headers

The usual fix is simple:

struct Xchart; // declares Xchart; definition is elsewhere.
short WINAPI MyFunction(Xchart *mychart); // Function declaration.

Only toolkit.cpp will need the definition of Xchart, but .cpp files themselves are not included elsewhere and don't contribute to circular inclusions.

How to avoid circular dependency in this project?

If those are the only dependent functions, you could declare and initialize in the following order:

class Game;

class Enemy {
public:
void turn(Game* game);
};

class Game {
public:
std::vector<Enemy> enemies;
};

Live example: https://ideone.com/g6Vmwi

Resolve build errors due to circular dependency amongst classes

The way to think about this is to "think like a compiler".

Imagine you are writing a compiler. And you see code like this.

// file: A.h
class A {
B _b;
};

// file: B.h
class B {
A _a;
};

// file main.cc
#include "A.h"
#include "B.h"
int main(...) {
A a;
}

When you are compiling the .cc file (remember that the .cc and not the .h is the unit of compilation), you need to allocate space for object A. So, well, how much space then? Enough to store B! What's the size of B then? Enough to store A! Oops.

Clearly a circular reference that you must break.

You can break it by allowing the compiler to instead reserve as much space as it knows about upfront - pointers and references, for example, will always be 32 or 64 bits (depending on the architecture) and so if you replaced (either one) by a pointer or reference, things would be great. Let's say we replace in A:

// file: A.h
class A {
// both these are fine, so are various const versions of the same.
B& _b_ref;
B* _b_ptr;
};

Now things are better. Somewhat. main() still says:

// file: main.cc
#include "A.h" // <-- Houston, we have a problem

#include, for all extents and purposes (if you take the preprocessor out) just copies the file into the .cc. So really, the .cc looks like:

// file: partially_pre_processed_main.cc
class A {
B& _b_ref;
B* _b_ptr;
};
#include "B.h"
int main (...) {
A a;
}

You can see why the compiler can't deal with this - it has no idea what B is - it has never even seen the symbol before.

So let's tell the compiler about B. This is known as a forward declaration, and is discussed further in this answer.

// main.cc
class B;
#include "A.h"
#include "B.h"
int main (...) {
A a;
}

This works. It is not great. But at this point you should have an understanding of the circular reference problem and what we did to "fix" it, albeit the fix is bad.

The reason this fix is bad is because the next person to #include "A.h" will have to declare B before they can use it and will get a terrible #include error. So let's move the declaration into A.h itself.

// file: A.h
class B;
class A {
B* _b; // or any of the other variants.
};

And in B.h, at this point, you can just #include "A.h" directly.

// file: B.h
#include "A.h"
class B {
// note that this is cool because the compiler knows by this time
// how much space A will need.
A _a;
}

HTH.

C++ is creating a header to solve circular dependency a good idea?

What you have is fine. The naming suffix I've usually seen is _fwd.h/_fwd.hpp/_fwd.hxx or whatever variant of .h you like. You can see this in Boost, for example: optional_fwd.hpp.

(Of course reducing circular dependencies is a good goal, but some things are mutually-recursive in nature, like grammars, so you have no choice.)

How can I resolve this mutual dependency between header files?

I suppose you could argue that I'm addressing the software-design aspect here, but I don't know how to do this without slightly refactoring your code. Namely, by avoiding the typedef temporarily. (Although I would advise dumping the typedef's permanently.) For your particular case, table.h doesn't need to know what ObjectString is, since it only uses a pointer to it. So you can simply not import "object.h" in table.h, and instead write:

object.h:

#ifndef s_object_h
#define s_object_h

#include "common.h"
#include "table.h"

typedef enum {
OBJECT_STRING
} ObjectType;

typedef struct {
ObjectType type;
Table attributes;
} Object;

struct ObjectString {
Object base;
char* chars;
int length;
};

typedef struct ObjectString ObjectString;

bool stringsEqual(ObjectString* a, ObjectString* b);

#endif

table.h:

#ifndef s_table_h
#define s_table_h

#include "value.h"

typedef struct {
struct ObjectString* key;
Value value;
} Entry;

typedef struct {
int capacity;
int count;
Entry* entries;
} Table;

void initTable(Table* table);
void setTable(Table* table, struct ObjectString* key, Value value);
bool getTable(Table* table, struct ObjectString* key, Value* out);

#endif


Related Topics



Leave a reply



Submit