The Design of Software (CLOSED)

A public forum for discussing the design of software, from the user interface to the code architecture. Now closed.

The "Design of Software" discussion group has been merged with the main Joel on Software discussion group.

The archives will remain online indefinitely.

Circular dependency hell in C++

Hi,

This is a followup discussion to a recent discussion named "Help with C++ references".

I've got the following design:

class Foo
{
  static FooProxy createFoo();
}

where FooProxy is a shared_ptr-like structure that I hand-coded because I'm not allowed to use templates in my platform. To be clear, FooProxy::operator->() returns Foo*.

So the problem is that Foo references FooProxy because its factory method returns an instance and FooProxy references Foo because its operator->() returns it.

I cannot forward-declare FooProxy because it is not a pointer or reference type and I cannot forward-declare Foo because it happens to be a nested class in my code (and those cannot be forward-declared).

The only solution I can think of is to convert Foo into a top-level class and forward-declare it. The reason I am not keen on doing this is that it still leaves me with a circular dependency which seems "wrong" and I suspect it will cause me problems in the future. I am looking for an alternate design that is free of circular dependencies while being natural to use. One solution that comes to mind is:

class Foo
{
  // WARNING: end-users should not use this method!
  Foo();
}

class FooProxy
{
  FooProxy createFoo();
}

  The ugliness is that users now have to be aware of the proxies as well the fact that I am forbidding them to use the public constructor. The whole thing seems counter-intuitive for end-users. I cannot hide the constructor because FooProxy::createFoo() needs to invoke it and I cannot declare FooProxy as a "friend" because it would result in circular dependencies again.

Any ideas? :)

Thank you,
Gili
Gili Send private email
Tuesday, August 07, 2007
 
 
FooProxy::operator->() returns Foo*

Define it like this:

class Foo *FooProxy::operator->();

The word class and that it's a pointer means you no longer have to #include Foo.h in FooProxy.h
Scott
Tuesday, August 07, 2007
 
 
"I cannot forward-declare FooProxy because it is not a pointer or reference type . . ."

This is a very common misconception.  From what you've posted, you can forward declare FooProxy.  (Possibly there's something you haven't posted that would preclude it.)  In the .h file:

class FooProxy;
class Foo
{
  static FooProxy createFoo();
}

In the Foo.cpp file (or .cc or whatever), you'll need to #include both Foo and FooProxy, but you won't in the .h file, which will cut the dependence in the .h file. 

Of course, you'll still need to #include Foo and/or FooProxy in any cpp files where they are actually used.
Michael G Send private email
Tuesday, August 07, 2007
 
 
"So the problem is that Foo references FooProxy because its factory method returns an instance and FooProxy references Foo because its operator->() returns it."

I would think that Foo should know nothing about FooProxy. So FooProxy should have the (static?) factory method that wraps an instance of Foo - or something similar that works for you.

If this is solution is feasable for you, I think your dependency problem will be gone too.
Marc Jacobi Send private email
Tuesday, August 07, 2007
 
 
Wow... I was going to write up a nice piece about why your solutions do not work but I'm happy to be proven wrong :) Michael G's solution works perfectly!

The key is that one must forward-declare FooProxy inside the enclosing class, right before Foo's declaration. My mistake was that I was trying to forward-declare all my classes in one place as follows:

namespace one
{
  class A;
  namespace two
  {
    class B::Foo; // first approach
    class B
    {
      class Foo;  // second approach
    }
  }
}

either of the above approaches are dead wrong. The first one approach fails because you're referencing an unknown type B, which you cannot forward-declare because you can't reference the contents of a forward-declared type. The second approach fails because the compiler complains "class type redefinition: B".

It's funny what a difference moving a line of code makes :) So in summary: one *can* forward-declare nested classes but only from inside the *real* declaration of the enclosing class.

Thank you everyone for all your help!
Gili

PS: Marc, the reason I am against moving the static factory method into FooProxy is that it forces me to make Foo's constructor public (or else the method can't construct it). The problem there is that end-users will see a public constructor they shouldn't be using. Alternatively I could declare FooProxy as a friend of Foo but then I've recreated the circular dependency.
Gili Send private email
Tuesday, August 07, 2007
 
 

This topic is archived. No further replies will be accepted.

Other recent topics Other recent topics
 
Powered by FogBugz