Happy New Year!
[ginac.git] / ginac / class_info.h
1 /** @file class_info.h
2  *
3  *  Helper templates to provide per-class information for class hierarchies. */
4
5 /*
6  *  GiNaC Copyright (C) 1999-2019 Johannes Gutenberg University Mainz, Germany
7  *
8  *  This program is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License as published by
10  *  the Free Software Foundation; either version 2 of the License, or
11  *  (at your option) any later version.
12  *
13  *  This program is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *  GNU General Public License for more details.
17  *
18  *  You should have received a copy of the GNU General Public License
19  *  along with this program; if not, write to the Free Software
20  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
21  */
22
23 #ifndef GINAC_CLASS_INFO_H
24 #define GINAC_CLASS_INFO_H
25
26 #include <cstddef> // for size_t
27 #include <cstring>
28 #include <iomanip>
29 #include <iostream>
30 #include <map>
31 #include <stdexcept>
32 #include <string>
33 #include <vector>
34
35 namespace GiNaC {
36
37 // OPT is the class that stores the actual per-class data. It must provide
38 // get_name(), get_parent_name() and get_id() members.
39
40 template <class OPT>
41 class class_info {
42 public:
43         class_info(const OPT & o) : options(o), next(first), parent(nullptr)
44         {
45                 first = this;
46                 parents_identified = false;
47         }
48
49         /** Get pointer to class_info of parent class (or nullptr). */
50         class_info *get_parent() const
51         {
52                 identify_parents();
53                 return parent;
54         }
55
56         /** Find class_info by name. */
57         static const class_info *find(const std::string &class_name);
58
59         /** Dump class hierarchy to std::cout. */
60         static void dump_hierarchy(bool verbose = false);
61
62         OPT options;
63
64 private:
65         struct tree_node {
66                 tree_node(class_info *i) : info(i) {}
67                 void add_child(tree_node *n) { children.push_back(n); }
68
69                 std::vector<tree_node *> children;
70                 class_info *info;
71         };
72
73         static void dump_tree(tree_node *n, const std::string & prefix, bool verbose);
74         static void identify_parents();
75
76         static class_info *first;
77         class_info *next;
78         mutable class_info *parent;
79
80         static bool parents_identified;
81 };
82
83 template <class OPT>
84 const class_info<OPT> *class_info<OPT>::find(const std::string &class_name)
85 {
86         // Use a map for faster lookup. The registered_class_info list doesn't
87         // change at run-time, so it's sufficient to construct the map once
88         // on the first trip through this function.
89         typedef std::map<std::string, const class_info *> name_map_type;
90         static name_map_type name_map;
91         static bool name_map_initialized = false;
92
93         if (!name_map_initialized) {
94                 // Construct map
95                 const class_info *p = first;
96                 while (p) {
97                         name_map[p->options.get_name()] = p;
98                         p = p->next;
99                 }
100                 name_map_initialized = true;
101         }
102
103         typename name_map_type::const_iterator it = name_map.find(class_name);
104         if (it == name_map.end())
105                 throw (std::runtime_error("class '" + class_name + "' not registered"));
106         else
107                 return it->second;
108 }
109
110 template <class OPT>
111 void class_info<OPT>::dump_tree(tree_node *n, const std::string & prefix, bool verbose)
112 {
113         std::string name = n->info->options.get_name();
114         std::cout << name;
115         if (verbose)
116                 std::cout << " [ID 0x" << std::hex << std::setw(8) << std::setfill('0') << n->info->options.get_id() << std::dec << "]" << std::endl;
117
118         size_t num_children = n->children.size();
119         if (num_children) {
120                 for (size_t i = 0; i < num_children; ++i) {
121                         if (verbose) {
122                                 std::cout << prefix << " +- ";
123                                 if (i == num_children - 1)
124                                         dump_tree(n->children[i], prefix + "    ", verbose);
125                                 else
126                                         dump_tree(n->children[i], prefix + " |  ", verbose);
127                         } else {
128                                 std::string spaces(name.size(), ' ');
129                                 if (i > 0)
130                                         std::cout << prefix << spaces;
131                                 if (num_children == 1)
132                                         std::cout << " --- ";
133                                 else if (i > 0)
134                                         std::cout << "  +- ";
135                                 else
136                                         std::cout << " -+- ";
137                                 if (i == num_children - 1)
138                                         dump_tree(n->children[i], prefix + spaces + "     ", verbose);
139                                 else
140                                         dump_tree(n->children[i], prefix + spaces + "  |  ", verbose);
141                         }
142                 }
143         } else if (!verbose)
144                 std::cout << std::endl;
145 }
146
147 template <class OPT>
148 void class_info<OPT>::dump_hierarchy(bool verbose)
149 {
150         identify_parents();
151
152         // Create tree nodes for all class_infos
153         std::vector<tree_node> tree;
154         for (class_info *p = first; p; p = p->next)
155                 tree.push_back(tree_node(p));
156
157         // Identify children for all nodes and find the root
158         tree_node *root = nullptr;
159         for (typename std::vector<tree_node>::iterator i = tree.begin(); i != tree.end(); ++i) {
160                 class_info *p = i->info->get_parent();
161                 if (p) {
162                         for (typename std::vector<tree_node>::iterator j = tree.begin(); j != tree.end(); ++j) {
163                                 if (j->info == p) {
164                                         j->add_child(&*i);
165                                         break;
166                                 }
167                         }
168                 } else
169                         root = &*i;
170         }
171
172         // Print hierarchy tree starting at the root
173         dump_tree(root, "", verbose);
174 }
175
176 template <class OPT>
177 void class_info<OPT>::identify_parents()
178 {
179         if (!parents_identified) {
180                 for (class_info *p = first; p; p = p->next) {
181                         const char *parent_name = p->options.get_parent_name();
182                         for (class_info *q = first; q; q = q->next) {
183                                 if (std::strcmp(q->options.get_name(), parent_name) == 0) {
184                                         p->parent = q;
185                                         break;
186                                 }
187                         }
188                 }
189                 parents_identified = true;
190         }
191 }
192
193 template <class OPT> class_info<OPT> *class_info<OPT>::first = nullptr;
194 template <class OPT> bool class_info<OPT>::parents_identified = false;
195
196 } // namespace GiNaC
197
198 #endif // ndef GINAC_CLASS_INFO_H