circular dependencies, unrecognized base types, oh-my

Discussion in 'C++' started by crichmon, Jun 26, 2004.

  1. crichmon

    crichmon Guest

    Any general advice for dealing with circular dependencies? For example, I
    have a situation which, when simplified, is similar to:

    /////////////
    // A.h

    class A
    {
    public:
    int x;
    };

    /////////////
    // B.h

    #include "A.h"
    #include "C.h"

    class B: public A
    {
    public:
    C* myC;
    };

    ////////////
    // C.h

    #include "B.h"

    class C
    {
    public:
    B* myB;
    };

    ////////////

    The problem that I am having is that if I don't add some kind of forward
    declaration in B.h and/or C.h, B and/or C will be undefined when processing
    C.h or B.h (respectively). My first question here is in a case like this,
    should I put a forward declaration of C in B.h and of B in C.h? Only one is
    necessary, but it all depends on which header file the compiler reaches
    first.

    And now for the compounded problem... I've altered C.h to include a forward
    declaration of B after B.h is included, and the first problem (above) goes
    away. I have some other code (not simplified and included) in C.h that
    explicitly accesses the x integer of B's base class (in C.h: myB->A::x).
    The compiler is giving me an error, saying that " 'A' is not a base type for
    type 'B' ". Arg! What's going on here?

    Now for the disclaimer... I haven't actually tried my simplified example yet
    in the compiler, so I don't know if the error behavior I describe can be
    produced with it. However it outlines the general problem in my code that
    I'm currently dealing with. The only differences is that my classes use
    multiple inheritance and templates as well, so those factors could easy add
    to the fire...

    Anyways, any thoughts or insights into this situation would be greatly
    appreciated.

    thanks,
    crichmon
     
    crichmon, Jun 26, 2004
    #1
    1. Advertising

  2. On Sat, 26 Jun 2004 07:56:25 GMT, "crichmon" <>
    wrote:

    >Any general advice for dealing with circular dependencies? For example, I
    >have a situation which, when simplified, is similar to:

    [snip]

    (Isn't this a FAQ??)

    Use forward declarations for the pointer members and only include the
    headers in your .cpp (i.e. implementation) file(s).

    --
    Bob Hairgrove
     
    Bob Hairgrove, Jun 26, 2004
    #2
    1. Advertising

  3. "crichmon" <> wrote in message
    news:J_9Dc.14238$...
    > Any general advice for dealing with circular dependencies? For example, I
    > have a situation which, when simplified, is similar to:
    >
    > /////////////
    > // A.h
    >
    > class A
    > {
    > public:
    > int x;
    > };
    >
    > /////////////
    > // B.h
    >
    > #include "A.h"
    > #include "C.h"
    >
    > class B: public A
    > {
    > public:
    > C* myC;
    > };
    >
    > ////////////
    > // C.h
    >
    > #include "B.h"
    >
    > class C
    > {
    > public:
    > B* myB;
    > };
    >
    > ////////////
    >
    > The problem that I am having is that if I don't add some kind of forward
    > declaration in B.h and/or C.h, B and/or C will be undefined when

    processing
    > C.h or B.h (respectively). My first question here is in a case like this,
    > should I put a forward declaration of C in B.h and of B in C.h?


    Yes.

    > Only one is
    > necessary, but it all depends on which header file the compiler reaches
    > first.


    You can't sensibly control which header file the compiler reaches first. Use
    forward decalrations in both cases.

    >
    > And now for the compounded problem... I've altered C.h to include a

    forward
    > declaration of B after B.h is included,


    What is the point of having a forward declaration *after* B.h has been
    included.

    > and the first problem (above) goes
    > away. I have some other code (not simplified and included) in C.h that
    > explicitly accesses the x integer of B's base class (in C.h: myB->A::x).
    > The compiler is giving me an error, saying that " 'A' is not a base type

    for
    > type 'B' ". Arg! What's going on here?


    C.h include B.h, C.h includes A.h, A.h includes C.h. This is a mess isn't
    it?

    When classes are this interdependent, it's really not a good idea to put
    them in seperate header files.

    Here's what I suggest, in one header file. I've added void C::func() to
    represent the code in C that accesses A::x

    class B;
    class C;

    class A
    {
    public:
    int x;
    };

    class C
    {
    public:
    void func();
    B* myB;
    };

    class B: public A
    {
    public:
    C* myC;
    };

    // now A, B and C are fully defined so we can place any code that uses them
    here

    inline void C::func()
    {
    myB->x;
    }

    Simple.

    john
     
    John Harrison, Jun 26, 2004
    #3
  4. crichmon

    crichmon Guest

    "John Harrison" <> wrote:
    > "crichmon" <> wrote:
    >>
    >> /////////////
    >> // A.h
    >>
    >> class A
    >> {
    >> public:
    >> int x;
    >> };
    >>
    >> /////////////
    >> // B.h
    >>
    >> #include "A.h"
    >> #include "C.h"
    >>
    >> class B: public A
    >> {
    >> public:
    >> C* myC;
    >> };
    >>
    >> ////////////
    >> // C.h
    >>
    >> #include "B.h"
    >>
    >> class C
    >> {
    >> public:
    >> B* myB;
    >> };
    >>
    >> ////////////
    >>
    >> The problem that I am having is that if I
    >> don't add some kind of forward declaration
    >> in B.h and/or C.h, B and/or C will be
    >> undefined when processing C.h or B.h
    >> (respectively). My first question here is
    >> in a case like this, should I put a forward
    >> declaration of C in B.h and of B in C.h?

    >
    > Yes.
    >
    >> Only one is necessary, but it all depends
    >> on which header file the compiler reaches
    >> first.

    >
    > You can't sensibly control which header file
    > the compiler reaches first. Use forward
    > decalrations in both cases.


    Okay, thanks!

    I figured out the rest of my problem! (see the following):


    >> And now for the compounded problem... I've
    >> altered C.h to include a forward declaration
    >> of B after B.h is included,

    >
    > What is the point of having a forward
    > declaration *after* B.h has been included.


    Because with a circular dependency, it's possible for B or C to not be
    defined yet.

    Use the examples mentioned previously, but imagine that all are surrounded
    by

    #ifndef A_H
    #define A_H
    ....
    #endif

    for file A.h (or B_H for file B.h, and C_H for file C.h).

    Let's say that B.h is processed first:

    The preprocessor will run "#ifndef B_H" and determine that it's undefined.
    It will then define B_H. Next it will include C.h and will begin processing
    it.

    The preprocessor will run "#ifndef C_H" and determine that it's undefined.
    It will then define C_H. Next it will include B.h and will begin processing
    it.

    The preprocessor will run "#ifndef B_H" and determine that it *is* defined,
    and will therefore not do anything inside the ifndef block. Since the
    entire file is contained within that block, the preprocessor will return to
    processing the C.h file.

    The C.h contains the definition for the C class. This definition makes use
    of a variable of type "B". Unfortunately because B has not yet been defined
    (the preproccessor only got as far as '#include "C.h"' in B.h), the compiler
    will complain and exit.

    The same scenario would occur if C.h is processed first... the only
    difference is that 'C' and 'B' will be swapped in the scenario described.

    A forward declaration is necessary, even after the include, so the compiler
    knows that the particular types are defined somewhere.


    >> and the first problem (above) goes away.
    >> I have some other code (not simplified and
    >> included) in C.h that explicitly accesses
    >> the x integer of B's base class (in C.h:
    >> myB->A::x). The compiler is giving me an
    >> error, saying that " 'A' is not a base
    >> type for type 'B' ". Arg! What's going
    >> on here?

    >
    > C.h include B.h, C.h includes A.h, A.h
    > includes C.h. This is a mess isn't it?
    >
    > When classes are this interdependent,
    > it's really not a good idea to put them in
    > seperate header files.


    I don't know if I would agree with this, although there are probably valid
    arguments for and against putting interdependant classes in the same header.


    > Here's what I suggest, in one header file.
    > I've added void C::func() to represent the
    > code in C that accesses A::x
    >
    > class B;
    > class C;
    >
    > class A
    > {
    > public:
    > int x;
    > };
    >
    > class C
    > {
    > public:
    > void func();
    > B* myB;
    > };
    >
    > class B: public A
    > {
    > public:
    > C* myC;
    > };
    >
    > // now A, B and C are fully defined so we
    > // can place any code that uses them here
    >
    > inline void C::func()
    > {
    > myB->x;
    > }
    >
    > Simple.


    Your solution works because your code for C::func occurs *after* the
    definition of the B class. The fact that I didn't realize this right away
    is is what gave me difficulty in my program.

    Because my previous project had been to learn C#, and since C# combines it's
    code and header files into a single *.cs file, I had gotten used to
    combining class definition and code into single files. I therefore started
    my project with a lot of *.h files that also contained the code for each
    function. Therefore, because I had to use forward declarations for B and C
    to handle the circular dependency issue, I was left dealing with a forward
    declaration of class B that indicated no inheritance (and thus the "'A' is
    not a base type for type 'B'" error). So my files looked like this:

    /////////////
    // A.h

    #ifndef A_H
    #define A_H

    class A
    {
    public:
    int x;
    };

    #endif

    /////////////
    // B.h

    #ifndef B_H
    #define B_H

    #include "A.h"
    #include "C.h"

    class C;

    class B: public A
    {
    public:
    C* myC;
    };

    #endif

    ////////////
    // C.h

    #ifndef C_H
    #define C_H

    #include "B.h"

    class B;

    class C
    {
    public:
    B* myB;

    void func()
    {
    myB->A::x;
    }
    };

    #endif

    ////////////

    If file B.h is processesd before file C.h, the compiler would complain about
    file C.h, saying that "'A' is not a base type for type 'B'" because all it
    knew about B was that it was a class due to it's forward declaration.

    (My program is actually much more complicated than my example, and I need to
    explicitly specific the parent class as I have a situatin where, using this
    example, 'func' would call a function defined in template class D, where A
    inherits from D<A> and B inherits from D<B>, so not specifying the specific
    class would result in an ambiguity error from the compiler.)

    So the solution then was to put all code into a seperate code file. Then
    everything worked as it should!

    crichmon
     
    crichmon, Jun 28, 2004
    #4
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Suzanne Vogel
    Replies:
    2
    Views:
    2,639
    Suzanne Vogel
    Jun 26, 2003
  2. Dylan
    Replies:
    7
    Views:
    594
    Dylan
    Jul 7, 2004
  3. ernesto basc?n pantoja

    Circular dependencies

    ernesto basc?n pantoja, Nov 29, 2004, in forum: C++
    Replies:
    2
    Views:
    3,803
    Larry Brasfield
    Nov 29, 2004
  4. Kiuhnm
    Replies:
    16
    Views:
    761
    Jonathan Mcdougall
    Jan 3, 2005
  5. ro86
    Replies:
    4
    Views:
    574
Loading...

Share This Page