How to #Include When There Is a Circular Dependency

Proper way to #include when there is a circular dependency?

You need to forward declare the definitions you need. So if A uses B as a parameter value, you need to forward declare B, and vice versa.

It could be that just forward declaring the class names:

 class A;
class B;

solves your problems.

The accepted answer to this question provides some additional guidance.

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.

What is a circular dependency and how can I solve it?

What is a dependency?

In order to understand what circular dependency is, it is better to understand what is a dependency and what it means to the compiler.

Let's say you have a project and, in a class, you have the following defined:

Public Class MyClass
'Some code here
Private MyString As String
'Some code there
End Class

When compiling your project, the compiler runs into the String class, which is defined in a DLL file called System. It will then link that DLL to your project, so at run-time, when defining or doing operation on the string, the System.dll will be loaded to perform those.

Now, let's say you have, further in your class, the following definition

'Some code here
Private MyObjet as CustomClass1
'Some code there

And let's say CustomClass1 is defined in another project of yours, named Project2.DLL:

Public Class CustomClass1
'Your custom class code
End Class

So when compiling your first project, the compiler will run into CustomClass1 definition, it knows it lays into Project2.dll and therefore will compile Project2 before, in order to be able to add that reference in your first project.

That's what a dependency is, it's hierarchical, there must be a starting point. Even the String class is dependant on other classes, and at the end, they all rely on bytes or bits to do the job, because that's the only thing a computer can do, play with 1 and 0.

So the circular part

So if you have, in Project2, a reference (a field definition, or something like that) that link to your first project, what happens?

  • The compiler reads your first project, then runs into CustomClass1
  • Then it tries to compile Project2, since CustomClass1 is defined there
  • Then it runs to a class defined in your first project
  • It tries to compile your first project in order to link it to the second
  • Then it runs to CustomClass1
  • Then it tried to compile Project2
  • I guess you got it...

So at some point the compiler displays an error, saying it cannot compile, as it doesn't understand what you're trying to do...

Yes, computers are that stupid.

How to solve it ?

Solving these kind of issue is sometimes difficult, but the basic idea is to build up a hierarchical structure, put the base class (those which don't need dependencies) together, then build up on them.

Take all the classes that depend on each other and put them together, they form a layer for something you try to do in your application.

How to solve the circular dependency

You can use Injector for this. Inject it via constructor as usual, and then when you will need some service that leads to the circular dependency, get that service from it.

class HttpService {
constructor(private injector: Injector) { }

doSomething() {
const auth = this.injector.get(AuthService);
// use auth as usual
}
}

How to resolve circular dependencies when using go modules and cgo

If you look at the verbose output from the go build command, you will see that when compiling the directory as a complete go package, the main.c file is being included as part of the C code used in hello.go.

From the documentation:

When the Go tool sees that one or more Go files use the special import "C", it will look for other non-Go files in the directory and compile them as part of the Go package

The easiest solution here is to separate the main C and Go packages, so that they don't interfere with each other's build process. Testing this out, removing the main.c file will build libchello.a and libgohello.a, and then adding it back in will complete the build of main.

C circular dependency

Seems like you shouldn't need to include anything in any of the files. A forward declaration of the relevant types should be sufficient:

#ifndef MapTest_vertex_h
#define MapTest_vertex_h

struct edgelist;

typedef struct
{
char* name;
float x, y;
edgelist* edges; // C++ only - not C
} vertex;

#endif

etc. In C coding, you have to write:

struct edgelist;

typedef struct
{
char* name;
float x, y;
struct edgelist* edges;
} vertex;


Related Topics



Leave a reply



Submit