changeset 256:0080762c8983

TreeNode: support inheritance through proxies
author David Demelier <markand@malikania.fr>
date Thu, 02 Oct 2014 20:40:56 +0200
parents 8196fdb0fc48
children 60f71f245c5b
files C++/Tests/TreeNode/main.cpp C++/TreeNode.h extern/gtest/CMakeLists.txt
diffstat 3 files changed, 170 insertions(+), 31 deletions(-) [+]
line wrap: on
line diff
--- a/C++/Tests/TreeNode/main.cpp	Thu Oct 02 17:14:51 2014 +0200
+++ b/C++/Tests/TreeNode/main.cpp	Thu Oct 02 20:40:56 2014 +0200
@@ -16,6 +16,8 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
+#include <chrono>
+#include <iostream>
 #include <string>
 
 #include <gtest/gtest.h>
@@ -1349,9 +1351,98 @@
 	ASSERT_EQ(-1, root.indexOfSame(same));
 }
 
+TEST(Misc, inplaceFront)
+{
+	Object root("root");
+
+	root.pushNew("a");
+	root.pushNew("b");
+
+	ASSERT_TRUE(root.isRoot());
+	ASSERT_FALSE(root.isLeaf());
+	ASSERT_EQ(2, root.countChildren());
+	ASSERT_EQ("b", root[0].name());
+	ASSERT_EQ("a", root[1].name());
+}
+
+TEST(Misc, inplaceBack)
+{
+	Object root("root");
+
+	root.appendNew("a");
+	root.appendNew("b");
+
+	ASSERT_TRUE(root.isRoot());
+	ASSERT_FALSE(root.isLeaf());
+	ASSERT_EQ(2, root.countChildren());
+	ASSERT_EQ("a", root[0].name());
+	ASSERT_EQ("b", root[1].name());
+}
+
+/* --------------------------------------------------------
+ * Test inheritance
+ * -------------------------------------------------------- */
+
+class Animal : public TreeNode<Animal> {
+public:
+	virtual ~Animal() = default;
+	virtual std::string noise() const
+	{
+		return "standard";
+	}
+};
+
+class Cat final : public Animal {
+public:
+	std::string noise() const override
+	{
+		return "miaou";
+	}
+};
+
+class Dog final : public Animal {
+public:
+	std::string noise() const override
+	{
+		return "waouf";
+	}
+};
+
+TEST(Inheritance, basic)
+{
+	Animal root;
+
+	root.append(Animal());
+	root.append(Cat());
+	root.append(Dog());
+
+	ASSERT_EQ("standard", root[0].noise());
+	ASSERT_EQ("miaou", root[1].noise());
+	ASSERT_EQ("waouf", root[2].noise());
+}
+
+TEST(Inheritance, copy)
+{
+	Animal root;
+
+	root.append(Animal());
+	root.append(Cat());
+	root.append(Dog());
+
+	ASSERT_EQ("standard", root[0].noise());
+	ASSERT_EQ("miaou", root[1].noise());
+	ASSERT_EQ("waouf", root[2].noise());
+
+	Animal copy(root);
+
+	ASSERT_EQ("standard", copy[0].noise());
+	ASSERT_EQ("miaou", copy[1].noise());
+	ASSERT_EQ("waouf", copy[2].noise());
+}
+
 int main(int argc, char **argv)
 {
 	testing::InitGoogleTest(&argc, argv);
 
 	return RUN_ALL_TESTS();
-}
\ No newline at end of file
+}
--- a/C++/TreeNode.h	Thu Oct 02 17:14:51 2014 +0200
+++ b/C++/TreeNode.h	Thu Oct 02 20:40:56 2014 +0200
@@ -28,6 +28,7 @@
 #include <deque>
 #include <memory>
 #include <stdexcept>
+#include <type_traits>
 
 namespace {
 
@@ -77,7 +78,37 @@
 template <typename T>
 class TreeNode {
 private:
-	using Container	= std::deque<std::unique_ptr<T>>;
+	struct Object {
+		std::unique_ptr<T> value;
+
+		virtual std::unique_ptr<Object> clone() const = 0;
+	};
+
+	template <typename U>
+	struct Proxy final : public Object {
+		inline Proxy(U &&u)
+		{
+			this->value = std::make_unique<U>(std::move(u));
+		}
+
+		inline Proxy(const U &u)
+		{
+			this->value = std::make_unique<U>(u);
+		}
+
+		template <typename... Args>
+		inline Proxy(Args&&... args)
+		{
+			this->value = std::make_unique<U>(std::forward<Args>(args)...);
+		}
+
+		std::unique_ptr<Object> clone() const override
+		{
+			return std::make_unique<Proxy<U>>(static_cast<const U &>(*this->value));
+		}	
+	};
+
+	using Container	= std::deque<std::unique_ptr<Object>>;
 
 	TreeNode	*m_parent { nullptr };
 	Container	 m_children;
@@ -89,6 +120,11 @@
 	TreeNode() = default;
 
 	/**
+	 * Default destructor.
+	 */
+	virtual ~TreeNode() = default;
+
+	/**
 	 * Copy constructor.
 	 *
 	 * @param other the other node
@@ -97,8 +133,8 @@
 		: 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;
+			m_children.push_back(c->clone());
+			m_children.back()->value->m_parent = this;
 		}
 	}
 
@@ -113,7 +149,7 @@
 	{
 		// Update children to update to *this
 		for (auto &c : m_children)
-			c->m_parent = this;
+			c->value->m_parent = this;
 
 		other.m_children.clear();
 	}
@@ -128,8 +164,8 @@
 		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;
+			m_children.push_back(c->clone());
+			m_children.back()->value->m_parent = this;
 		}
 
 		return *this;
@@ -146,7 +182,7 @@
 
 		// Update children to update to *this
 		for (auto &c : m_children)
-			c->m_parent = this;
+			c->value->m_parent = this;
 
 		other.m_children.clear();
 
@@ -158,10 +194,11 @@
 	 *
 	 * @param child the children
 	 */
-	void push(const T &child)
+	template <typename Value>
+	inline void push(const Value &child)
 	{
-		m_children.push_front(std::make_unique<T>(child));
-		m_children.front()->m_parent = this;
+		m_children.push_front(std::make_unique<Proxy<Value>>(child));
+		m_children.front()->value->m_parent = this;
 	}
 
 	/**
@@ -169,10 +206,13 @@
 	 *
 	 * @param child the children
 	 */
-	void push(T &&child)
+	template <typename Value>
+	inline void push(Value &&child, typename std::enable_if<std::is_rvalue_reference<Value &&>::value>::type * = nullptr)
 	{
-		m_children.push_front(std::make_unique<T>(std::move(child)));
-		m_children.front()->m_parent = this;
+		using Type = typename std::decay<Value>::type;
+
+		m_children.push_front(std::make_unique<Proxy<Type>>(std::move(child)));
+		m_children.front()->value->m_parent = this;
 	}
 
 	/**
@@ -183,8 +223,8 @@
 	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;
+		m_children.emplace_front(std::make_unique<Proxy<T>>(std::forward<Args>(args)...));
+		m_children.front()->value->m_parent = this;
 	}
 
 	/**
@@ -192,10 +232,11 @@
 	 *
 	 * @param child the children
 	 */
-	void append(const T &child)
+	template <typename Value>
+	inline void append(const Value &child)
 	{
-		m_children.push_back(std::make_unique<T>(child));
-		m_children.back()->m_parent = this;
+		m_children.push_back(std::make_unique<Proxy<Value>>(child));
+		m_children.back()->value->m_parent = this;
 	}
 
 	/**
@@ -203,10 +244,13 @@
 	 *
 	 * @param child the children
 	 */
-	void append(T &&child)
+	template <typename Value>
+	inline void append(Value &&child, typename std::enable_if<std::is_rvalue_reference<Value &&>::value>::type * = nullptr)
 	{
-		m_children.push_back(std::make_unique<T>(std::move(child)));
-		m_children.back()->m_parent = this;
+		using Type = typename std::decay<Value>::type;
+
+		m_children.push_back(std::make_unique<Proxy<Type>>(std::move(child)));
+		m_children.back()->value->m_parent = this;
 	}
 
 	/**
@@ -217,8 +261,8 @@
 	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;
+		m_children.emplace_back(std::make_unique<Proxy<T>>(std::forward<Args>(args)...));
+		m_children.back()->value->m_parent = this;
 	}
 
 	/**
@@ -303,7 +347,7 @@
 	void remove(T &value)
 	{
 		m_children.erase(std::remove_if(m_children.begin(), m_children.end(), [&] (auto &p) {
-			return p.get() == &value;
+			return p->value.get() == &value;
 		}), m_children.end());
 	}
 
@@ -317,7 +361,7 @@
 	void removeSame(const Value &value, typename std::enable_if<TypeTraits<Value>::equalityComparable>::type * = nullptr)
 	{
 		m_children.erase(std::remove_if(m_children.begin(), m_children.end(), [&] (auto &p) {
-			return *p == value;
+			return *p->value == value;
 		}), m_children.end());
 	}
 
@@ -339,7 +383,7 @@
 	int indexOf(const T &child) const
 	{
 		for (unsigned i = 0; i < m_children.size(); ++i)
-			if (m_children[i].get() == &child)
+			if (m_children[i]->value.get() == &child)
 				return i;
 
 		return -1;
@@ -356,7 +400,7 @@
 	int indexOfSame(const Value &value, typename std::enable_if<TypeTraits<Value>::equalityComparable>::type * = nullptr)
 	{
 		for (unsigned i = 0; i < m_children.size(); ++i)
-			if (*m_children[i] == value)
+			if (*m_children[i]->value == value)
 				return i;
 
 		return -1;
@@ -371,7 +415,7 @@
 	 */
 	T &operator[](int index)
 	{
-		return static_cast<T &>(*m_children.at(index));
+		return static_cast<T &>(*m_children.at(index)->value);
 	}
 
 	/**
@@ -383,8 +427,8 @@
 	 */
 	const T &operator[](int index) const
 	{
-		return static_cast<const T &>(*m_children.at(index));
+		return static_cast<const T &>(*m_children.at(index)->value);
 	}
 };
 
-#endif // !_TREE_NODE_H_
\ No newline at end of file
+#endif // !_TREE_NODE_H_
--- a/extern/gtest/CMakeLists.txt	Thu Oct 02 17:14:51 2014 +0200
+++ b/extern/gtest/CMakeLists.txt	Thu Oct 02 20:40:56 2014 +0200
@@ -27,3 +27,7 @@
 	PRIVATE
 		${GoogleTest_SOURCE_DIR}
 )
+
+if (NOT WIN32)
+	target_link_libraries(gtest -pthread)
+endif ()