From 30ccb2e6952a0a71f93fe62392f950e222dd8736 Mon Sep 17 00:00:00 2001 From: Dominik Meyer Date: Wed, 3 Apr 2019 21:21:22 +0200 Subject: [PATCH] ADD: initial source commit --- .gitignore | 1 + CMakeLists.txt | 75 +++++++++ include/Tree/tree.hpp | 352 +++++++++++++++++++++++++++++++++++++++ src/Tree/tree.cpp | 117 +++++++++++++ tests/test_add_child.cpp | 114 +++++++++++++ tests/test_base.cpp | 48 ++++++ 6 files changed, 707 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 include/Tree/tree.hpp create mode 100644 src/Tree/tree.cpp create mode 100644 tests/test_add_child.cpp create mode 100644 tests/test_base.cpp diff --git a/.gitignore b/.gitignore index b1d6e7b..1bd5d98 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ build release debug +src/config.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..0bc606e --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,75 @@ +cmake_minimum_required (VERSION 3.1 FATAL_ERROR) +project (libtree++ VERSION 0.1.0 LANGUAGES CXX) +set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake/Modules) +include(GNUInstallDirs) +enable_testing() + +#cmake options for the project +option(DOXYGEN "Also build the doxygen documentation" OFF) + + +# set the required c++ standard +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +include(CTest) +find_package(Git) + +INCLUDE(ProcessDOXYGEN) +INCLUDE(ProcessGIT) +INCLUDE(add_my_test) + +IF(CMAKE_BUILD_TYPE MATCHES DEBUG) + set(DEBUG 1) +ENDIF(CMAKE_BUILD_TYPE MATCHES DEBUG) + +configure_file(${PROJECT_SOURCE_DIR}/src/config.hpp.in ${PROJECT_SOURCE_DIR}/src/config.hpp @ONLY) + + +set(generated "${PROJECT_SOURCE_DIR}/src/config.hpp") + +add_custom_target(clean-generated COMMAND rm -f ${generated}) + +add_library(tree STATIC + src/config.hpp + include/Tree/tree.hpp + src/Tree/tree.cpp +) + +target_include_directories(tree PUBLIC + $ + $ + PRIVATE src +) + + +IF(CMAKE_BUILD_TYPE MATCHES DEBUG) + target_compile_options(tree PUBLIC -Wall -g -O0 ) +ELSE() + target_compile_options(tree PUBLIC -O4) + target_compile_options(tree PUBLIC -O4) +ENDIF() + +# +# Everything TEST related +# + +add_my_test(TEST test_base + SOURCES tests/test_base.cpp + LIBS tree + ) + +add_my_test(TEST test_add_child + SOURCES tests/test_add_child.cpp + LIBS tree + ) + +get_property(_mytests GLOBAL PROPERTY _mytests) +FOREACH( _test ${_mytests}) + +IF(CMAKE_BUILD_TYPE MATCHES DEBUG) + target_compile_options(${_test} PUBLIC -Wall -g -O0) +ELSE() + target_compile_options(${_test} PUBLIC -O4) +ENDIF() +ENDFOREACH() diff --git a/include/Tree/tree.hpp b/include/Tree/tree.hpp new file mode 100644 index 0000000..a97a221 --- /dev/null +++ b/include/Tree/tree.hpp @@ -0,0 +1,352 @@ +#ifndef __TREE_BASENODE_HPP__ +#define __TREE_BASENODE_HPP__ +/** +* @file +* @brief header file for the libtree++ library +* @author Dominik Meyer +* @date 2019-03-03 +* @copyright 2019 GPLv2 by Dominik Meyer +*/ +/* +Copyright (C) 2019 Dominik Meyer + +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 3 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, see . +*/ + + + + + + +#include +#include +#include +#include + +/** +* @brief main namespace for the libtree++ library +*/ +namespace Tree +{ + /** + * @brief base node for all nodes within the tree, implementing base tree methods + */ + class BaseNode : public std::enable_shared_from_this + { + private: + /// the parent node of a node ;) + std::shared_ptr parent; + + /// all child nodes of a node + std::list> children; + + public: + /** + * @brief print the tree starting with the current node to stdout + * + * @param depth - used for indenting the tree + */ + void printTree(const uint32_t depth); + + /** + * @brief print the tree starting with the current node to stdout + * + * just calls printTree(0) + */ + void printTree() { printTree(0); } + + /** + * @brief returns a string representing this node + * + * this method has to be implemented by all nodes extending this class + * + * @return std::string representing this node + */ + virtual const std::string toString() const = 0; + + /** + * @brief returns the type of this node as a string + * + * this method has to be implemented by all nodes extending this class + * + * @return std::string representing the type of this node + */ + virtual const std::string type() const = 0; + + /** + * @brief returns the base of this node as a string + * + * this method has to be implemented by all nodes extending this class + * + * a base is some kind of way to group nodes belonging to a certain topic + * + * @return std::string representing the type of this node + */ + virtual const std::string base() const = 0; + + /** + * @brief return the parent node of the node + * + * @return nullptr - no parent is set, therefore, it should be a root node + * @return a std::shared_ptr to the the parent node + */ + const std::shared_ptr getParent() const { return parent;} + + /** + * @brief set the parent node of this node + * + * @param p - the parent node as a shared_ptr + */ + void setParent(const std::shared_ptr &p) {parent=p;} + + /** + * @brief returns an iterator to the beginning of the list of children + * + * @return iterator to the beginning of the list of children + */ + std::list>::iterator getChildrenBegin() {return children.begin();} + + /** + * @brief returns an iterator to the end of the list of children + * + * @return iterator to the end of the list of children + */ + std::list>::iterator getChildrenEnd() {return children.end();} + + /** + * @brief returns the nr of direct children + * + * @return the nr of direct children + */ + const unsigned int getNrDirectChildren() const { return children.size(); } + + /** + * @brief returns the nr of all children + * + * @return the nr of all children + */ + const unsigned int getNrChildren() const; + + + /** + * @brief deletes all the children and their children and ... from the tree + */ + void deleteChildren(); + + + /** + * @brief deletes the given node and all its children + * + * @param c - the node to delete + * + */ + void deleteChild(const std::shared_ptr &c); + + /** + * @brief remove the given node from the children of the node + * + * remove is not delete. The node gets just disconnected from the tree + * + * @param c - the node to remove from the nodes list of children + * + */ + void removeChild(const std::shared_ptr &c); + + /** + * @brief adds a child to the node which is already a shared_ptr + * + * @param c - a shared_ptr to a BaseNode + */ + void addChild(const std::shared_ptr &c) {c->setParent(shared_from_this()); children.push_back(c); } + + /** + * @brief adds a container of nodes to the list of children + * + * @param begin - iterator to the beginning of the container of objects of type std::shared_ptr + * @param end - iterator to the end of the container of objects of type std::shared_ptr + */ + template + void addChildren(Iter begin, Iter end) { std::copy( begin, end, std::back_inserter( children ) );} + + + /** + * @brief finds the next node in the tree of a given type + * + * @param t - the type of the node as a string + * + * @return nullptr - a node of that type could not be found + * @return the found node as a shared_ptr + */ + std::shared_ptr findNext(const std::string &t); + + /** + * @brief finds all nodes of a given type + * + * @param type - the type of the node as a string + * @param c - a container for holding all found nodes, has to be holding objects of type std::shared_ptr + */ + template + void findAll(const std::string &type, Container &c); + + /** + * @brief finds all nodes of a given base + * + * @param base - the base type of the node as a string + * @param c - a container for holding all found nodes, has to be holding objects of type std::shared_ptr + */ + template + void findAllBase(const std::string &base, Container &c); + + + /** + * @brief checks if the given node is a direct child of this node + * + * @param n - shared_ptr of the node to check + * + * @return true - the node is a direct child + * @return false - the node is not a direct child + */ + const bool isDirectChild(std::shared_ptr &n) const; + + + /** + * @brief checks if the given node is a child of this node + * + * @param n - shared_ptr of the node to check + * + * @return true - the node is a child + * @return false - the node is not a child + */ + const bool isChild(std::shared_ptr &n) const; + + + /** + * @brief replaces this node within the tree with the given one + * + * this means moving all children to the given node and setting all of their parents + * to the new node + * + * @param n - the new node to replace the current one with + */ + void replace(std::shared_ptr n); + + /** + * @brief overriding the to string operator + * + * used to convert the object on the fly to a string + * + * @return a string representing this object + */ + operator std::string() const { return toString(); } + + }; + + /** + * @brief stream operator override + * + * make it possible to use nodes within output streams + * + * @param Str - the output stream to stream to ;) + * @param v - the node which should be streamed to Str + * + * @return the outstream returned + */ + + inline std::ostream & operator<<(std::ostream & Str, const Tree::BaseNode &v) { + Str << v.toString(); + return Str; + } + + /* + * Some basic Node Types + */ + + /** + * @brief class representing the Root of a Tree + */ + class TreeRoot : public Tree::BaseNode + { + public: + /** + * @brief returns the type of this node as a string + * + * this method has to be implemented by all nodes extending this class + * + * @return std::string representing the type of this node + */ + const std::string type() const { return "Tree::TreeRoot";} + + /** + * @brief returns the base of this node as a string + * + * this method has to be implemented by all nodes extending this class + * + * a base is some kind of way to group nodes belonging to a certain topic + * + * @return std::string representing the type of this node + */ + const std::string base() const { return "Tree::Base";} + + + /** + * @brief returns a string representing this node + * + * this method has to be implemented by all nodes extending this class + * + * @return std::string representing this node + */ + virtual const std::string toString() const { return type();}; + }; + + /** + * @brief class representing a temporary node within the tree + */ + class TempNode : public Tree::BaseNode + { + public: + /** + * @brief returns the type of this node as a string + * + * this method has to be implemented by all nodes extending this class + * + * @return std::string representing the type of this node + */ + const std::string type() const { return "Tree::TempNode";} + + /** + * @brief returns the base of this node as a string + * + * this method has to be implemented by all nodes extending this class + * + * a base is some kind of way to group nodes belonging to a certain topic + * + * @return std::string representing the type of this node + */ + const std::string base() const { return "Tree::Base";} + + + /** + * @brief returns a string representing this node + * + * this method has to be implemented by all nodes extending this class + * + * @return std::string representing this node + */ + virtual const std::string toString() const { return type();}; + }; + +}; // namespace Tree + + +#endif diff --git a/src/Tree/tree.cpp b/src/Tree/tree.cpp new file mode 100644 index 0000000..d946a61 --- /dev/null +++ b/src/Tree/tree.cpp @@ -0,0 +1,117 @@ +/* +Copyright (C) 2019 Dominik Meyer + +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 3 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, see . +*/ + +#include +#include +#include + + +std::shared_ptr Tree::BaseNode::findNext(const std::string &t) +{ + std::list>::iterator childIT; + std::shared_ptr temp; + + if (type() == t ) + { + return shared_from_this(); + } + + for(childIT=children.begin(); childIT != children.end(); ++childIT) + { + temp=(*childIT)->findNext(t); + if (temp != nullptr) + { + return temp; + } + } + + return nullptr; +} + + + +/* +* return the number of children +*/ +const unsigned int Tree::BaseNode::getNrChildren() const +{ + std::list>::const_iterator childIT; + unsigned int nr=children.size(); + + for(childIT=children.begin(); childIT != children.end(); ++childIT) + { + nr += (*childIT)->getNrChildren(); + } + + return nr; +} + +/* +* print the tree and indent according to depth +*/ +void Tree::BaseNode::printTree(const uint32_t depth) +{ + for (uint32_t i=0; i< depth; i++) + { + std::cout << " "; + } + + std::cout << toString() << std::endl; + + std::list>::iterator childIT; + + for(childIT=getChildrenBegin(); childIT != getChildrenEnd(); ++childIT) + { + (*childIT)->printTree(depth+1); + } + +} + + +void Tree::BaseNode::removeChild(const std::shared_ptr &c) +{ + std::list>::iterator it; + it=std::find(children.begin(), children.end(),c); + children.erase(it); +} + +void Tree::BaseNode::deleteChild(const std::shared_ptr &c) +{ + std::list>::iterator it; + it=std::find(children.begin(), children.end(),c); + children.erase(it); + + c->setParent(nullptr); +} + + +/* +* delete all children +*/ +void Tree::BaseNode::deleteChildren() +{ + std::list>::iterator it; + + for(it=children.begin(); it!=children.end(); ++it) + { + if ((*it) != nullptr ) + { + deleteChild((*it)); + } + } + +} diff --git a/tests/test_add_child.cpp b/tests/test_add_child.cpp new file mode 100644 index 0000000..1b7881b --- /dev/null +++ b/tests/test_add_child.cpp @@ -0,0 +1,114 @@ +/* +Copyright (C) 2019 Dominik Meyer + +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 3 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, see . +*/ + +#include +#include +#include +#include +#include +#include + +int main() +{ + bool error=false; + + + std::vector> nodes; + + // create a root node of the tree + std::shared_ptr t=std::make_shared(); + + // create one child + std::shared_ptr temp0=std::make_shared(); + + // add child to tree + t->addChild(temp0); + + if (t->getNrDirectChildren() != 1 || t->getNrChildren()!=1) + { + error=true; + std::cerr << std::string(__FILE__) + ":" + std::to_string(__LINE__) + " NR of children do not match added children" << std::endl; + } + + // create another child + std::shared_ptr temp1=std::make_shared(); + + // add child to tree + temp0->addChild(temp1); + + if (t->getNrDirectChildren() != 1 || t->getNrChildren()!=2 || temp0->getNrDirectChildren()!=1 || temp0->getNrChildren() != 1) + { + error=true; + std::cerr << std::string(__FILE__) + ":" + std::to_string(__LINE__) + " NR of children do not match added children" << std::endl; + } + + // remove a node from the list of children of root node + t->removeChild(temp0); + + if (t->getNrDirectChildren() != 0 || t->getNrChildren()!= 0 || temp0->getNrDirectChildren()!=1 || temp0->getNrChildren() != 1) + { + error=true; + std::cerr << std::string(__FILE__) + ":" + std::to_string(__LINE__) + " NR of children do not match added children" << std::endl; + } + + // readd the removed node + t->addChild(temp0); + + if (t->getNrDirectChildren() != 1 || t->getNrChildren()!=2 || temp0->getNrDirectChildren()!=1 || temp0->getNrChildren() != 1) + { + error=true; + std::cerr << std::string(__FILE__) + ":" + std::to_string(__LINE__) + " NR of children do not match added children" << std::endl; + } + + // delete Child + temp0->getParent()->deleteChild(temp0); + + if (t->getNrDirectChildren() != 0 || t->getNrChildren()!=0 ) + { + error=true; + std::cerr << std::string(__FILE__) + ":" + std::to_string(__LINE__) + " NR of children do not match added children" << std::endl; + } + + // add a bunch of children + for(int i=0; i<20; i++) + { + std::shared_ptr temp0=std::make_shared(); + nodes.push_back(temp0); + } + + t->addChildren(nodes.begin(), nodes.end()); + + if (t->getNrDirectChildren() != 20 || t->getNrChildren()!=20 ) + { + error=true; + std::cerr << std::string(__FILE__) + ":" + std::to_string(__LINE__) + " NR of children do not match added children" << std::endl; + } + + + std::cout << "--------------" << std::endl; + t->printTree(); + std::cout << "--------------" << std::endl; + + if (error) + { + std::cerr << "Test failed" << std::endl; + return -1; + } + + std::cerr << "Test OK" << std::endl; + return 0; +} diff --git a/tests/test_base.cpp b/tests/test_base.cpp new file mode 100644 index 0000000..3c955b2 --- /dev/null +++ b/tests/test_base.cpp @@ -0,0 +1,48 @@ +#include +#include +#include + + + +int main() +{ + bool error=false; + std::shared_ptr t=std::make_shared(); + + // first test if the parent node is a nullptr for root node + if ( t->getParent() != nullptr ) + { + std::cerr << "Failed Test: Root node parent is not null" << std::endl; + error=true; + } + + /* + * create a temp node and add to root + */ + std::shared_ptr temp0=std::make_shared(); + + t->addChild(temp0); + + // check if the parent node of temp0 has been set correctly + if (temp0->getParent() != t) + { + std::cerr << "Failed Test: temp0 node parent is null" << std::endl; + error=true; + } + + // check if the number of direct children of t is 1 + if (t->getNrDirectChildren() != 1) + { + std::cerr << "Failed Test: nr of direct children is not 1" << std::endl; + error=true; + } + + + if (error) + { + return -1; + } + + std::cerr << "Test OK" << std::endl; + return 0; +}