Checking if a class is a descendent or ascendent of another class

Trying
1
2
3
bool A::isDescendentOf (A* p) const {
	return std::is_convertible<decltype(*this), decltype(*p)>::value;
}

doesn't seem to be working (nor does std:is_base_of), and I want to generalize the whole thing anyway.

The challenge I pose here is to write a function to be placed an a class's constructor that will update the info about the class's position in a hierarchy with a given root class. So rather than storing that information manually with simple informational code, it will be updated automatically whenever you change the hierarchy. For now, I am just trying to identify all descendent and ascendent classes of any given class. The code I've written below works correctly for just one base class and one derived class, but it runs into inaccuracies when a grandchild class is added.

I believe the only fix is needed in A::establishDerivedClassInfo(). Any help would be appreciated. A better overall approach is always welcomed.

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
#include <typeinfo>
#include <typeindex>
#include <iostream>
#include <set>
#include <map>

struct DerivedClassInfo {
	std::type_index type;
	std::set<DerivedClassInfo*> ancestorClasses;  // not sure which container is best
	std::set<DerivedClassInfo*> descendentClasses;
	DerivedClassInfo (const std::type_info& t) noexcept: type(t) {}
};

class DerivedClassManager {
	private:
		static std::map<std::type_index, DerivedClassInfo*> data;
	public:
		static const std::map<std::type_index, DerivedClassInfo*>& getData() {return data;}
		static void storeData (std::type_index t, DerivedClassInfo* d) {data[t] = d;}
		static const DerivedClassInfo* getData (std::type_index t) {return data[t];}
};
std::map<std::type_index, DerivedClassInfo*> DerivedClassManager::data;

class A {
	private:
		DerivedClassInfo* derivedClassInfo;
	public:
		A() {EstablishDerivedClassInfo();}
		virtual ~A() = default;
		DerivedClassInfo* getDerivedClassInfo() const {return derivedClassInfo;}
		void setDerivedClassInfo (DerivedClassInfo* d) {derivedClassInfo = d;}
		void showDerivedClassInfo() const;
		bool isDescendentOf (A*) const;
		bool isAncestorOf (A*) const;
	protected:
		void EstablishDerivedClassInfo();
};

class B: virtual public A {
	public:
		B() {EstablishDerivedClassInfo();}
};

class C: public B {
	public:
		C() {EstablishDerivedClassInfo();}
};

inline void A::showDerivedClassInfo() const {
	std::cout << std::endl << typeid(*this).name() << "'s DerivedClassInfo:" << std::endl;
	std::cout << "Descendent classes:" << std::endl;
	for (DerivedClassInfo* x: DerivedClassManager::getData(typeid(*this))->descendentClasses)
		std::cout << x->type.name() << std::endl;
	std::cout << "Ancestor classes:" << std::endl;
	for (DerivedClassInfo* x: DerivedClassManager::getData(typeid(*this))->ancestorClasses)
		std::cout << x->type.name() << std::endl;
}

inline void A::EstablishDerivedClassInfo() {  // This needs fixing
	DerivedClassInfo* d = new DerivedClassInfo (typeid(*this));
	setDerivedClassInfo(d);
	if (typeid(*this) == typeid(A))
		return;
	DerivedClassInfo* d_A;
	const std::map<std::type_index, DerivedClassInfo*> data = DerivedClassManager::getData();
	auto it = data.find(typeid(A));
	if (it != data.end())
		d_A = it->second;
	else
	{
		A* a = new A;
		d_A = a->getDerivedClassInfo();
	}
	d->ancestorClasses.emplace(d_A);
	d_A->descendentClasses.emplace(d);
	for (const std::pair<std::type_index, DerivedClassInfo*>& x: data)
	{	// Iterate through data and search for all direct parents or children of *this
		for (DerivedClassInfo* y: x.second->ancestorClasses)
			if (y->type == typeid(*this))
				d->descendentClasses.emplace (y);
		for (DerivedClassInfo* y: x.second->descendentClasses)
			if (y->type == typeid(*this))
				d->ancestorClasses.emplace (y);		
	}
// Perhaps now need to check if a parent of *this is a child of a child of ... of A (not done yet)

	DerivedClassManager::storeData (typeid(*this), d);
	DerivedClassManager::storeData (typeid(A), d_A);
}

inline bool A::isDescendentOf (A* p) const {
// return std::is_convertible<decltype(*this), decltype(*p)>::value; does not work 
// nor does return std::is_base_of<decltype(*p), decltype(*this)>::value
	for (DerivedClassInfo* x: DerivedClassManager::getData(typeid(*p))->descendentClasses)
		if (x->type == getDerivedClassInfo()->type) return true;
	return false;
}

inline bool A::isAncestorOf (A* p) const {
// return std::is_convertible<decltype(*p), decltype(*this)>::value; DOES work
	for (DerivedClassInfo* x: DerivedClassManager::getData(typeid(*p))->ancestorClasses)
		if (x->type == getDerivedClassInfo()->type) return true;
	return false;
}

int main() {
	A* a = new A;
	B* b = new B;
	C* c = new C;
	// a,b,c constructed and nothing else done (order of construction should be irrelevant)
	a->showDerivedClassInfo();
	b->showDerivedClassInfo();
	c->showDerivedClassInfo();
	std::cout.setf (std::ios_base::boolalpha);
	std::cout << "b is a descendent of a: " << b->isDescendentOf(a) << std::endl;
	std::cout << "c is a descendent of a: " << c->isDescendentOf(a) << std::endl;
	std::cout << "a is an ancestor of b: " << a->isAncestorOf(b) << std::endl;
	std::cout << "a is an ancestor of c: " << a->isAncestorOf(c) << std::endl;
	std::cout << "b is an ancestor of c: " << b->isAncestorOf(c) << std::endl;
	std::cout << "c is a descendent of b: " << c->isDescendentOf(b) << std::endl;
	std::cin.get();
}

Incorrect output:
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

1A's DerivedClassInfo:
Descendent classes:
1B
1B  // should not have this repetition, but that is a minor error to be fixed later
1C
Ancestor classes:

1B's DerivedClassInfo:
Descendent classes:  // missing C here
Ancestor classes:
1B  // B should not be here
1A
1B

1C's DerivedClassInfo:
Descendent classes:
Ancestor classes:
1A
1C  // should be B instead of C

b is a descendent of a: true
c is a descendent of a: true
a is an ancestor of b: true
a is an ancestor of c: true
b is an ancestor of c: false   // incorrect
c is a descendent of b: false   // incorrect
 
Last edited on
Maybe the best method is to do all the work outside the inheritance tree.

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
//Warning, C++11 code ahead
typedef std::set<std::string> _cendent_list;

// You can also extend this to include a list of non-family members
//     if you want to catch those.

template <class Origin, class Relative>
void find_relationship_helper3(_cendent_list&, _cendent_list& des, std::true_type)
{
    des.insert(typeid(Relative).name());
}
 
template <class Origin, class Relative>
void find_relationship_helper3(_cendent_list&, _cendent_list&, std::false_type)
{}

template <class Origin, class Relative>
void find_relationship_helper2(_cendent_list& ante, _cendent_list&, std::true_type)
{
    ante.insert(typeid(Relative).name());
}

template <class Origin, class Relative>
void find_relationship_helper2(_cendent_list& ante, _cendent_list& des, std::false_type)
{
    find_relationship_helper3<Origin, Relative>
        (ante, des, typename std::is_base_of<Origin, Relative>::type());
}

template <class Origin, class Relative>
void find_relationship_helper(_cendent_list& ante, _cendent_list& des)
{
    find_relationship_helper2<Origin, Relative>
        (ante, des, typename std::is_base_of<Relative, Origin>::type());
}

template <class Origin, class Relative, class Relative2, class... FamilyPack>
void find_relationship_helper(_cendent_list& ante, _cendent_list& des)
{
    find_relationship_helper<Origin, Relative>(ante, des);
    find_relationship_helper<Origin, Relative2, FamilyPack...>(ante, des);
}


//Important function here
template <class Origin, class... FamilyPack>
std::pair<_cendent_list, _cendent_list> find_relationship()
{
    _cendent_list ante, des;
    find_relationship_helper<Origin, FamilyPack...>(ante, des);
    return std::make_pair(ante, des);
}


I don't have a darn clue if this would work.

The only pre-C++11 ways I can think of involve either a lot of dynamic casting or a lot of overloads and overrides.

Edit:
Also, you should be careful to make sure you free all requested resources.

Edit 2:
Okay, so I actually tested it: http://ideone.com/mF7LMq
Last edited on
Daleth, your method with variadic templates looks very cool and I will study it and compare with my method (and I will still try to finish my method to compare at the end). Utility programs are your main interest, after all. Just some concerns: When I use

1
2
3
4
5
6
7
struct Parent{};
struct Child : Parent{};
struct Grandchild : Child{};
struct Y : Child{};
struct Z : Y{};
// ...
auto Z_list = find_relationship<Z, Y, Grandchild, Parent>();

I obviously won't determine that Child is an ascendent of Z because I didn't list it in the parameters. Ideally, we should not need to know which classes (of the possibly hundreds of classes in your program) are candidate ascendents or descendents. My method, though not working correctly yet, does not require any "guessing" but automated computation to work out all relatives of a class (or was it your intention to list all ALL classes in the entire program into the parameter list?). It also does not need to know what type 'this' is to begin with when requesting the relatives of an object (e.g. my line in main(): a->showDerivedClassInfo();). Perhaps I can try to combine our ideas. Or use simple dynamic_cast.
Last edited on
Perhaps I can try to combine our ideas.


That is probably best, and the shortcoming you pointed out about my code is very true. The only real way to guarantee all members of the inheritance tree are accounted for (while maintaining practicality) is to have a manager oversee the entire tree.
Topic archived. No new replies allowed.