view C++/TreeNode.h @ 253:a4770082e7d6

TreeNode: forget to return this
author David Demelier <markand@malikania.fr>
date Thu, 02 Oct 2014 11:45:24 +0200
parents 19bfc11f77c2
children 812dd806f803
line wrap: on
line source

/*
 * TreeNode.h -- C++11 pointer-free N-ary tree
 *
 * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr>
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#ifndef _TREE_NODE_H_
#define _TREE_NODE_H_

/**
 * @file TreeNode.h
 * @brief N-ary tree without pointers
 */

#include <deque>
#include <memory>
#include <stdexcept>

namespace {

/**
 * @class TypeTraits
 * @brief Some checks on the type
 *
 * Provides the following members depending on the type:
 *
 *	equalityComparable	- If == comparison can be performed
 */
template <typename T>
class TypeTraits {
private:
	/**
	 * @class HasEqualsTo
	 * @brief Check if the type is comparable
	 *
	 * Sets to true if the type T can is equality comparable.
	 */
	template <typename U>
	class HasEqualsTo {
	public:
		static_assert(sizeof (char) != sizeof (long), "buy a new compiler");

		template <typename Value>
		static constexpr auto check(Value *u) -> decltype(*u == *u, char(0));

		static constexpr long check(...);

		static constexpr const bool value = sizeof (check((U *)0)) == sizeof (char);
	};

public:
	static constexpr const bool equalityComparable = HasEqualsTo<T>::value;
};

} // !namespace

/**
 * @class TreeNode
 * @brief Safe C++11 N-ary tree
 *
 * This class use a std::deque as the container, it allocate object on the heap to avoid useless
 * copy and move when adding new elements.
 */
template <typename T>
class TreeNode {
private:
	using Container	= std::deque<std::unique_ptr<T>>;

	TreeNode	*m_parent { nullptr };
	Container	 m_children;

public:
	/**
	 * Default constructor.
	 */
	TreeNode() = default;

	/**
	 * Copy constructor.
	 *
	 * @param other the other node
	 */
	TreeNode(const TreeNode &other)
		: m_parent(nullptr)
	{
		for (const auto &c : other.m_children) {
			m_children.push_back(std::make_unique<T>(*c));
			m_children.back()->m_parent = this;
		}
	}

	/**
	 * Move constructor.
	 *
	 * @param other the other node
	 */
	TreeNode(TreeNode &&other)
		: m_parent(nullptr)
		, m_children(std::move(other.m_children))
	{
		// Update children to update to *this
		for (auto &c : m_children)
			c->m_parent = this;

		other.m_children.clear();
	}

	/**
	 * Copy assignment operator.
	 *
	 * @param other the other node
	 */
	TreeNode &operator=(const TreeNode &other)
	{
		m_children.clear();

		for (const auto &c : other.m_children) {
			m_children.push_back(std::make_unique<T>(*c));
			m_children.back()->m_parent = this;
		}

		return *this;
	}

	/**
	 * Move assignment operator.
	 *
	 * @param other the other node
	 */
	TreeNode &operator=(TreeNode &&other)
	{
		m_children = std::move(other.m_children);

		// Update children to update to *this
		for (auto &c : m_children)
			c->m_parent = this;

		other.m_children.clear();

		return *this;
	}

	/**
	 * Add a child node to the beginning.
	 *
	 * @param child the children
	 */
	void push(const T &child)
	{
		m_children.push_front(std::make_unique<T>(child));
		m_children.front()->m_parent = this;
	}

	/**
	 * Move to the beginning.
	 *
	 * @param child the children
	 */
	void push(T &&child)
	{
		m_children.push_front(std::make_unique<T>(std::move(child)));
		m_children.front()->m_parent = this;
	}

	/**
	 * Construct an element at the beginning in place.
	 *
	 * @param args the arguments
	 */
	template <typename... Args>
	void pushNew(Args&&... args)
	{
		m_children.emplace_front(std::make_unique<T>(std::forward<Args>(args)...));
		m_children.front()->m_parent = this;
	}

	/**
	 * Add a child node to the end
	 *
	 * @param child the children
	 */
	void append(const T &child)
	{
		m_children.push_back(std::make_unique<T>(child));
		m_children.back()->m_parent = this;
	}

	/**
	 * Move a child node to the end
	 *
	 * @param child the children
	 */
	void append(T &&child)
	{
		m_children.push_back(std::make_unique<T>(std::move(child)));
		m_children.back()->m_parent = this;
	}

	/**
	 * Construct an element at the end in place.
	 *
	 * @param args the arguments
	 */
	template <typename... Args>
	void appendNew(Args&&... args)
	{
		m_children.emplace_back(std::make_unique<T>(std::forward<Args>(args)...));
		m_children.back()->m_parent = this;
	}

	/**
	 * Count the number of children in this node.
	 *
	 * @return the number of children
	 */
	unsigned countChildren() const
	{
		return static_cast<unsigned>(m_children.size());
	}

	/**
	 * Get the parent node.
	 *
	 * @return the parent node
	 * @throw std::out_of_range if there is no parent
	 */
	T &parent()
	{
		if (!m_parent)
			throw std::out_of_range("no parent");

		return static_cast<T &>(*m_parent);
	}

	/**
	 * Get the parent node.
	 *
	 * @return the parent node
	 * @throw std::out_of_range if there is no parent
	 */
	const T &parent() const
	{
		if (!m_parent)
			throw std::out_of_range("no parent");

		return static_cast<const T &>(*m_parent);
	}

	/**
	 * Check if the node is root (no parent).
	 *
	 * @return true if root
	 */
	bool isRoot() const
	{
		return m_parent == nullptr;
	}

	/**
	 * Check if the node is leaf (no children).
	 *
	 * @return true if leaf
	 */
	bool isLeaf() const
	{
		return m_children.size() == 0;
	}

	/**
	 * Find a child in this. The type must have operator==.
	 *
	 * @param child the child
	 * @return the index or -1 if not found
	 */
	template <typename Value>
	int indexOf(const Value &child, typename std::enable_if<TypeTraits<Value>::equalityComparable>::type * = nullptr) const
	{
		for (int i = 0; i < (int)m_children.size(); ++i)
			if (*m_children[i] == child)
				return i;

		return -1;
	}

	/**
	 * Access a child.
	 *
	 * @param index the index
	 * @return the reference to the children node
	 * @throw std::out_of_range on out of bounds
	 */
	T &operator[](int index)
	{
		return static_cast<T &>(*m_children.at(index));
	}

	/**
	 * Access a child.
	 *
	 * @param index the index
	 * @return the reference to the children node
	 * @throw std::out_of_range on out of bounds
	 */
	const T &operator[](int index) const
	{
		return static_cast<const T &>(*m_children.at(index));
	}
};

#endif // !_TREE_NODE_H_