Alex
Alex

Reputation: 6933

Templated Class has a Circular Dependency

I have two classes. One is a management class, which stores a bunch of worker classes. The workers are actually a templated class.

#include "worker.h"    
class Management {

     private: 
      worker<type1> worker1;
      worker<type2> worker2;
      ...

};

The problem arises due to the fact that the templated classes needs to use Management.

Example:

class Worker{
   ...
};

#include "Worker.inl"

The Worker inl file:

#include "Management.h"  //Circular Dependency!

Worker::Worker(){
    //Management is accessed here
    Management mgmt1();
    mgmt1.doSomething(); //Can't use that forward declaration!
}
...

Normally you would forward declare Management.h in the Worker header file, and call it a day. Sadly, since the class is templated, it is always going to get included.

I guess you can claim that the design is bad, since a templated class shouldn't be templated if it needs to know this sort of information, but it is what it is, and I have to work with it.

You can also view this question as a microcosm of office life.

Upvotes: 3

Views: 312

Answers (5)

user396672
user396672

Reputation: 3166

if you actually need to access Management from worker class you can add Management as additional template parameter to worker. I.e. something like this:

template <class mngmnt, int type >
class worker{
  mngmnt* p_mngmnt;  
public:
  char* getManagementName(){return p_mngmnt->management_name;}
};


class Management {

public:
 char* management_name;

 private: 
   worker<Management,1> worker1;
   worker<Management,2> worker2;

};

Upvotes: 0

tenfour
tenfour

Reputation: 36896

One way to solve this with clean syntax & no runtime overhead is to use an interface. Basically, all the operations that Worker needs to perform on Management, put into some interface like this:

interface IManagementOps
{
public:
    void DoStuff() = 0;
};

class Worker
{
    IManagementOps* pOps;
    ...     
};

class Management : public IManagementOps
{
    ...
};

Please be careful that your design is good though and that it's actually appropriate for Worker to be doing things with Management.

Upvotes: 1

Billy ONeal
Billy ONeal

Reputation: 106530

I don't see how templates change this. Forward declare to remove circular dependencies in headers.

EDIT: Sorry, I reversed this initially. The second example is correct though (see CodePad)

template class worker;

class Managment
{
    worker<type1> a;
    worker<type2> b;
};

template<typename T> class worker
{
///... okay to use class Managment here.
};

#include <iostream>

class type1 {};
class type2 {};

class Managment;

template<typename T> struct worker
{
    inline void doSomethingElse();
};

class Managment
{
    worker<type1> a;
    worker<type2> b;
public:
    inline void doSomething();
};


inline void Managment::doSomething()
{
    a.doSomethingElse();
    b.doSomethingElse();
}

template<typename T>
inline void worker<T>::doSomethingElse()
{
    std::cout << "Hello!";
}

int main()
{
    Managment mgmt;
    mgmt.doSomething();
}

Upvotes: 1

P&#233;ter T&#246;r&#246;k
P&#233;ter T&#246;r&#246;k

Reputation: 116256

If no actual members of Management are referred by worker.inl (i.e. only pointers/references to Management), you could forward declare it:

class Management;

Worker::Worker(){
    Management* mgmt1; // fine
    Management& mgmt2; // fine
    //mgmt1 = new Management(); // won't compile with forward declaration
    //mgmt2.doSomething(); // won't compile with forward declaration
}

Otherwise your only option may be to move the method implementations into a cpp file, not included in the headers.

Note that Worker can not be forward declared, since it is contained by value in Management:

  worker<type1> worker1;

thus the compiler needs to know its full definition at this point.

You should also consider putting the two class definitions into the same header file, to express that they are interdependent, thus form a component.

Upvotes: 2

Dr. Snoopy
Dr. Snoopy

Reputation: 56347

I assume you're using include guards to avoid the compile error due to recursive inclusion.

But to solve the problem, i believe the solution is to use forward declarations and use Pointers/References when necessary.

Upvotes: 0

Related Questions