Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cyclic Dependencies in Template Class

I am having an issue with cyclic dependencies with template classes. I have something similar to the following,

// A.hxx
template<typename T>
class B;

template<typename T>
class C;

template<typename T>
class A
{
    A(T const& x, T const& y, T const& z)
    {
        data[0] = x;
        data[1] = y;
        data[2] = z;
    }

    A(B<T> const& b) :
     A(b.x(),b.y(),b.z())
    {}

    A(C<T> const& c) :
     A(c.x(),c.y(),c.z())
    {}


    T x() {return data[0];}
    T y() {return data[1];}
    T z() {return data[2];}

    T data[3];
};



// B.hxx
template<typename T>
class A;

template<typename T>
class C;

template<typename T>
class B
{
    B(T const& y, T const& z, T const& x)
    {
        data[0] = y;
        data[1] = z;
        data[2] = x;
    }

    B(A<T> const& a) :
     B(a.y(),a.z(),a.x())
    {}

    B(C<T> const& c) :
     B(c.y(),c.z(),c.x())
    {}

    T x() {return data[2];}
    T y() {return data[0];}
    T z() {return data[1];}

    T data[3];
};



// C.hxx
template<typename T>
class A;

template<typename T>
class B;

template<typename T>
class C
{
    C(T const& z, T const& x, T const& y)
    {
        data[0] = z;
        data[1] = x;
        data[2] = y;
    }

    C(A<T> const& a) :
     C(a.z(),a.x(),a.y())
    {}

    C(B<T> const& b) :
     C(b.z(),b.x(),b.y())
    {}

    T x() {return data[1];}
    T y() {return data[2];}
    T z() {return data[0];}

    T data[3];
};

The forward declarations do not work. I have tried breaking the definitions out of the declaration and including the relevant hxx file after declaring the class, but no luck either. Any help would be appreciated. Thanks

like image 366
armstrhu Avatar asked Mar 14 '23 03:03

armstrhu


1 Answers

In that special case, I would try to remove the cyclic dependancy by using interfaces or superclasses. Basics: to remove the cyclic dependancy, each real class inherits from a superclass that only declares methods that are used in other classes. It might implement the ones that do not depend on other classes, or be a mere interface (only virtual methods). The only rule is that you must only use pointers or reference to object of other classes to avoid the slicing problem. Here it is simpler because all classes inherit from a common one, but in a more general use case, each could have its own superclass. It could be:

D.hxx

#ifndef _D
#define _D

template<typename T> 
class D {
public:
    virtual T x() const = 0;
    virtual T y() const = 0;
    virtual T z() const = 0;
    virtual ~D() = 0;    // better to add a virtual destructor...
};

#endif

that way, the other files become (say for A.hxx):

#include "d.h"

template<typename T>
class A: public D<T>
{
public:
    A(T const& x, T const& y, T const& z)
    {
        data[0] = x;
        data[1] = y;
        data[2] = z;
    }

    A(class D<T> const& d): A(d.x(), d.y(), d.z()) {} // for C++11 and above...

    T x() const { return data[0]; }
    T y() const { return data[1]; }
    T z() const { return data[2]; }

private:
    T data[3];
};

It works here, because you only use references of objects from other classes, so you only need the declarations. But I cannot know whether it can apply to your real use case.

Anyway as already said, the common superclass was only used here because it was simple, but you could use one interface or superclass per class

like image 153
Serge Ballesta Avatar answered Mar 29 '23 22:03

Serge Ballesta