mirror of
				https://github.com/saitohirga/WSJT-X.git
				synced 2025-10-31 04:50:34 -04:00 
			
		
		
		
	
		
			
				
	
	
		
			239 lines
		
	
	
		
			6.5 KiB
		
	
	
	
		
			ReStructuredText
		
	
	
	
	
	
			
		
		
	
	
			239 lines
		
	
	
		
			6.5 KiB
		
	
	
	
		
			ReStructuredText
		
	
	
	
	
	
| ++++++++++++++++++++++++++++
 | |
|  Interoperability Revisited 
 | |
| ++++++++++++++++++++++++++++
 | |
| 
 | |
| :date: $Date$
 | |
| :copyright: Copyright Thomas Witt 2004.
 | |
| 
 | |
| .. Distributed under the Boost
 | |
| .. Software License, Version 1.0. (See accompanying
 | |
| .. file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
 | |
| 
 | |
| Problem
 | |
| =======
 | |
| 
 | |
| The current iterator_facade specification makes it unneccessarily tedious to
 | |
| implement interoperable iterators.
 | |
| 
 | |
| In the following text a simplified example of the current iterator_facade specification is used to
 | |
| illustrate the problem.
 | |
| 
 | |
| In the current specification binary operators are implemented in the following way::
 | |
| 
 | |
|   template <class Derived>
 | |
|   struct Facade
 | |
|   {
 | |
|   };
 | |
| 
 | |
|   template <class T1, T2>
 | |
|   struct is_interoperable :
 | |
|     or_< 
 | |
|          is_convertible<T1, T2>
 | |
|        , is_convertible<T2, T1>
 | |
|     > 
 | |
|   {};
 | |
| 
 | |
|   template<
 | |
|       class Derived1
 | |
|     , class Derived2
 | |
|   >
 | |
|   enable_if<is_interoperable<Derived1, Derived2>, bool> operator==(
 | |
|       Derived1 const& lhs
 | |
|     , Derived2 const& rhs
 | |
|   )
 | |
|   {
 | |
|     return static_cast<Derived1 const&>(lhs).equal_to(static_cast<Derived2 const&(rhs));
 | |
|   } 
 | |
| 
 | |
| The problem with this is that operator== always forwards to Derived1::equal_to. The net effect is that the
 | |
| following "obvious" implementation of to interoperable types does
 | |
| not quite work. ::
 | |
| 
 | |
|   struct Mutable : Facade<Mutable>
 | |
|   {
 | |
|     bool equal_to(Mutable const&);  
 | |
|   };
 | |
| 
 | |
|   struct Constant : Facade<Constant>
 | |
|   {
 | |
|     Constant();
 | |
|     Constant(Constant const&);
 | |
|     Constant(Mutable const&);
 | |
| 
 | |
|     ...
 | |
| 
 | |
|     bool equal_to(Constant const&);  
 | |
|   };
 | |
| 
 | |
|   Constant c;
 | |
|   Mutable  m;
 | |
| 
 | |
|   c == m; // ok, dispatched to Constant::equal_to
 | |
|   m == c; // !! error, dispatched to Mutable::equal_to
 | |
| 
 | |
|   Instead the following "slightly" more complicated implementation is necessary
 | |
| 
 | |
|   struct Mutable : Facade<Mutable>
 | |
|   {
 | |
|     template <class T>
 | |
|     enable_if<is_convertible<Mutable, T> || is_convertible<T, Mutable>, bool>::type equal_to(T const&);  
 | |
|   };
 | |
| 
 | |
|   struct Constant : Tag<Constant>
 | |
|   {
 | |
|     Constant();
 | |
|     Constant(Constant const&);
 | |
|     Constant(Mutable const&);
 | |
| 
 | |
|     template <class T>
 | |
|     enable_if<is_convertible<Constant, T> || is_convertible<T, Constant>, bool>::type equal_to(T const&);  
 | |
|   };
 | |
| 
 | |
| Beside the fact that the code is significantly more complex to understand and to teach there is
 | |
| a major design problem lurking here. Note that in both types equal_to is a function template with 
 | |
| an unconstrained argument T. This is necessary so that further types can be made interoperable with
 | |
| Mutable or Constant. Would Mutable be defined as   ::
 | |
| 
 | |
|   struct Mutable : Facade<Mutable>
 | |
|   {
 | |
|     bool equal_to(Mutable const&);  
 | |
|     bool equal_to(Constant const&);  
 | |
|   };
 | |
| 
 | |
| Constant and Mutable would still be interoperable but no further interoperable could be added 
 | |
| without changing Mutable. Even if this would be considered acceptable the current specification forces
 | |
| a two way dependency between interoperable types. Note in the templated equal_to case this dependency 
 | |
| is implicitly created when specializing equal_to.
 | |
| 
 | |
| Solution
 | |
| ========
 | |
| 
 | |
| The two way dependency can be avoided by enabling type conversion in the binary operator
 | |
| implementation. Note that this is the usual way interoperability betwween types is achieved
 | |
| for binary operators and one reason why binary operators are usually implemented as non-members.
 | |
| 
 | |
| A simple implementation of this strategy would look like this ::
 | |
| 
 | |
|   template<
 | |
|       class T1
 | |
|     , class T2
 | |
|   >
 | |
|   struct interoperable_base :
 | |
|       if_< 
 | |
|           is_convertible<
 | |
|               T2
 | |
|             , T1
 | |
|           >
 | |
|         , T1
 | |
|         , T2>
 | |
|   {};
 | |
| 
 | |
| 
 | |
|   template<
 | |
|       class Derived1
 | |
|     , class Derived2
 | |
|   >
 | |
|   enable_if<is_interoperable<Derived1, Derived2>, bool> operator==(
 | |
|       Derived1 const& lhs
 | |
|     , Derived2 const& rhs
 | |
|   )
 | |
|   {
 | |
|     typedef interoperable_base<
 | |
|                 Derived1
 | |
|               , Derived2
 | |
|             >::type Base;
 | |
| 
 | |
|     return static_cast<Base const&>(lhs).equal_to(static_cast<Derived2 const&(rhs));
 | |
|   } 
 | |
| 
 | |
| This way our original simple and "obvious" implementation would
 | |
| work again. ::
 | |
| 
 | |
|   c == m; // ok, dispatched to Constant::equal_to
 | |
|   m == c; // ok, dispatched to Constant::equal_to, m converted to Constant
 | |
| 
 | |
| The backdraw of this approach is that a possibly costly conversion of iterator objects
 | |
| is forced on the user even in cases where direct comparison could be implemented
 | |
| in a much more efficient way. This problem arises especially for iterator_adaptor
 | |
| specializations and can be significantly slow down the iteration over ranges. Given the fact
 | |
| that iteration is a very basic operation this possible performance degradation is not 
 | |
| acceptable.
 | |
| 
 | |
| Luckily whe can have our cake and eat it by a slightly more clever implementation of the binary 
 | |
| operators. ::
 | |
| 
 | |
|   template<
 | |
|       class Derived1
 | |
|     , class Derived2
 | |
|   >
 | |
|   enable_if<is_convertible<Derived2, Derived1>, bool> operator==(
 | |
|       Derived1 const& lhs
 | |
|     , Derived2 const& rhs
 | |
|   )
 | |
|   {
 | |
|     return static_cast<Derived1 const&>(lhs).equal_to(static_cast<Derived2 const&(rhs));
 | |
|   } 
 | |
| 
 | |
|   template<
 | |
|       class Derived1
 | |
|     , class Derived2
 | |
|   >
 | |
|   enable_if<is_convertible<Derived1, Derived2>, bool> operator==(
 | |
|       Derived1 const& lhs
 | |
|     , Derived2 const& rhs
 | |
|   )
 | |
|   {
 | |
|     return static_cast<Derived2 const&>(rhs).equal_to(static_cast<Derived1 const&(lhs));
 | |
|   } 
 | |
| 
 | |
| Given our simple and obvious definition of Mutable and Constant nothing has changed yet. ::
 | |
| 
 | |
|   c == m; // ok, dispatched to Constant::equal_to, m converted to Constant
 | |
|   m == c; // ok, dispatched to Constant::equal_to, m converted to Constant
 | |
| 
 | |
| But now the user can avoid the type conversion by supplying the
 | |
| appropriate overload in Constant :: 
 | |
| 
 | |
|   struct Constant : Facade<Constant>
 | |
|   {
 | |
|     Constant();
 | |
|     Constant(Constant const&);
 | |
|     Constant(Mutable const&);
 | |
| 
 | |
|     ...
 | |
| 
 | |
|     bool equal_to(Constant const&);  
 | |
|     bool equal_to(Mutable const&);  
 | |
|   };
 | |
| 
 | |
|   c == m; // ok, dispatched to Constant::equal_to(Mutable const&), no conversion
 | |
|   m == c; // ok, dispatched to Constant::equal_to(Mutable const&), no conversion
 | |
| 
 | |
| This definition of operator== introduces a possible ambiguity when both types are convertible
 | |
| to each other. I don't think this is a problem as this behaviour is the same with concrete types.
 | |
| I.e.  ::
 | |
| 
 | |
|   struct A {};
 | |
| 
 | |
|   bool operator==(A, A);
 | |
| 
 | |
|   struct B { B(A); }; 
 | |
| 
 | |
|   bool operator==(B, B);
 | |
| 
 | |
|   A a;
 | |
|   B b(a);
 | |
| 
 | |
|   a == b; // error, ambiguous overload
 | |
| 
 | |
| Effect
 | |
| ======
 | |
| 
 | |
| Iterator implementations using iterator_facade look exactly as if they were
 | |
| "hand-implemented" (I am working on better wording).
 | |
| 
 | |
| a) Less burden for the user
 | |
| 
 | |
| b) The definition (standardese) of specialized adpters might be easier 
 | |
|    (This has to be proved yet)
 |