Circular Template Arguments

Nov 7, 2013 at 12:45am
Today I faced a problem where I had circular dependency in my template arguments. I was trying to make a class hierarchy similar to:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
template<class BType>
class A_base
{
	public:

		BType* getB();
};

template<class AType>
class B_base
{
	public:

		AType* getA();
};

int main()
{
	A_base<B_base< ...  <-- how can I possibly instantiate this class!!
}


Basically I had objects that were of type A<B<A<B<... you could see where this is going!

Now I scoured the forums and didn't really find a good solutions. A lot of people respond to this problem as "you must of made a mistake in your code design if you need circular template dependency". Lets ignore my opinions about people not in the know berating another person in the know and explain why this might be a valid design goal. Basically I have a tree like structure of heterogeneous types that must facilitate two-way interactions where A's can call B's and B's can call A's. This structure is useful in many contexts the difference is the methods A and B provide are different in each of these contexts. Instead of adding the getA and getB and all the other connectivity methods in every version of A and every version of B, I wanted to create a base class that managed this automatically. Seems sensible to me don't you think?

Another piece of advice was break up your code so there is a forward-only and backwards-only dependent types. This is not a complete solution because the two cannot know about the other and this does not really facilitate arbitrary two-way communication (where A calls B then B calls A back). It also makes the code more complicated in that I have two sets of objects and interfaces.

So the solution was to make the template arguments specific to the things I wanted to be flexible. The connectivity interface of A_base and B_base should be constant. Hence that cannot be in the template parameter. It was merely the traits that I wanted to make flexible so... I came up with this solution:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
#include <iostream>

template<class aTraitType,class bTraitType>
class A;

template<class aTraitType,class bTraitType>
class B;

template<class aTraitType,class bTraitType>
class A
	: public virtual aTraitType
{
	private:

		B<aTraitType,bTraitType>* myB;

	public:

		A(B<aTraitType,bTraitType>* myB_in=NULL)
			: myB(myB_in)
		{}

		void setB(B<aTraitType,bTraitType>* myB_in)
		{
			myB=myB_in;
		}

		B<aTraitType,bTraitType>* getB()
		{
			return myB;
		}
};

template<class aTraitType,class bTraitType>
class B
	: public virtual bTraitType
{
	private:

		A<aTraitType,bTraitType>* myA;

	public:

		B(A<aTraitType,bTraitType>* myA_in=NULL)
			: myA(myA_in)
		{}

		void setA(A<aTraitType,bTraitType>* myA_in)
		{
			myA=myA_in;
		}

		A<aTraitType,bTraitType>* getA()
		{
			return myA;
		}
};

class A_traits
{
	public:

		void print()
		{
			std::cout << "class A at " << this << " reporting for duty" << std::endl;
		}
};

class B_traits
{
	public:

		void print()
		{
			std::cout << "class B at " << this << " reporting for duty" << std::endl;
		}
};

int main()
{
	A<A_traits,B_traits> aObj;
	B<A_traits,B_traits> bObj;

	aObj.setB(&bObj);
	bObj.setA(&aObj);

	aObj.print();
	bObj.print();

	aObj.getB()->print();
	bObj.getA()->print();

	aObj.getB()->getA()->print();
	bObj.getA()->getB()->print();
}


Now this compiles and works great. The problem is that aObj and bObj cannot call their opposite within a trait method because print() does not know anything about the connectivity. So the solution there was to make traits an abstract base class. Then magically everything works! I was so pleased with this solution I decided to post it here for those who have similar problems:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
#include <iostream>

template<class aTraitType,class bTraitType>
class A_base;

template<class aTraitType,class bTraitType>
class B_base;

template<class aTraitType,class bTraitType>
class A_base
	: public virtual aTraitType
{
	private:

		B_base<aTraitType,bTraitType>* myB;

	public:

		A_base(B_base<aTraitType,bTraitType>* myB_in=NULL)
			: myB(myB_in)
		{}

		void setB(B_base<aTraitType,bTraitType>* myB_in)
		{
			myB=myB_in;
		}

		B_base<aTraitType,bTraitType>* getB()
		{
			return myB;
		}
};

template<class aTraitType,class bTraitType>
class B_base
	: public virtual bTraitType
{
	private:

		A_base<aTraitType,bTraitType>* myA;

	public:

		B_base(A_base<aTraitType,bTraitType>* myA_in=NULL)
			: myA(myA_in)
		{}

		void setA(A_base<aTraitType,bTraitType>* myA_in)
		{
			myA=myA_in;
		}

		A_base<aTraitType,bTraitType>* getA()
		{
			return myA;
		}
};

class A_traits
{
	public:

		virtual void print(unsigned long depth=0)=0;
};

class B_traits
{
	public:

		virtual void print(unsigned long depth=0)=0;
};

template<class aTraitType,class bTraitType>
class A
	: public A_base<aTraitType,bTraitType>
{
	public:

		void print(unsigned long depth=0)
		{
			if(depth==0)
				std::cout << "Class A at " << this << " reporting for duty" << std::endl;
			else if(this->getB()!=NULL)
				this->getB()->print(depth-1);
			else
				std::cout << "Class A is not connected to B" << std::endl;
		}
};

template<class aTraitType,class bTraitType>
class B
	: public B_base<aTraitType,bTraitType>
{
	public:

		void print(unsigned long depth=0)
		{
			if(depth==0)
				std::cout << "Class B at " << this << " reporting for duty" << std::endl;
			else if(this->getA()!=NULL)
				this->getA()->print(depth-1);
			else
				std::cout << "Class B is not connected to A" << std::endl;
		}
};

int main()
{
	A<A_traits,B_traits> aObj;
	B<A_traits,B_traits> bObj;

	aObj.print(1);
	bObj.print(1);

	aObj.setB(&bObj);
	bObj.setA(&aObj);

	aObj.print();
	bObj.print();

	aObj.print(1);
	bObj.print(1);

	aObj.print(2);
	bObj.print(2);
}


So this outputs the following. Clearly there is two-way communication!

Class A is not connected to B
Class B is not connected to A
Class A at 0x7fff25d1aa10 reporting for duty
Class B at 0x7fff25d1aa00 reporting for duty
Class B at 0x7fff25d1aa00 reporting for duty
Class A at 0x7fff25d1aa10 reporting for duty
Class A at 0x7fff25d1aa10 reporting for duty
Class B at 0x7fff25d1aa00 reporting for duty

Nov 7, 2013 at 12:56am
For the first example, could you explain your real-world scenario where you need a circular dependency and both types in the relationship have to be supplied as template parameters?

As a workaround, you can do some magic with making the constructor a template instead of the class, similar to boost::any.
Last edited on Nov 7, 2013 at 12:56am
Nov 7, 2013 at 2:21am
Sorry about being vague in my problem description... The code I am writing is for physical simulations where there is a continuous domain and a discrete domain. Equations in physics is continuous while computers are discrete. Like FEM and CFD I use a "mesh" to translate between the two domains. However in my particular problem there are three levels of discretization, hence three different interdependent mesh's. Depending on the topology I can solve different types of problems just by changing the how the different entities are connected. So to write generic code each entity needs to know about their neighbors. Now because there are different types of boundary conditions, different types of solver algorithms, gradient methods, discretization methods etc. etc. I wanted to leave the door open for the same structure to be used in all these different approaches just by changing the template arguments. The need for two-way communication is driven by efficiency. Some operations can avoid duplicating calculations if they are initiated at a mid level. Similarly for writing code that executes in parallel I can achieve better balance if I initiate other operations at different mid levels. Once again sorry for being vague but the problem I am working on is not all that simple.... I hope that gives you a better idea?

I didn't really understand the template constructor thing though? Maybe I didn't find the right any.hpp file on google but it didn't show any template constructor.
Last edited on Nov 7, 2013 at 2:22am
Nov 7, 2013 at 2:40am
(untested)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
struct MyClass
{
    template<typename T>
    MyClass(T *t)
    : v(new any<T>(t)) //magic
    {
        v.f(); //magic
    }
private:
    struct any_base
    {
        virtual void f() = 0;
        virtual ~any_base() = default;
    };
    template<typename T>
    struct any : any_base
    {
        T *v;
        any(T *t)
        : v(t)
        {
        }
        virtual void f()
        {
            std::cout << "any for T = " << typeid(T).name() << std::endl;
            std::cout << "\tv = " << (void *)v << std::endl;
        }
        virtual ~any() = default;
    };
    std::unique_ptr<any_base> v;
};
This is all from memory, so there may be mistakes.
Last edited on Nov 7, 2013 at 2:41am
Nov 7, 2013 at 3:19am
Thanks L B

I may not totally understand that example you gave, it seems to be using composition to provide an interface as defined by any_base? then implemented in any? I could see vaguely how that would work.... basically you have eliminated the need for template arguments by storing a pointer of something that implements my "traits" interface.... It is an alternative solution..... thanks for posting that.... Its hard for me to see the relative merits of the the different approaches at the moment... If new thoughts come to mind I will post them here....

Ok Update.... I see a little more what you were getting at L B and what you have provided makes more sense to me.... basically you can solve the problem of AType<BType<AType<BType<... when your class is not a template but a general purpose container.... So originally I was using the template arguments to define what interface and object can expect from it neighbor.

I think you solution solves the AType<BType<AType<BType< problem directly. While the solution I posted solves something a bit more specific maybe I should change my topic title... makes sense why you were asking about a real world example. Let me elaborate more...

So lets say I have a set of types that always interact as follows (with some branches that are not shown):

A <==> B <==> C <==> D <===> E <==> F

A's only talk to B's, B's talk to A's and C's etc. Now in the code any number of A's can be connected to any number of B's (any number of B's could be connected with any number of C's etc.) and it is this connectivity that defines the calculation. So I wanted to have each A, B, C etc... have the same connectivity methods implemented in the same class. So A_base and B_base only needs to be written once. Yet I wanted to the interfaces of the A's and B's to remain templates so they are flexible..... think of it as a highly structured container of heterogeneous types...

So the naive solution was the first example... which resulted in a circular template dependency hence why I posted it with a misleading topic title....

Last edited on Nov 7, 2013 at 3:49am
Nov 7, 2013 at 3:45am
Well the benefit to this approach is that you don't have the problem from the first code snippet of infinitely nested templates.
Nov 7, 2013 at 4:09am
Yeah I agree..... I didn't see it at first.... My head was stuck in a different problem.... Regardless thanks for offering the help!
Last edited on Nov 7, 2013 at 4:29am
Topic archived. No new replies allowed.