From 89518f294dc3dacdb40795a3512170e3850ace97 Mon Sep 17 00:00:00 2001
From: Jan Travnicek <Jan.Travnicek@fit.cvut.cz>
Date: Fri, 7 Sep 2018 21:01:09 +0200
Subject: [PATCH] add callback acceptor to variant

---
 alib2std/src/extensions/container/variant.hpp | 242 ++++++++++++------
 .../extensions/container/VariantTest.cpp      |   7 +
 .../extensions/container/VariantTest.h        |   5 +
 3 files changed, 177 insertions(+), 77 deletions(-)

diff --git a/alib2std/src/extensions/container/variant.hpp b/alib2std/src/extensions/container/variant.hpp
index 6783fdc094..970cec350a 100644
--- a/alib2std/src/extensions/container/variant.hpp
+++ b/alib2std/src/extensions/container/variant.hpp
@@ -61,6 +61,25 @@ struct variant_helper;
  */
 template<typename F, typename... Ts>
 struct variant_helper<F, Ts...> {
+	/**
+	 * \brief
+	 * Implementation of call passing for type F (if the type F is currently stored inside the variant).
+	 *
+	 * \tparam Result the callback result
+	 * \tparam Callable type of callback to execute
+	 *
+	 * \param id the representation of type stored inside the variant
+	 * \param data storage occupied by value stored in the variant
+	 * \param callable the callback to execute
+	 */
+	template < class Result, class Callable >
+	inline static Result call ( ext::type_index id, const void * data, Callable callable ) {
+		if (id == ext::type_index ( typeid ( F ) ) )
+			return callable ( * reinterpret_cast < const F * > ( data ) );
+		else
+			return variant_helper < Ts ... >::template call < Result > ( id, data, callable );
+	}
+
 	/**
 	 * \brief
 	 * Implementation of destructor for type F (if the type F is currently stored inside the variant)
@@ -69,7 +88,7 @@ struct variant_helper<F, Ts...> {
 	 * \param data storage occupied by value stored in the variant
 	 */
 	inline static void destroy(ext::type_index id, void * data) {
-		if (id == ext::type_index ( ext::type_index ( typeid ( F ) ) ) )
+		if (id == ext::type_index ( typeid ( F ) ) )
 			reinterpret_cast<F*>(data)->~F();
 		else
 			variant_helper<Ts...>::destroy(id, data);
@@ -105,34 +124,6 @@ struct variant_helper<F, Ts...> {
 			variant_helper<Ts...>::move(old_t, old_v, new_v);
 	}
 
-	/**
-	 * \brief
-	 * Implementation of operator << for type F (if the type F is currently stored inside the variant)
-	 *
-	 * \param id the representation of type stored inside the variant
-	 * \param data storage occupied by value stored in the variant
-	 */
-	inline static void print(std::ostream& out, ext::type_index id, const void* data) {
-		if (id == ext::type_index ( typeid ( F ) ))
-			out << *reinterpret_cast<const F*>(data);
-		else
-			variant_helper<Ts...>::print(out, id, data);
-	}
-
-	/**
-	 * \brief
-	 * Implementation of to_string for type F (if the type F is currently stored inside the variant)
-	 *
-	 * \param id the representation of type stored inside the variant
-	 * \param data storage occupied by value stored in the variant
-	 */
-	inline static std::string string(ext::type_index id, const void* data) {
-		if (id == ext::type_index ( typeid ( F ) ))
-			return ext::to_string ( *reinterpret_cast<const F*>(data) );
-		else
-			return variant_helper<Ts...>::string(id, data);
-	}
-
 	/**
 	 * \brief
 	 * Implementation of three way comparison for type F (if the type F is currently stored inside the variant)
@@ -161,6 +152,25 @@ struct variant_helper<F, Ts...> {
  */
 template<typename... Ts>
 struct variant_helper<void, Ts...> {
+	/**
+	 * \brief
+	 * Implementation of call passing for type F (if the type F is currently stored inside the variant).
+	 *
+	 * \tparam Result the callback result
+	 * \tparam Callable type of callback to execute
+	 *
+	 * \param id the representation of type stored inside the variant
+	 * \param data storage occupied by value stored in the variant
+	 * \param callable the callback to execute
+	 */
+	template < class Result, class Callable >
+	inline static Result call ( ext::type_index id, const void * data, Callable callable ) {
+		if (id == ext::type_index ( typeid ( void ) ) )
+			return callable ( );
+		else
+			return variant_helper < Ts ... >::template call < Result > ( id, data, callable );
+	}
+
 	/**
 	 * \brief
 	 * Implementation of destructor for void type (if the void type is currently stored inside the variant)
@@ -199,34 +209,6 @@ struct variant_helper<void, Ts...> {
 			variant_helper<Ts...>::move(old_t, old_v, new_v);
 	}
 
-	/**
-	 * \brief
-	 * Implementation of operator << for void type (if the void type is currently stored inside the variant)
-	 *
-	 * \param id the representation of type stored inside the variant
-	 * \param data storage occupied by value stored in the variant
-	 */
-	inline static void print(std::ostream& out, ext::type_index id, const void* data) {
-		if (id == ext::type_index ( typeid ( void ) ))
-			out << "void";
-		else
-			variant_helper<Ts...>::print(out, id, data);
-	}
-
-	/**
-	 * \brief
-	 * Implementation of to_string for void type (if the void type is currently stored inside the variant)
-	 *
-	 * \param id the representation of type stored inside the variant
-	 * \param data storage occupied by value stored in the variant
-	 */
-	inline static std::string string(ext::type_index id, const void* data) {
-		if (id == ext::type_index ( typeid ( void ) ))
-			return "void";
-		else
-			return variant_helper<Ts...>::string(id, data);
-	}
-
 	/**
 	 * \brief
 	 * Implementation of three way comparison for void type (if the void type is currently stored inside the variant)
@@ -254,45 +236,38 @@ template < > struct variant_helper < > {
 
 	/**
 	 * \brief
-	 * Destructor helper when the pack of types of the variant was depleted.
-	 *
-	 * Does nothing.
-	 */
-	inline static void destroy(ext::type_index, void *) { }
-
-	/**
-	 * \brief
-	 * Copy constructor helper when the pack of types of the variant was depleted.
+	 * Implementation of call passing for type F (if the type F is currently stored inside the variant).
 	 *
 	 * Does nothing.
 	 */
-	inline static void copy(ext::type_index, const void *, void *) { }
+	template < class Result, class Callable >
+	inline static Result call ( ext::type_index, const void *, Callable ) {
+		throw std::logic_error ( "Out of types of variant." );
+	}
 
 	/**
 	 * \brief
-	 * Move constructor helper when the pack of types of the variant was depleted.
+	 * Destructor helper when the pack of types of the variant was depleted.
 	 *
 	 * Does nothing.
 	 */
-	inline static void move(ext::type_index, void *, void *) {  }
+	inline static void destroy(ext::type_index, void *) { }
 
 	/**
 	 * \brief
-	 * Print to stream helper when the pack of types of the variant was depleted.
+	 * Copy constructor helper when the pack of types of the variant was depleted.
 	 *
 	 * Does nothing.
 	 */
-	inline static void print(std::ostream&, ext::type_index, const void *) { }
+	inline static void copy(ext::type_index, const void *, void *) { }
 
 	/**
 	 * \brief
-	 * To string helper when the pack of types of the variant was depleted.
+	 * Move constructor helper when the pack of types of the variant was depleted.
 	 *
 	 * Does nothing.
-	 *
-	 * \return empty string
 	 */
-	inline static std::string string(ext::type_index, const void *) { return ""; }
+	inline static void move(ext::type_index, void *, void *) {  }
 
 	/**
 	 * \brief
@@ -457,6 +432,71 @@ class variant : public variant_base < max ( SizeOf < Ts >::size ... ), max ( Ali
 	 */
 	using helper_t = variant_helper<Ts...>;
 
+	/**
+	 * Internal variant to string callback implementation.
+	 */
+	class VariantToString {
+	public:
+		/**
+		 * The callback for types different from void
+		 *
+		 * \tparam T the accepted tye
+		 *
+		 * \param value the accepted value
+		 *
+		 * \return string representation of the value
+		 */
+		template < class T >
+		std::string operator ( ) ( const T & value ) const {
+			return ext::to_string ( value );
+		}
+
+		/**
+		 * The callback for void type
+		 *
+		 * \return string "void"
+		 */
+		std::string operator ( ) ( ) const {
+			return "void";
+		}
+	};
+
+	/**
+	 * Internal variant to ostream callback implementation.
+	 */
+	class VariantToStream {
+		/**
+		 * The stream to print to.
+		 */
+		std::ostream & m_out;
+	public:
+		/**
+		 * Constructor of the variant to stream callback.
+		 *
+		 * \param out the output stream to use in function call operators.
+		 */
+		VariantToStream ( std::ostream & out ) : m_out ( out ) {
+		}
+
+		/**
+		 * The callback for types different from void
+		 *
+		 * \tparam T the accepted tye
+		 *
+		 * \param value the accepted value
+		 */
+		template < class T >
+		void operator ( ) ( const T & value ) {
+			m_out << value;
+		}
+
+		/**
+		 * The callback for void type, printing "void" to the stream
+		 */
+		void operator ( ) ( ) {
+			m_out << "void";
+		}
+	};
 public:
 	/**
 	 * \brief
@@ -720,6 +760,22 @@ public:
 		helper_t::destroy(this->type_id, &this->data);
 	}
 
+	/**
+	 * \brief
+	 * Callback executor on current variant value.
+	 *
+	 * \tparam Result the uniform result type
+	 * \tparam Callable type of callable able to accept any variant type.
+	 *
+	 * \param callable object of callable type able to accept any variant type via function call operator. The possible void type is represented by call with no parameters
+	 *
+	 * \return the result of the callable on the current variable type
+	 */
+	template < class Result, class Callable >
+	Result call ( Callable callable ) const {
+		return helper_t::template call < Result > ( this->type_id, & this->data, callable );
+	}
+
 	/**
 	 * \brief
 	 * Operator to print the variant to the output stream.
@@ -730,7 +786,7 @@ public:
 	 * \return the output stream from the \p out
 	 */
 	friend std::ostream & operator << ( std::ostream & out, const variant < Ts ... > & obj ) {
-		helper_t::print(out, obj.type_id, &obj.data);
+		obj.call < void > ( VariantToStream ( out ) );
 		return out;
 	}
 
@@ -741,7 +797,7 @@ public:
 	 * \return string representation of the variant
 	 */
 	explicit operator std::string() const {
-		return helper_t::string(this->type_id, &this->data);
+		return this->call < std::string > ( VariantToString ( ) );
 	}
 
 	/**
@@ -969,6 +1025,22 @@ public:
 		return std::move ( m_value );
 	}
 
+	/**
+	 * \brief
+	 * Callback executor on current variant value.
+	 *
+	 * \tparam Result the uniform result type
+	 * \tparam Callable type of callable able to accept any variant type.
+	 *
+	 * \param callable object of callable type able to accept any variant type via function call operator. The possible void type is represented by call with no parameters
+	 *
+	 * \return the result of the callable on the current variable type
+	 */
+	template < class Result, class Callable >
+	Result call ( Callable callable ) const {
+		return callable ( m_value );
+	}
+
 	/**
 	 * \brief
 	 * Operator to print the variant to the output stream.
@@ -1126,6 +1198,22 @@ public:
 	typename std::enable_if < std::is_same < T, void >::value, void >::type set ( ) {
 	}
 
+	/**
+	 * \brief
+	 * Callback executor on current variant value.
+	 *
+	 * \tparam Result the uniform result type
+	 * \tparam Callable type of callable able to accept any variant type.
+	 *
+	 * \param callable object of callable type able to accept any variant type via function call operator. The possible void type is represented by call with no parameters
+	 *
+	 * \return the result of the callable on the current variable type
+	 */
+	template < class Result, class Callable >
+	Result call ( Callable callable ) const {
+		return callable ( );
+	}
+
 	/**
 	 * \brief
 	 * Operator to print the variant to the output stream.
diff --git a/alib2std/test-src/extensions/container/VariantTest.cpp b/alib2std/test-src/extensions/container/VariantTest.cpp
index 01b6123957..8fea1b358f 100644
--- a/alib2std/test-src/extensions/container/VariantTest.cpp
+++ b/alib2std/test-src/extensions/container/VariantTest.cpp
@@ -3,6 +3,7 @@
 #include <alib/set>
 #include <alib/algorithm>
 #include <alib/map>
+#include <sstream>
 
 CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( VariantTest, "bits" );
 CPPUNIT_TEST_SUITE_REGISTRATION( VariantTest );
@@ -57,6 +58,12 @@ void VariantTest::testVariant() {
 	CPPUNIT_ASSERT( (e < d) == false );
 
 	CPPUNIT_ASSERT( (e == d) == true );
+
+	CPPUNIT_ASSERT ( ( ( std::string ) e ) == "43" );
+
+	std::stringstream ss;
+	ss << e;
+	CPPUNIT_ASSERT ( ss.str ( ) == "43" );
 }
 
 void VariantTest::testVariantSet() {
diff --git a/alib2std/test-src/extensions/container/VariantTest.h b/alib2std/test-src/extensions/container/VariantTest.h
index da2eb7ea55..d8205974e6 100644
--- a/alib2std/test-src/extensions/container/VariantTest.h
+++ b/alib2std/test-src/extensions/container/VariantTest.h
@@ -3,6 +3,7 @@
 
 #include <cppunit/extensions/HelperMacros.h>
 #include <alib/compare>
+#include <alib/string>
 
 class VariantTest : public CppUnit::TestFixture
 {
@@ -60,6 +61,10 @@ struct test {
 		out << *(other.holder);
 		return out;
 	}
+
+	operator std::string ( ) const {
+		return ext::to_string ( * holder );
+	}
 };
 
 struct test2 {
-- 
GitLab