Download

Circular dependency again?

hello

trying to get components to talk to each other and cant work out how to avoid circular dependency.
ive searched and read about forward declarations but even that throws errors.
how do you do it?

here is my simplified setup

mypawn.h - main pawn class
componentA.h - first component class
componentB.h - second component class

mypawn needs to call functions on componentA and componentB
componentA* A;
componentB* B;

componentA needs to call functions on componentB
componentB* B;

componentB needs to call functions on componentA
componentA* A;

ive also read about having an independent class that holds all includes but it seems a silly over complicated way to handle it.

what do i include where to get it to work?

psudo or real code answers if you can please
thanks

Hi there,

well, you should provide some source code to look through to give you proper hints and tips you can try.

Normally a component belongs to some “owner” and though this owner could be used as the entry point when there is a need of communication to its components. The need of components talking to each other dont mean each component needs to
store a pointer to all components it wants to talk to. Inside a function of a component it could call the owner, ask this owner for a component we need to talk to and get back a pointer we can use inside our functions. Then you can call the functions you want without having a dependency.

But like i said, it would be the best you send some code :wink:

cant really see the need to show code, its along the lines of
A->DoSomething();

i also wanted to avoid the need for components to call on the owner and talk directly to each other to keep it more modular.
guess ill just have to send everything through the owner and ruin the modular aspect, oh well.

thanks for the answer

Use Forward Declaration in Header Files, then include the headers in the CPP files. That can often avoid the dependency issue, if the files have to include each other.

Forward Declaration only works if you’re NOT accessing the functions/variables of the class in that file, all it does is tell the compiler “at some point, this will exist, so trust me” - but the compiler will throw an error if you try to access it before you’ve told it what it is.

Having an overarching class that includes all headers can get around this and sometimes it’s the only way, but the biggest issue with it is that you end up with bloated binaries because every file is including every other header, so it’s not the ideal workaround.

thanks, so you are saying do this?
ComponentA.h


class componentB;

/...../

componentB* B;

ComponentA.cpp


#include "ComponentB.h"

/...../

but then there is:

so
B->DoSomething(); would not work?
i planned on filling those pointers from the pawn at begin play

maybe i need to rethink the whole design, i could get away with sending floats through the pawn class i guess.

But… yu are coding in the header file???

no of course not
so A->dosomething() will work in the .cpp file if i do as i posted above?

You can only do that if through the chain of #includes, the header has been included - or if the compiler just happens to compile things in the right order (which you have no control over).

I’m suggesting this:

Class A Header



class ClassB;

ClassB* MyVarA


Class B Header



class ClassA;

ClassA* MyVarB


Class A CPP



#include "ClassB.h"

ClassB->DoSomething();


Class B CPP



#include "ClassA.h"

ClassA->DoSomething();


Maybe a little beside the point, but is it really good design if component A calls stuff on component B and vice versa?

why would that be bad design? please enlighten me if there is a better way.

my components are designed to interact with and feed off each other, parts of a physics simulation. i want to keep the main pawn class out of the equation as much as possible, only providing the physics body, and the components (if they are attached) do their thing with that body.

however, circular dependency being a thing makes me want to reconsider my design anyway.
so thanks everyone :slight_smile:

There seem to be a lot of people willing to help you here, but you are not sharing enough. If you want proper help, and explanation. You should post the source code, or at the VERY least if you dont want to do that, post the header files, and explain what the functions should do, why and where they need access to the other class.

Then someone may be able to provide you with specific design improvements and show you what you are doing wrong. Most programming languages are just too vast and there are too many situations, so that it´s not always possible to give general advice that applies to every scenario.

Jamsh already explained how to deal with that.

there is nothing to share sorry.
ive been through the prototype stage and coded pretty much what i need but in 1 huge inflexible class. so the idea is to split it into smaller pieces in order to a)allow any number, or no ‘components’ to be added, and b) make the code easier to deal with.

i had no idea circular dependency existed and could possibly cause issues until it popped up on the compile error log.
because of this, and the very helpful answers on here, ive decided to completely redesign how everything works together.

cheers

edit:
and thanks for the actual code answer TheJamsh :slight_smile:

Just that my understanding of a component design is is mostly to achieve modular functionality that can be combined but from outside of the components, in your case I guess this would happen in the pawn. By making one component rely on another you are coupling them, at which point I would wonder why they need to be separate components in the first place. But yeah I don’t know what you’re doing, maybe you have perfectly valid reasons.

Components talking to other Components isn’t necessarily a bad design, but if they have to do it all the time, you may as well combine into one :slight_smile:

circular dependency (of components) is actually the exact thing im after. i didnt know it would cause problems for the compiler so i will tackle it another way.

as for combining them, a wheel for example, can function without an engine. if however an engine is present, the engine needs to know about the wheel and the wheel needs to know about the engine, they depend on each other for torque/angular velocity calculations. the only thing they need to know about the pawn they are attached to is its physics body and any player input. the pawn is not involved beyond that.

the thread topic has been answered very nicely by TheJamsh. if anyone else comes upon this issue they can now find the answer. im grateful for the knowledge but im going a different way with it and dont require any further help at this moment in time.
thank you all

Even though TheJamsh may have answered it, the fact that you are stating that you need to redesign it completely indicates that you might not have fully grasped the solution, and for that matter this may be helpful to other people reading this as well.

If you are programming in C++ and do not know about forward declarations you have some reading up to do. I´m sorry I didn´t quite catch onto the problem on my first post, because it´s just something very trivial. In fact you very often want to use forward declarations even if you don´t need to. The reason for this is simple that it will make the code generation faster. Your header files don´t include other header files everywhere, the relevant headerfiles are now only included in source where the class is actually needed, whilst the header just contains a few forward declarations. This gets particularily important in very large projects.

Consider 50 C++ classes in our project. All 50 of those have pointers to every other class, however they only ever access a member function of one other class in its source code.
Now if you make a change to one of the classes, if you had everything included in the header, your whole project would need to be recompiled.
If you use forward declarations, only two classes will need to be recompiled. The class itself, and the other class that accesses it.


Now that bit aside. The fact that you can use forward declarations means you wouldnt have to overhaul and redesign anything per se. It just worries me that you came to this conclusion, it might indicate you didn´t understand what it means. That´s all.