0

a part of my program basically boils down to the code below which won't compile due to circular dependency. I know there are previous discussions about this topic, but none of the previous solutions (at least none that I can find) resolves my situation:

A.h

#ifndef A_H
#define A_H

#include <iostream>

#include "B.h"

class A
{
  public:
    typedef struct a_struct
    {
      int x;
      int y;
    }a_struct_t;

    void print_something() { std::cout << "Hello world" << std::endl; }
  
  private:
    typedef struct another_struct
    {
      int x;
      a_struct_t a_struct;
    }another_struct_t;
    
    friend void B::b_func1();
};
    
#endif

B.h

#ifndef B_H
#define B_H

#include "A.h"

class B
{ 
  public:
    void b_func1();
    A::a_struct_t b_func2();
};

#endif

B.cpp

#include "B.h"

void B::b_func()
{ 
  A::another_struct_t another_struct;
  // Do something with another_struct;
} 

A::a_struct_t B::b_func2()
{
  A::a_struct_t a_struct;
  return a_struct;
}

try.cpp

#include "A.h"

int main()
{
  A a_object;
  a_object.print_something();

  return 0;
}

The issue is class A can't friend just void B::b_func1() in A.h without #include "B.h" and have class B be completely defined, but class B also can't use A::a_struct_t in B.h without #include "A.h" and have class A be completely defined. I've tried forward declaring class A in B.h, but I still can't use A::a_struct_t in B.h because class A is an incomplete type.

I can move a_struct_t to class B which works, but it really belongs in class A. Also I can friend entire class B and not #include "B.h" in A.h which also works, but ideally I'd like to just friend void B::b_func1() instead of the entire class B. Is there a way to make this work? Thanks in advance,

P.S. - I've read about the forward declaration that was the top comment in the thread titled "Resolve build errors due to circular dependency amongst classes". That unfortunately doesn't work in my case, at least I've not been able to apply forward declaration in a way to rid of the error: invalid use of incomplete type 'class xxx'

Arcaniaco
  • 400
  • 2
  • 10
Dixin Fan
  • 1
  • 1
  • 2
    Why is `a_struct_t` defined inside the definition of `A`? Naively, it seems like the easiest fix would be to move that to a separate header (which both A.h and B.h would include) and refer to it without `A::`. Then B.h would not need to include A.h. – zwol Jun 02 '21 at 22:44
  • @zwol Actually `a_struct_t` is shared between `A` and `B`, and it was decided early that all shared `struct` definitions will reside in `A`. I have several dozens of `A`s and `B`s with one `A` paired with one `B`. Even though there is this paired relationship between a `A` and a corresponding `B`, and the paired classes share `struct`s, the two classes do complete different work and so it was also decided early that they would remain as two different classes rather than one. I want to avoid a separate header because then I'd have several dozens of these. I Hope that makes sense. – Dixin Fan Jun 04 '21 at 16:23

1 Answers1

0

It is not possible for these reasons:

  1. A.h - You can’t forward declare B and use B::b_func1 as B needs to be fully defined to use the b_func1 function.
  2. B.h – You can’t forward declare A and use A::a_struct for the same reason. You also can’t do forward declare nested class/struct such as A::a_struct. (How do I forward declare an inner class?)

So there’s only 3 options I can think of from least work to most work:

  1. Allow B to full access to A (friend all of B) - you know this one already
  2. Introduce a helper C which its sole purpose to provide another_struct_t.
  3. Redesign to avoid this circular dependency

This is what it would look like if you went with option 2, keeping most of the code the same as before:

A.h

#ifndef A_H
#define A_H

#include <iostream>

class A
{
  public:
    typedef struct a_struct
    {   
      int x;
      int y;
    }a_struct_t;

    void print_something(){ std::cout << "Hello world" << std::endl;}

};

#endif          

B.h

#ifndef B_H
#define B_H

#include "A.h"

class B
{
  public:
    B();
    void b_func1();
    A::a_struct_t b_func2();
};

#endif

B.cpp

#include "B.h"
#include "C.h"

void B::b_func1()
{
  C::another_struct_t another_struct;
  // Do something with another_struct;
}

A::a_struct_t B::b_func2()
{
  A::a_struct_t a_struct;
  return a_struct;
}

C.h

#ifndef C_H
#define C_H

#include "A.h"
#include "B.h"

class C
{
  private:
    typedef struct another_struct
    {   
      int x;
      A::a_struct_t a_struct;
    }another_struct_t;

   friend void B::b_func1();
};

#endif

try.cpp

#include "A.h"

int main()
{
  A a_object;
  a_object.print_something();

  return 0;
}

vx7
  • 1