From ddb993b049b444f20e42823f09266ed808bd2f6f Mon Sep 17 00:00:00 2001
From: Jan Travnicek <Jan.Travnicek@fit.cvut.cz>
Date: Sat, 19 Aug 2017 18:35:05 +0200
Subject: [PATCH] prepare getAbstraction for qualifiers support

---
 .../src/abstraction/AlgorithmRegistry.hpp     | 151 +++++++++++++-----
 .../abstraction/common/ParamQualifiers.hpp    |  36 +++++
 2 files changed, 147 insertions(+), 40 deletions(-)
 create mode 100644 alib2common/src/abstraction/common/ParamQualifiers.hpp

diff --git a/alib2common/src/abstraction/AlgorithmRegistry.hpp b/alib2common/src/abstraction/AlgorithmRegistry.hpp
index 886ff17013..4957b35cb6 100644
--- a/alib2common/src/abstraction/AlgorithmRegistry.hpp
+++ b/alib2common/src/abstraction/AlgorithmRegistry.hpp
@@ -12,11 +12,14 @@
 #include <memory>
 #include <vector>
 #include <string>
+#include <foreach>
 
 #include <exception/CommonException.h>
 #include <abstraction/OperationAbstraction.hpp>
 #include <abstraction/CastRegistry.hpp>
 
+#include <abstraction/common/ParamQualifiers.hpp>
+
 namespace abstraction {
 
 class AlgorithmRegistry {
@@ -61,8 +64,8 @@ class AlgorithmRegistry {
 		virtual std::shared_ptr < abstraction::OperationAbstraction > getAbstraction ( ) const override;
 	};
 
-	static ext::map < std::string, ext::map < ext::vector < std::string >, std::unique_ptr < Entry > > > & getEntries ( ) {
-		static ext::map < std::string, ext::map < ext::vector < std::string >, std::unique_ptr < Entry > > > algorithmGroups;
+	static ext::map < std::string, ext::vector < std::pair < ext::vector < std::pair < std::string, ext::set < abstraction::ParamQualifiers::ParamQualifier > > >, std::shared_ptr < Entry > > > > & getEntries ( ) {
+		static ext::map < std::string, ext::vector < std::pair < ext::vector < std::pair < std::string, ext::set < abstraction::ParamQualifiers::ParamQualifier > > >, std::shared_ptr < Entry > > > > algorithmGroups;
 		return algorithmGroups;
 	};
 
@@ -76,11 +79,16 @@ public:
 	static void registerAlgorithm ( ReturnType ( * callback ) ( ParamTypes ... ), bool downcast, bool normalize ) {
 		std::string algorithm = ext::to_string < Algo > ( );
 
-		ext::vector < std::string > params;
-		( void ) std::initializer_list < int > { ( params.push_back ( ext::to_string < typename std::decay < ParamTypes >::type > ( ) ), 0 ) ... };
+		ext::vector < std::pair < std::string, ext::set < abstraction::ParamQualifiers::ParamQualifier > > > params;
+		( void ) std::initializer_list < int > { ( params.push_back ( std::make_pair ( ext::to_string < typename std::decay < ParamTypes >::type > ( ), abstraction::ParamQualifiers::paramQualifiers < ParamTypes > ( ) ) ), 0 ) ... };
+
+		auto & group = getEntries ( ) [ algorithm ];
+		for ( const std::pair < ext::vector < std::pair < std::string, ext::set < abstraction::ParamQualifiers::ParamQualifier > > >, std::shared_ptr < Entry > > & entry : group )
+			if ( entry.first == params )
+				throw exception::CommonException ( "Callback for " + algorithm + " already registered." );
 
-		if ( ! getEntries ( ) [ algorithm ].insert ( std::make_pair ( params, std::unique_ptr < Entry > ( new EntryImpl < ReturnType, ParamTypes ... > ( callback, downcast, normalize ) ) ) ).second )
-			throw ::exception::CommonException ( "Callback for " + algorithm + " already registered." );
+		auto entryValue = std::make_pair ( params, std::shared_ptr < Entry > ( new EntryImpl < ReturnType, ParamTypes ... > ( callback, downcast, normalize ) ) );
+		group.push_back ( std::move ( entryValue ) );
 	}
 
 	static std::shared_ptr < abstraction::OperationAbstraction > getAbstraction ( const std::string & name, const ext::vector < std::string > & paramTypes, bool & downcast, bool & normalize ) {
@@ -88,49 +96,100 @@ public:
 		if ( group == getEntries ( ).end ( ) )
 			throw exception::CommonException ( "Entry " + name + " not available" );
 
-		auto overload = group->second.find ( paramTypes );
-		if ( overload == group->second.end ( ) ) {
-			std::vector < std::pair < std::vector < MatchType >, std::map < ext::vector < std::string >, std::unique_ptr < Entry > >::iterator > > compatibilityData;
-			for ( auto iter = group->second.begin ( ); iter != group->second.end ( ); ++ iter ) {
-				if ( iter->first.size ( ) != paramTypes.size ( ) )
-					continue;
-
-				std::vector < MatchType > compatibilityVector;
-				for ( unsigned i = 0; i < paramTypes.size ( ); ++ i ) {
-					if ( iter->first [ i ] == paramTypes [ i ] ) {
-						compatibilityVector.push_back ( MatchType::EXACT );
-					} else if ( abstraction::CastRegistry::castAvailable ( iter->first [ i ], paramTypes [ i ] ) ) {
-						compatibilityVector.push_back ( MatchType::CAST );
-					} else {
-						compatibilityVector.push_back ( MatchType::INCOMPATIBLE );
-					}
+		auto incompatibleLambda = [ ] ( const ext::tuple < MatchType, std::string, ext::set < abstraction::ParamQualifiers::ParamQualifier > > & compatibility ) {
+			return std::get < 0 > ( compatibility ) == MatchType::INCOMPATIBLE;
+		};
+
+		auto castLambda = [ ] ( const ext::tuple < MatchType, std::string, ext::set < abstraction::ParamQualifiers::ParamQualifier > > & compatibility ) {
+			return std::get < 0 > ( compatibility ) == MatchType::CAST;
+		};
+
+		auto exactLambda = [ ] ( const ext::tuple < MatchType, std::string, ext::set < abstraction::ParamQualifiers::ParamQualifier > > & compatibility ) {
+			return std::get < 0 > ( compatibility ) == MatchType::EXACT;
+		};
+
+		// determine how one can actually map what we have ( paramTypes ) as params to what is available as overloads ( group->second )
+		std::vector < std::pair < ext::vector < ext::tuple < MatchType, std::string, ext::set < abstraction::ParamQualifiers::ParamQualifier > > >, std::shared_ptr < Entry > > > compatibilityData;
+		for ( const std::pair < ext::vector < std::pair < std::string, ext::set < abstraction::ParamQualifiers::ParamQualifier > > >, std::shared_ptr < Entry > > & entry : group->second ) {
+			if ( entry.first.size ( ) != paramTypes.size ( ) )
+				continue;
+
+			ext::vector < ext::tuple < MatchType, std::string, ext::set < abstraction::ParamQualifiers::ParamQualifier > > > compatibilityVector;
+			for ( unsigned i = 0; i < paramTypes.size ( ); ++ i ) {
+				MatchType matchType;
+				if ( entry.first [ i ].first == paramTypes [ i ] ) {
+					matchType = MatchType::EXACT;
+				} else if ( abstraction::CastRegistry::castAvailable ( entry.first [ i ].first, paramTypes [ i ] ) ) {
+					matchType = MatchType::CAST;
+				} else {
+					matchType = MatchType::INCOMPATIBLE;
 				}
 
-				if ( std::none_of ( compatibilityVector.begin(), compatibilityVector.end(), [] ( MatchType & matchType ) { return matchType == MatchType::INCOMPATIBLE; } ) ) {
-					compatibilityData.push_back ( std::make_pair ( std::move ( compatibilityVector ), iter ) );
-				}
+				compatibilityVector.push_back ( ext::make_tuple ( matchType, entry.first [ i ].first, entry.first [ i ].second ) );
+			}
+
+			// clear incompatibilities are fitered out
+			if ( std::none_of ( compatibilityVector.begin ( ), compatibilityVector.end ( ), incompatibleLambda ) )
+				compatibilityData.push_back ( std::make_pair ( std::move ( compatibilityVector ), entry.second ) );
+		}
+
+		// remaining compatible overloads are examined per parameter and the best option is remembered as overload index that achieved it
+		ext::vector < ext::set < unsigned > > winnerList;
+		for ( unsigned i = 0; i < paramTypes.size ( ); ++ i ) {
+			ext::set < unsigned > best;
+
+			unsigned overload = 0;
+			for ( const std::pair < ext::vector < ext::tuple < MatchType, std::string, ext::set < abstraction::ParamQualifiers::ParamQualifier > > >, std::shared_ptr < Entry > > & data : compatibilityData ) {
+				if ( exactLambda ( data.first [ i ] ) )
+					best.insert ( overload );
+
+				++ overload;
+			}
+
+			if ( best.size ( ) > 0 ) {
+				winnerList.push_back ( std::move ( best ) );
+				continue;
 			}
 
-			if ( compatibilityData.size ( ) == 1 ) {
-				overload = compatibilityData [ 0 ].second;
-			} else if ( compatibilityData.size ( ) > 1 ) {
-				std::stringstream ss;
-				ss << paramTypes;
+			overload = 0;
+			for ( const std::pair < ext::vector < ext::tuple < MatchType, std::string, ext::set < abstraction::ParamQualifiers::ParamQualifier > > >, std::shared_ptr < Entry > > & data : compatibilityData ) {
+				if ( castLambda ( data.first [ i ] ) )
+					best.insert ( overload );
 
-				throw exception::CommonException ( "Entry overload " + ss.str ( ) + " ambiguous." );
+				++ overload;
 			}
+
+			winnerList.push_back ( std::move ( best ) );
+		}
+
+		// intersection of everything together finds indexes which are better or of the same quality for all params over all overloads
+		ext::set < unsigned > winner { ext::sequence < unsigned > ( 0 ).begin ( ), ext::sequence < unsigned > ( compatibilityData.size ( ) ).end ( ) };
+		for ( const std::set < unsigned > & best : winnerList ) {
+			ext::set < unsigned > filtered;
+			std::set_intersection ( winner.begin ( ), winner.end ( ), best.begin ( ), best.end ( ), std::inserter ( filtered, filtered.end ( ) ) );
+			winner = std::move ( filtered );
 		}
-		if ( overload == group->second.end ( ) ) {
+
+		// if there is a sinle winner, return it
+		std::shared_ptr < Entry > best;
+		if ( winner.size ( ) == 1 ) {
+			best = compatibilityData [ * winner.begin ( ) ].second;
+		} else if ( winner.size ( ) > 1 ) {
+			std::stringstream ss;
+			ss << paramTypes;
+
+			throw exception::CommonException ( "Entry overload " + ss.str ( ) + " ambiguous." );
+		} else {
 			std::stringstream ss;
 			ss << paramTypes;
 
 			throw exception::CommonException ( "Entry overload " + ss.str ( ) + " not available." );
 		}
 
-		downcast = overload->second->getDowncast ( );
-		normalize = overload->second->getNormalize ( );
+		downcast = best->getDowncast ( );
+		normalize = best->getNormalize ( );
 
-		return overload->second->getAbstraction ( );
+		return best->getAbstraction ( );
 	}
 
 	static std::shared_ptr < abstraction::OperationAbstraction > getAbstraction ( const std::string & name, const ext::vector < std::string > & paramTypes ) {
@@ -140,7 +199,7 @@ public:
 	}
 
 	static void listGroup ( const std::string & group ) {
-		for ( const std::pair < const std::string, ext::map < ext::vector < std::string >, std::unique_ptr < Entry > > > & entry : getEntries ( ) ) {
+		for ( const std::pair < const std::string, ext::vector < std::pair < ext::vector < std::pair < std::string, ext::set < abstraction::ParamQualifiers::ParamQualifier > > >, std::shared_ptr < Entry > > > > & entry : getEntries ( ) ) {
 			if ( entry.first.find ( group ) == 0 ) //found at the begining
 				std::cout << entry.first << std::endl;
 		}
@@ -151,15 +210,27 @@ public:
 		if ( group == getEntries ( ).end ( ) )
 			throw exception::CommonException ( "Entry " + algorithm + " not available" );
 
-		for ( const std::pair < const ext::vector < std::string >, std::unique_ptr < Entry > > & overloads : group->second ) {
-			for ( const std::string & param : overloads.first ) {
-				std::cout << param << " " << std::endl;
+		for ( const std::pair < ext::vector < std::pair < std::string, ext::set < abstraction::ParamQualifiers::ParamQualifier > > >, std::shared_ptr < Entry > > & overloads : group->second ) {
+			for ( const std::pair < std::string, ext::set < abstraction::ParamQualifiers::ParamQualifier > > & param : overloads.first ) {
+				if ( param.second.count ( abstraction::ParamQualifiers::ParamQualifier::CONST ) )
+					std::cout << "const ";
+
+				std::cout << param.first;
+
+				if ( param.second.count ( abstraction::ParamQualifiers::ParamQualifier::LREF ) )
+					std::cout << " &";
+
+				if ( param.second.count ( abstraction::ParamQualifiers::ParamQualifier::RREF ) )
+					std::cout << " &";
+
+				std::cout << ", ";
 			}
+			std::cout << std::endl;
 		}
 	}
 
 	static void list ( ) {
-		for ( const std::pair < const std::string, ext::map < ext::vector < std::string >, std::unique_ptr < Entry > > > & entry : getEntries ( ) ) {
+		for ( const std::pair < const std::string, ext::vector < std::pair < ext::vector < std::pair < std::string, ext::set < abstraction::ParamQualifiers::ParamQualifier > > >, std::shared_ptr < Entry > > > > & entry : getEntries ( ) ) {
 			std::cout << entry.first << std::endl;
 		}
 	}
diff --git a/alib2common/src/abstraction/common/ParamQualifiers.hpp b/alib2common/src/abstraction/common/ParamQualifiers.hpp
new file mode 100644
index 0000000000..ab729b9dd0
--- /dev/null
+++ b/alib2common/src/abstraction/common/ParamQualifiers.hpp
@@ -0,0 +1,36 @@
+#ifndef _PARAM_QUALIFIERS_HPP_
+#define _PARAM_QUALIFIERS_HPP_
+
+#include <set>
+#include <type_traits>
+
+namespace abstraction {
+
+class ParamQualifiers {
+public:
+	enum class ParamQualifier {
+		CONST,
+		LREF,
+		RREF,
+	};
+
+	template < class Param >
+	static ext::set < ParamQualifier > paramQualifiers ( ) {
+		ext::set < ParamQualifier > res;
+
+		if ( std::is_lvalue_reference < Param >::value )
+			res.insert ( ParamQualifier::LREF );
+
+		if ( std::is_rvalue_reference < Param >::value )
+			res.insert ( ParamQualifier::RREF );
+
+		if ( std::is_const < typename std::remove_reference < Param >::type >::value )
+			res.insert ( ParamQualifier::CONST );
+
+		return res;
+	}
+};
+
+} /* namespace abstraction */
+
+#endif // _PARAM_QUALIFIERS_HPP_
-- 
GitLab