Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Friend function massacred by circular includes

Class A is the sole instantiator and container of instances of class B.

So it seemed like a good idea to make the constructor of class B private, and call it only by a friend function declared in class B and defined in class A.

File A.h (EDIT: included enum defined in class B)

#ifndef   A_H
#define   A_H

#include "B.h"

using namespace std;

class A
{   public:
    A();
    shared_ptr<B> CreateBInstance( const B::ENUM_STANDARD_COLORS );
    shared_ptr<B> CreateBInstance( const string );  // Custom color

    private:
    unique_ptr<map<int, shared_ptr<B>>> UPTR__BInstancesMap;
}
#endif

File B.h (EDIT: included enum defined in class B)

#ifndef   B_H
#define   B_H

#include "A.h"  // Causes chaos

using namespace std;

class B
{   public:
    enum class ENUM_STANDARD_COLORS : unsigned int
    {   ...     // ~70 standard colors
    };

    private:
    B();

    friend
    shared_ptr<B> A::CreateBInstance( const ENUM_STANDARD_COLORS );

    friend
    shared_ptr<B> A::CreateBInstance( const string );   // Custom color
}
#endif

Class A requires a full declaration (#include) of class B for shared_ptr<B>.

Class B requires some declaration of class A for the friend function. A simple forward declaration causes the error:

invalid use of incomplete type 'struc A'

at the friend function declaration.

A full declaration (#include) is far worse:

error: prototype for 'std::shared_ptr A::CreateInstance()' does not match any in class 'A'

(but it does!) at the friend function declaration.
And, because class B is now broken:

error: 'B' was not declared in this scope
error: template argument 1 is invalid

appears at every mention of B in class A, as well as in classes F and G (etc.) which #include class B for their own use of shared_ptr<B>.
And, because classes F and G are also now broken:

error: 'F' was not declared in this scope
error: template argument 1 is invalid>
error: 'G' was not declared in this scope
error: template argument 1 is invalid

appears at every mention of F and G in class A.

Unfortunately, the include guards don't prevent the circular inclusion.

Is there a way to make this friend function work? I've never achieved so many errors in one go!

like image 971
pbyhistorian Avatar asked Aug 11 '16 06:08

pbyhistorian


2 Answers

Class A requires a full declaration (#include) of class B for shared_ptr<B>.

No, forward declaration would be sufficient in A.h. shared_ptr (and unique_ptr) won't require class B to be complete type for return type declaration[1] and class data member declaration.

File A.h

#ifndef   A_H
#define   A_H

class B;  // forward declaration

using namespace std;

class A
{   public:
    A();
    shared_ptr<B> CreateBInstance();

    private:
    unique_ptr<map<int, shared_ptr<B>>> UPTR__BInstancesMap;
};
#endif

[1] It's also true for "regular" type T which isn't shared_ptr.

like image 154
songyuanyao Avatar answered Nov 03 '22 01:11

songyuanyao


In A header file, you can simply tell the compiler that an type B exists and it needs to look for it while linking. To do so, replace

#include "B.h" 

by

class B;
like image 42
Omar Avatar answered Nov 03 '22 02:11

Omar