tags:

views:

100

answers:

5

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.

+2  A: 

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.

Péter Török
Right, some action needs to be done on the Management class. Exactly like mgmt2.doSomething(). This is why I needed the .h file. Sounds to me that the only way around this is a bit of a rewrite. Oh Joy.
windfinder
Actually, you can use value types here -- it need not be pointers. Example: http://codepad.org/gtLynfPC
Billy ONeal
A: 

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.

Matias Valdenegro
+1  A: 
Billy ONeal
This only works with pointers and refs to `worker<T>`...
Oli Charlesworth
@Billy, AFAIK the compiler needs to know the exact size of a `worker` object - thus its complete class definition - to resolve this.
Péter Török
@Billy: Perhaps I misunderstand your code snippet, but if your first line is simply a forward declaration of `worker<T>`, then the compiler won't know how big a `Management` object is.
Oli Charlesworth
@Oli: I don't see why the compiler needs to know how big a management object is at that point -- sure, Worker would need to be a complete type in order to *instantiate* management, but as far as I knew it wasn't required at declaration time -- it's just a declaration. But you are correct in that it didn't compile. I've updated my answer with a compiling example.
Billy ONeal
+1  A: 

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.

tenfour
@tenfour: there will be a runtime overhead (`virtual has a cost`) however I agree it's highly unlikely to be noticeable.
Matthieu M.
`#define interface class` ???
Mark Ransom
@Matthieu, the runtime overhead can be avoided if you never use pointers or references. If you are calling a member of a concrete object, the compiler knows which member will be called and bypasses the virtual lookup.
Mark Ransom
@Mark: but that would defeat the point of using an interface!
Oli Charlesworth
you can also use `__declspec(novtable)`. The point is to bundle the operations that `Worker` uses into one interface, as a matter of organization. Actually I prefer Billy ONeal's solution usually, but sometimes the interface method is cleaner too, depending on the context. `interface` I think is a MS-only thing, but yea you could similarly just use a `class`.
tenfour
@tenfour: Yes, `interface` is not and has never been standard C++.
Billy ONeal
A: