/** @file class_info.h * * Helper templates to provide per-class information for class hierarchies. */ /* * GiNaC Copyright (C) 1999-2020 Johannes Gutenberg University Mainz, Germany * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef GINAC_CLASS_INFO_H #define GINAC_CLASS_INFO_H #include // for size_t #include #include #include #include #include #include #include namespace GiNaC { // OPT is the class that stores the actual per-class data. It must provide // get_name(), get_parent_name() and get_id() members. template class class_info { public: class_info(const OPT & o) : options(o), next(first), parent(nullptr) { first = this; parents_identified = false; } /** Get pointer to class_info of parent class (or nullptr). */ class_info *get_parent() const { identify_parents(); return parent; } /** Find class_info by name. */ static const class_info *find(const std::string &class_name); /** Dump class hierarchy to std::cout. */ static void dump_hierarchy(bool verbose = false); OPT options; private: struct tree_node { tree_node(class_info *i) : info(i) {} void add_child(tree_node *n) { children.push_back(n); } std::vector children; class_info *info; }; static void dump_tree(tree_node *n, const std::string & prefix, bool verbose); static void identify_parents(); static class_info *first; class_info *next; mutable class_info *parent; static bool parents_identified; }; template const class_info *class_info::find(const std::string &class_name) { // Use a map for faster lookup. The registered_class_info list doesn't // change at run-time, so it's sufficient to construct the map once // on the first trip through this function. typedef std::map name_map_type; static name_map_type name_map; static bool name_map_initialized = false; if (!name_map_initialized) { // Construct map const class_info *p = first; while (p) { name_map[p->options.get_name()] = p; p = p->next; } name_map_initialized = true; } typename name_map_type::const_iterator it = name_map.find(class_name); if (it == name_map.end()) throw (std::runtime_error("class '" + class_name + "' not registered")); else return it->second; } template void class_info::dump_tree(tree_node *n, const std::string & prefix, bool verbose) { std::string name = n->info->options.get_name(); std::cout << name; if (verbose) std::cout << " [ID 0x" << std::hex << std::setw(8) << std::setfill('0') << n->info->options.get_id() << std::dec << "]" << std::endl; size_t num_children = n->children.size(); if (num_children) { for (size_t i = 0; i < num_children; ++i) { if (verbose) { std::cout << prefix << " +- "; if (i == num_children - 1) dump_tree(n->children[i], prefix + " ", verbose); else dump_tree(n->children[i], prefix + " | ", verbose); } else { std::string spaces(name.size(), ' '); if (i > 0) std::cout << prefix << spaces; if (num_children == 1) std::cout << " --- "; else if (i > 0) std::cout << " +- "; else std::cout << " -+- "; if (i == num_children - 1) dump_tree(n->children[i], prefix + spaces + " ", verbose); else dump_tree(n->children[i], prefix + spaces + " | ", verbose); } } } else if (!verbose) std::cout << std::endl; } template void class_info::dump_hierarchy(bool verbose) { identify_parents(); // Create tree nodes for all class_infos std::vector tree; for (class_info *p = first; p; p = p->next) tree.push_back(tree_node(p)); // Identify children for all nodes and find the root tree_node *root = nullptr; for (typename std::vector::iterator i = tree.begin(); i != tree.end(); ++i) { class_info *p = i->info->get_parent(); if (p) { for (typename std::vector::iterator j = tree.begin(); j != tree.end(); ++j) { if (j->info == p) { j->add_child(&*i); break; } } } else root = &*i; } // Print hierarchy tree starting at the root dump_tree(root, "", verbose); } template void class_info::identify_parents() { if (!parents_identified) { for (class_info *p = first; p; p = p->next) { const char *parent_name = p->options.get_parent_name(); for (class_info *q = first; q; q = q->next) { if (std::strcmp(q->options.get_name(), parent_name) == 0) { p->parent = q; break; } } } parents_identified = true; } } template class_info *class_info::first = nullptr; template bool class_info::parents_identified = false; } // namespace GiNaC #endif // ndef GINAC_CLASS_INFO_H