Obtaining pointers to represent each branch in a hierarchy

My program has a large version of this, where every leaf class is singleton, and pointers of the base class to represent each possible path are stored in a map during compile time. I have it working as follows:
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
#include <iostream>
#include <string>
#include <map>

struct A;  struct AChild;  struct B;  struct BChild;  struct C;  struct D;  struct E;

struct System {
	static const std::map<std::string, A*> prototypes;
	static std::map<std::string, A*> initializePrototypes();
};
const std::map<std::string, A*> System::prototypes = initializePrototypes();

struct A {
	const std::string name;
	AChild* child;
	A (const std::string& n) : name(n) {}
	A (const std::string& n, AChild*  newChild) : name(n), child(newChild) {}
};

struct AChild : A {using A::A;};

struct B : AChild {
	BChild* child;
	using AChild::AChild;
	B (const std::string& n, BChild* newChild) : AChild(n), child(newChild) {}
};

struct BChild : B {using B::B;};

struct C : public BChild {
	static C* getInstance() {static C* c = new C("C");  return c;}  // singleton because it's a leaf
  private:
	C (const std::string& n) : BChild(n) {}
};

struct D : BChild {
	static D* getInstance() {static D* d = new D("D");  return d;}  // singleton because it's a leaf
  private:
	D (const std::string& n) : BChild(n) {}
};

struct E : AChild {
	static E* getInstance() {static E* e = new E("E");  return e;}  // singleton because it's a leaf
	using AChild::AChild;
  private:
	E (const std::string& n) : AChild(n) {}
};

std::map<std::string, A*> System::initializePrototypes() {
	std::map<std::string, A*> map;
	B *bc = new B("B-C", C::getInstance()), *bd = new B("B-D", C::getInstance());
	A *abc = new A("A-B-C", bc),  *abd = new A("A-B-D", bd),  *ae = new A("A-E", E::getInstance());
	map.emplace(abc->name, abc);
	map.emplace(abd->name, abd);
	map.emplace(ae->name, ae);
	return map;
}

int main() {
	for (const auto& x : System::prototypes)
		std::cout << x.first << ", " << x.second << std::endl;
	std::cin.get();
}

/*  Output:
A-B-C, 0x652a48
A-B-D, 0x652a78
A-E, 0x652ac8
*/


But System::initializePrototypes() is constructing the map manually, and I want to use recursion somehow, because my hierarchy is much bigger than this. It's also easy to miss a path doing it the above way, and when new classes are added to the hierarchy, it will be a nightmare to update the map. So the ideal solution is recursion constructing the map perfectly--and when new classes are introduced, nothing needs to be added to System::initializePrototypes().
Last edited on
Why is every node in the tree of a different type?
Because every node has special data members and methods, yet are all derived from A (which is a very broad class). They are not like nodes of linked list.
It doesn't sound like they belong in a generic tree structure. You don't gain anything, since you need to know at the access point the type of object you want and the path to it on the tree.
Ok here is the motivation:
Every Person has an A* data member. In the hierarchy of A above, the derived classes C,D,E are singletons. Hence in the above example, there are only a finite number of possible A* values. Therefore, instead of initializing a Person with a new A* value every time (imagine 10000 different values of A* corresponding to 10000 people when there are only 50 possibilities for A*!), first determine which path corresponds to him (there is a unique path for each Person), then assign the unique A* value corresponding to that path. This is what System::prototypes is for.
Last edited on
I considered that as a way to assign values to A*, and currently it would work. But looking ahead, I will probably have multiple inheritance in the hierarchy for A. So there will be multiple paths leading to certain leafs, which means that the whole system will fall apart if I take that approach right now. Hence I think an A* value for each possible path, instead of each leaf, is the flexible approach I should take.

I think a possible method is to use variadic template arguments A, B, C, .... and put them into some manager class, perhaps stating explicitly which clases are the leafs. Then using type_traits, std::is_base_of, etc... determine with recursion what I've done above with brute force in System::initializePrototypes(). As a result, if I ever add new class X, Y, Z to the hierarchy, I only need to add X, Y, Z to the template argument for this manager class. But perhaps I'm just dreaming.
Last edited on
If the number of possible values is small, then why not simply preallocate all values in a sequential structure and give Persons indices on that structure?
Last edited on
Ok, even though some may be questioning my design to begin with, I nevertheless find this challenge a nice exercise. This does help one sharpen his skills I feel. So, the original goal is somewhat ambitious, so to take a smaller step to eventually get there, I've written a class that determines all the leaf classes of any hierarchy. All that is required is to input the classes of the hierarchy into its templates:
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
template <typename...NODES>
struct HierarchyMangager {
	template <typename T, typename FIRST, typename... REST>
	struct isLeafHelper {  // Helper recursive function object.
		bool operator()(void) const {
			if (!std::is_base_of<T, FIRST>::value || std::is_same<T, FIRST>::value)
				return isLeafHelper<T, REST...>()();
			return false;
		}
	};
	
	template <typename T, typename U>
	struct isLeafHelper<T, U> {  // Partial specialization to catch base case.
		bool operator()(void) const {
			return !std::is_base_of<T, U>::value || std::is_same<T, U>::value;
			// Important to check std::is_same<T, U>::value, else E would not be identified as a leaf!
		}
	};
	
	template <typename T>
	static bool isLeaf() {
		return isLeafHelper<T, NODES...>()();
	}

	template <typename T>
	static A* leafPointer() {
		return isLeaf<T>() ? T::getInstance() : nullptr;
	}
};

int main() {
	HierarchyMangager<A,B,C,D,E> manager;
	const bool leafA = manager.isLeaf<A>(),  leafB = manager.isLeaf<B>(),  leafC = manager.isLeaf<C>(),  
		leafD = manager.isLeaf<D>(),  leafE = manager.isLeaf<E>();
	std::cout << std::boolalpha << "leafA = " << leafA << "\nleafB = " << leafB << "\nleafC = " << leafC 
		<< "\nleafD = " << leafD << "\nleafE = " << leafE << std::endl;
	std::cin.get();
}

/*
Output:
leafA = false
leafB = false
leafC = true
leafD = true
leafE = true
*/


The next (fun) step is to now use the identified leaves and get the desired paths leading to those leaves through some sort of recursion (e.g. using the parents of the leaves as new leaves, or recursively find the parents of the leaves until the root is reached). Anyone who wishes to chime in some solutions, please feel free to do so.
Last edited on
Topic archived. No new replies allowed.