Subject: Re: [boost] [review] Conversion review ends today
From: Vicente J. Botet Escriba (vicente.botet_at_[hidden])
Date: 2011-08-29 13:08:43


Le 29/08/11 18:07, Jeffrey Lee Hellrung, Jr. a écrit :
> On Mon, Aug 29, 2011 at 7:41 AM, Gordon Woodhull<gordon_at_[hidden]>wrote:
>
> Gordon,
>
> I'm considering writing a review, but it will necessarily be late (sometime
> this week?).
>
> My initial inclination is to vote against acceptance of the library due to
> what I currently perceive to be a lack of "real-world use", since it seems
> like the margin of error for "getting it right" for these extrinsic
> conversion operators is pretty small. However, I intend to review the
> documentation to provide feedback nonetheless.
Jeffrey I don't think your vote could change the review result :(.
Anyway, any comment on the library is welcome.
> Vicente, if you have some real-world use cases that you know of, please
> share.
>
I removed the initial motivation from the documentation (it is a little
bit long). Here it is what I wrote:

"
I've needed recently to convert from `boost::chrono::time_point<Clock,
Duration>` to `boost::posix_time::ptime` and from
`boost::chrono::duration<Rep, Period>` to
`boost::posix_time::time_duration`. These kinds of conversions are
needed quite often when you use code from two different libraries that
have each implemented the same concept using a different representation,
and hard-coded the library interface to its own implementation. Well,
this is a normal situation we can't avoid. Life is life.

Quite often we need to convert unrelated types `Source` and `Target`. As
these classes are unrelated, neither of them offers conversion operators
to the other. Usually we get it by defining a specific function such as

     Target ConvertToTarget(Source& v);

In my case I started by defining

     template <typename Rep, typename Period>
     boost::posix_time::time_duration convert_to_posix_time_time_duration(
             const boost::chrono::duration<Rep, Period>& from);

     template <typename Clock, typename Duration>
     posix_time::ptime convert_to_posix_time_ptime(const
chrono::time_point<Clock, Duration>& from);

Imagine now that you need to convert a `std::pair<Source, Source>` to a
`std::pair<Target, Target>`. The standard defines conversion of two
pairs types if the related types are C++ convertible:

     template <typename T1, typename T2>
     struct pair {
         ...
         template<class U, class V>
         //requires Constructible<T1, const U&> && Constructible<T2,
const V&>
         std::pair(const pair<U, V>& p);

         template<class U , class V>
         //requires HasAssign<T1, const U&> && HasAssign<T2, const V&>
         std::pair& operator=(const std::pair<U , V>& p);
         ...
     };

As the types `Target` and `Source` are not C++ convertible other than
using a specific function, we need to use a workaround.

We can again define a specific function

     std::pair<Target,Target>
ConvertToPairOfTarget(std::pair<Source,Source>& v) {
         return std::make_pair(ConvertToTarget(v.fisrt),
ConvertToTarget(v.second));
     }

While the `ConvertToTarget` could be specific, it seems clear to me that
the `ConvertToPairOfTarget` should be generic

     template <typename Target1, typename Target2, typename Source1,
typename Source2)
     std::pair<Target1,Target2>
ConvertToPair(std::pair<Source1,Source2>& v);

In order to do that we need that the pair template parameters define a
common function, let it call __convert_to,

     template <typename Target, typename Source)
     Target convert_to(Source& v);

so `ConvertToPair` can be defined as

     template <typename Target1, typename Target2, typename Source1,
typename Source2)
     std::pair<Target1,Target2>
ConvertToPair(std::pair<Source1,Source2>& v) {
         return std::make_pair(convert_to<Target1>(v.fisrt),
convert_to<Target2>(v.second));
     }

We need to specialize the __convert_to function for the specific classes
`Source` and `Target`. We can do it as follows

     Target convert_to(Source& v) {return ConvertToTarget(v);}

Note that the preceding overloads doesn't really work, as C++ doesn't
use the result type on overload resolution. The library uses a
customization point that takes into account the result type.

In my case I needed

     template <typename Rep, typename Period>
     boost::posix_time::time_duration convert_to(const
boost::chrono::duration<Rep, Period>& from)
     {
             return convert_to_posix_time_time_duration(from);
     }

     template <typename Clock, typename Duration>
     boost::posix_time::ptime convert_to(const
boost::chrono::time_point<Clock, Duration>& from)
     {
             return convert_to_posix_time_ptime(from);
     }

So now I can convert

     std::pair<chrono::time_point<Clock, Duration>,
boost::chrono::duration<Rep, Period> >

to

     std::pair<boost::posix_time::ptime, boost::posix_time::time_duration>

using the `ConvertToPair` function.

What about converting `std::pair<Source,std::pair<Source,Source> >` to
`std::pair<Target,std::pair<Target,Target> >`? The issue now is that
`convert_to(std::make_pair<to, std::make_pair<to,to> >)` does not
compile because the conversion of `std::pair` is named `ConvertToPair`.
So we need to specialize the function __convert_to for pairs.

     template <typename T1, typename T2, typename S1, typename S2)
     static std::pair<T1,T2> convert_to(std::pair<Source1,Source2>& from) {
     {
         return std::pair<T1,T2>(convert_to<T1>(from.first),
convert_to<T2>(from.second));
     }

There is still a last point. The preceding design works well with
unrelated classes, but what about classes that already define conversion
via a constructor or a conversion operator - do we need to specialize
these conversions? The answer is no. We need to define the default
implementation of the __convert_to function to just return the explicit
conversion.

     template < typename Target, typename Source>
     Target convert_to(const Source& from)
     {
         return Target(from);
     }

As noted above these overloads don't work, and the library uses a
customization point that takes into account the result type."

Let me know if this is enough real for you.

Best,
Vicente