From 9ff6d1138a11079e23ee9bfb0a258e2453c07d04 Mon Sep 17 00:00:00 2001
From: Jan Travnicek <Jan.Travnicek@fit.cvut.cz>
Date: Wed, 15 Oct 2014 22:58:40 +0200
Subject: [PATCH] add same visitor + visitor tests

---
 alib2data/src/std/visitor.hpp             | 67 ++++++++++++++++++++++-
 alib2data/test-src/std/StdVisitorTest.cpp | 66 +++++++++++++++++++++-
 alib2data/test-src/std/StdVisitorTest.h   |  4 ++
 3 files changed, 132 insertions(+), 5 deletions(-)

diff --git a/alib2data/src/std/visitor.hpp b/alib2data/src/std/visitor.hpp
index d65eecdfa8..64c7720032 100644
--- a/alib2data/src/std/visitor.hpp
+++ b/alib2data/src/std/visitor.hpp
@@ -84,13 +84,15 @@ template<> struct const_promoting_helper<>  {
 	inline static bool tryPromote(void*, const Desired&, const Base&, const TargetVisitor&) { return false; }
 };
 
+class const_promoting_visitor_base {};
+
 // Visitor template declaration
 template<typename... Types>
 class const_promoting_visitor;
 
 // specialization for single type
 template<typename T>
-class const_promoting_visitor<T> {
+class const_promoting_visitor<T> : public const_promoting_visitor_base {
 public:
 	virtual void Visit(void*, const T &, const T&) const = 0;
 
@@ -116,6 +118,48 @@ public:
 	}
 };
 
+class const_same_visitor_base {};
+
+// Visitor template declaration
+template<typename... Types>
+class const_same_visitor;
+
+// specialization for single type
+template<typename T>
+class const_same_visitor<T> : public const_same_visitor_base {
+public:
+	virtual void Visit(void*, const T &, const T&) const = 0;
+
+	template<typename R>
+	void Visit1(void* userData, const T& first, const R& second) const {
+		if(dynamic_cast<const T*>(&second)) {
+			this->Visit(userData, first, dynamic_cast<const T&>(second));
+		} else {
+			throw std::logic_error("Same visitor: Visited types are different.");
+		}
+	}
+};
+
+// specialization for multiple types
+template<typename T, typename... Types>
+class const_same_visitor<T, Types...> : public const_same_visitor<Types...> {
+public:
+	// promote the function(s) from the base class
+	using const_same_visitor<Types...>::Visit;
+	using const_same_visitor<Types...>::Visit1;
+
+	virtual void Visit(void*, const T &, const T&) const = 0;
+
+	template<typename R>
+	void Visit1(void* userData, const T& first, const R& second) const {
+		if(dynamic_cast<const T*>(&second)) {
+			this->Visit(userData, first, dynamic_cast<const T&>(second));
+		} else {
+			throw std::logic_error("Same visitor: Visited types are different.");
+		}
+	}
+};
+
 template<typename T, typename... Types>
 class acceptor_base {
 public:
@@ -125,13 +169,19 @@ public:
 
 	typedef const_promoting_visitor<Types...> const_promoting_visitor_type;
 	typedef const_promoting_helper<Types...> const_promoting_helper_type;
+
+	typedef const_same_visitor<Types...> const_same_visitor_type;
+
 	typedef T base_type;
 
+
 	virtual void Accept(void* userData, visitor<Types...>& visitor) const = 0;
 
 	virtual void Accept(void* userData, const const_visitor<Types...>& visitor) const = 0;
 
 	virtual bool Accept(void* userData, const T& other, const const_promoting_visitor<Types...>& visitor) const = 0;
+
+	virtual void Accept(void* userData, const T& other, const const_same_visitor<Types...>& visitor) const = 0;
 };
 
 template<typename Derived, typename AcceptorBase, typename Base>
@@ -140,6 +190,7 @@ public:
 	virtual void Accept(void* userData, typename AcceptorBase::visitor_type& visitor) const {
 		visitor.Visit(userData, static_cast<const Derived&>(*this));
 	}
+
 	virtual void Accept(void* userData, const typename AcceptorBase::const_visitor_type& visitor) const {
 		visitor.Visit(userData, static_cast<const Derived&>(*this));
 	}
@@ -147,10 +198,20 @@ public:
 	virtual bool Accept(void* userData, const typename AcceptorBase::base_type& other, const typename AcceptorBase::const_promoting_visitor_type& visitor) const {
 		return visitor.template Visit1<typename AcceptorBase::base_type, typename AcceptorBase::const_promoting_helper_type>(userData, static_cast<const Derived&>(*this), other);
 	}
+
+	virtual void Accept(void* userData, const typename AcceptorBase::base_type& other, const typename AcceptorBase::const_same_visitor_type& visitor) const {
+		visitor.template Visit1<typename AcceptorBase::base_type>(userData, static_cast<const Derived&>(*this), other);
+	}
 };
 
+template<class T, class R,
+		typename std::enable_if< std::is_base_of< const_same_visitor_base, R >::value >::type* = nullptr >
+void Accept(void* userData, const T& first, const T& second, const R& visitor) {
+	first.Accept(userData, second, visitor);
+}
 
-template<class T, class R>
+template<class T, class R,
+		typename std::enable_if< std::is_base_of< const_promoting_visitor_base, R >::value >::type* = nullptr >
 void Accept(void* userData, const T& first, const T& second, const R& visitor) {
 	bool res;
 	res = first.Accept(userData, second, visitor);
@@ -159,7 +220,7 @@ void Accept(void* userData, const T& first, const T& second, const R& visitor) {
 	res = second.Accept(userData, first, visitor);
 	if(res) return;
 
-	throw std::logic_error("cant promote parameters");
+	throw std::logic_error("Promoting visitor: Can't promote one parameter to type of the other.");
 }
 
 } /* namespace std */
diff --git a/alib2data/test-src/std/StdVisitorTest.cpp b/alib2data/test-src/std/StdVisitorTest.cpp
index 314c3d0e13..073c523d34 100644
--- a/alib2data/test-src/std/StdVisitorTest.cpp
+++ b/alib2data/test-src/std/StdVisitorTest.cpp
@@ -200,7 +200,27 @@ public:
 
 };
 
-class TmpVisitor : public VisitableTmpBase::const_promoting_visitor_type {
+class TmpVisitor : public VisitableTmpBase::const_visitor_type {
+	void Visit(void* userData, const Tmp1& first) const {
+		int& data = *((int*) userData);
+		data = 1;
+		std::cout << first << std::endl;
+	}
+
+	void Visit(void* userData, const Tmp2& first) const {
+		int& data = *((int*) userData);
+		data = 2;
+		std::cout << first << std::endl;
+	}
+
+	void Visit(void* userData, const Tmp3& first) const {
+		int& data = *((int*) userData);
+		data = 3;
+		std::cout << first << std::endl;
+	}
+};
+
+class TmpSameVisitor : public VisitableTmpBase::const_same_visitor_type {
 	void Visit(void* userData, const Tmp1& first, const Tmp1& second) const {
 		int& data = *((int*) userData);
 		data = 1;
@@ -220,9 +240,51 @@ class TmpVisitor : public VisitableTmpBase::const_promoting_visitor_type {
 	}
 };
 
-void StdVisitorTest::testPromoteVisitor() {
+class TmpPromotingVisitor : public VisitableTmpBase::const_promoting_visitor_type {
+	void Visit(void* userData, const Tmp1& first, const Tmp1& second) const {
+		int& data = *((int*) userData);
+		data = 1;
+		std::cout << first << " " << second << std::endl;
+	}
+
+	void Visit(void* userData, const Tmp2& first, const Tmp2& second) const {
+		int& data = *((int*) userData);
+		data = 2;
+		std::cout << first << " " << second << std::endl;
+	}
+
+	void Visit(void* userData, const Tmp3& first, const Tmp3& second) const {
+		int& data = *((int*) userData);
+		data = 3;
+		std::cout << first << " " << second << std::endl;
+	}
+};
+
+void StdVisitorTest::testVisitor() {
 	TmpVisitor visitor;
 
+	Tmp1 tmp1(2);
+
+	int a = 0;
+	tmp1.Accept((void*) &a, visitor);
+
+	CPPUNIT_ASSERT(a == 1);
+}
+
+void StdVisitorTest::testSameVisitor() {
+	TmpSameVisitor visitor;
+
+	Tmp2 tmpA(2);
+	Tmp2 tmpB(3);
+
+	int a = 0;
+	Accept((void*) &a, (TmpBase&) tmpA, (TmpBase&) tmpB, visitor);
+
+	CPPUNIT_ASSERT(a == 2);
+}
+void StdVisitorTest::testPromoteVisitor() {
+	TmpPromotingVisitor visitor;
+
 	Tmp1 tmp1(2);
 	Tmp3 tmp3("3");
 
diff --git a/alib2data/test-src/std/StdVisitorTest.h b/alib2data/test-src/std/StdVisitorTest.h
index da75d57781..bb2890cd70 100644
--- a/alib2data/test-src/std/StdVisitorTest.h
+++ b/alib2data/test-src/std/StdVisitorTest.h
@@ -7,6 +7,8 @@ class StdVisitorTest : public CppUnit::TestFixture
 {
   CPPUNIT_TEST_SUITE( StdVisitorTest );
   CPPUNIT_TEST( testPromoteVisitor );
+  CPPUNIT_TEST( testSameVisitor );
+  CPPUNIT_TEST( testVisitor );
   CPPUNIT_TEST_SUITE_END();
 
 public:
@@ -14,6 +16,8 @@ public:
   void tearDown();
 
   void testPromoteVisitor();
+  void testSameVisitor();
+  void testVisitor();
 };
 
 #endif  // VISITOR_TEST_H_
-- 
GitLab