| 
									
										
										
										
											2004-01-06 19:37:17 +00:00
										 |  |  | ++++++++++++++++++++++++++++
 | 
					
						
							|  |  |  |  Interoperability Revisited 
 | 
					
						
							|  |  |  | ++++++++++++++++++++++++++++
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | :copyright: Copyright Thomas Witt 2004.
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-09-29 01:59:59 +00:00
										 |  |  | .. 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)
 | 
					
						
							| 
									
										
										
										
											2004-01-06 19:37:17 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 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.
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-07-06 19:47:17 +00:00
										 |  |  | 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));
 | 
					
						
							|  |  |  |   } 
 | 
					
						
							| 
									
										
										
										
											2004-01-06 19:37:17 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | The problem with this is that operator== always forwards to Derived1::equal_to. The net effect is that the
 | 
					
						
							| 
									
										
										
										
											2007-07-06 19:47:17 +00:00
										 |  |  | following "obvious" implementation of to interoperable types does
 | 
					
						
							|  |  |  | not quite work. ::
 | 
					
						
							| 
									
										
										
										
											2004-01-06 19:37:17 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-07-06 19:47:17 +00:00
										 |  |  |   struct Mutable : Facade<Mutable>
 | 
					
						
							|  |  |  |   {
 | 
					
						
							|  |  |  |     bool equal_to(Mutable const&);  
 | 
					
						
							|  |  |  |   };
 | 
					
						
							| 
									
										
										
										
											2004-01-06 19:37:17 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-07-06 19:47:17 +00:00
										 |  |  |   struct Constant : Facade<Constant>
 | 
					
						
							|  |  |  |   {
 | 
					
						
							|  |  |  |     Constant();
 | 
					
						
							|  |  |  |     Constant(Constant const&);
 | 
					
						
							|  |  |  |     Constant(Mutable const&);
 | 
					
						
							| 
									
										
										
										
											2004-01-06 19:37:17 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-07-06 19:47:17 +00:00
										 |  |  |     ...
 | 
					
						
							| 
									
										
										
										
											2004-01-06 19:37:17 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-07-06 19:47:17 +00:00
										 |  |  |     bool equal_to(Constant const&);  
 | 
					
						
							|  |  |  |   };
 | 
					
						
							| 
									
										
										
										
											2004-01-06 19:37:17 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-07-06 19:47:17 +00:00
										 |  |  |   Constant c;
 | 
					
						
							|  |  |  |   Mutable  m;
 | 
					
						
							| 
									
										
										
										
											2004-01-06 19:37:17 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-07-06 19:47:17 +00:00
										 |  |  |   c == m; // ok, dispatched to Constant::equal_to
 | 
					
						
							|  |  |  |   m == c; // !! error, dispatched to Mutable::equal_to
 | 
					
						
							| 
									
										
										
										
											2004-01-06 19:37:17 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-07-06 19:47:17 +00:00
										 |  |  |   Instead the following "slightly" more complicated implementation is necessary
 | 
					
						
							| 
									
										
										
										
											2004-01-06 19:37:17 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-07-06 19:47:17 +00:00
										 |  |  |   struct Mutable : Facade<Mutable>
 | 
					
						
							|  |  |  |   {
 | 
					
						
							|  |  |  |     template <class T>
 | 
					
						
							|  |  |  |     enable_if<is_convertible<Mutable, T> || is_convertible<T, Mutable>, bool>::type equal_to(T const&);  
 | 
					
						
							|  |  |  |   };
 | 
					
						
							| 
									
										
										
										
											2004-01-06 19:37:17 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-07-06 19:47:17 +00:00
										 |  |  |   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&);  
 | 
					
						
							|  |  |  |   };
 | 
					
						
							| 
									
										
										
										
											2004-01-06 19:37:17 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 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 
 | 
					
						
							| 
									
										
										
										
											2005-02-27 17:28:24 +00:00
										 |  |  | an unconstrained argument T. This is necessary so that further types can be made interoperable with
 | 
					
						
							| 
									
										
										
										
											2007-07-06 19:47:17 +00:00
										 |  |  | Mutable or Constant. Would Mutable be defined as   ::
 | 
					
						
							| 
									
										
										
										
											2004-01-06 19:37:17 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-07-06 19:47:17 +00:00
										 |  |  |   struct Mutable : Facade<Mutable>
 | 
					
						
							|  |  |  |   {
 | 
					
						
							|  |  |  |     bool equal_to(Mutable const&);  
 | 
					
						
							|  |  |  |     bool equal_to(Constant const&);  
 | 
					
						
							|  |  |  |   };
 | 
					
						
							| 
									
										
										
										
											2004-01-06 19:37:17 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 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.
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-07-06 19:47:17 +00:00
										 |  |  | 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
 | 
					
						
							| 
									
										
										
										
											2004-01-06 19:37:17 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 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 
 | 
					
						
							| 
									
										
										
										
											2007-07-06 19:47:17 +00:00
										 |  |  | 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
 | 
					
						
							| 
									
										
										
										
											2004-01-06 19:37:17 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 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.
 | 
					
						
							| 
									
										
										
										
											2007-07-06 19:47:17 +00:00
										 |  |  | I.e.  ::
 | 
					
						
							| 
									
										
										
										
											2004-01-06 19:37:17 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-07-06 19:47:17 +00:00
										 |  |  |   struct A {};
 | 
					
						
							| 
									
										
										
										
											2004-01-06 19:37:17 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-07-06 19:47:17 +00:00
										 |  |  |   bool operator==(A, A);
 | 
					
						
							| 
									
										
										
										
											2004-01-06 19:37:17 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-07-06 19:47:17 +00:00
										 |  |  |   struct B { B(A); }; 
 | 
					
						
							| 
									
										
										
										
											2004-01-06 19:37:17 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-07-06 19:47:17 +00:00
										 |  |  |   bool operator==(B, B);
 | 
					
						
							| 
									
										
										
										
											2004-01-06 19:37:17 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-07-06 19:47:17 +00:00
										 |  |  |   A a;
 | 
					
						
							|  |  |  |   B b(a);
 | 
					
						
							| 
									
										
										
										
											2004-01-06 19:37:17 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-07-06 19:47:17 +00:00
										 |  |  |   a == b; // error, ambiguous overload
 | 
					
						
							| 
									
										
										
										
											2004-01-06 19:37:17 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 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 
 | 
					
						
							| 
									
										
										
										
											2005-02-27 17:28:24 +00:00
										 |  |  |    (This has to be proved yet)
 |