Boost logo

Boost :

Subject: Re: [boost] [review] Conversion review ends today
From: Jeffrey Lee Hellrung, Jr. (jeffrey.hellrung_at_[hidden])
Date: 2011-08-30 14:43:31


On Mon, Aug 29, 2011 at 10:08 AM, Vicente J. Botet Escriba <
vicente.botet_at_[hidden]> wrote:

> Le 29/08/11 18:07, Jeffrey Lee Hellrung, Jr. a écrit :
>
[...]

> 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.
>

Your example of converting between boost::chrono objects and
boost::posix_time objects is, indeed, a real use case, so thank you. I can
imagine other similar use cases, where you want objects from two independent
libraries to be interconvertible. But this isn't *quite* what I was looking
for. Have you (or do you know someone who has) used the framework provided
by Boost.Conversion to utilize conversions in a generic setting (e.g.,
generic algorithms or data structures)? If not or not so much, are we
hoping that such algorithms and data structures will be created in the
future?

I'm feeling like I'm missing something here regarding the library's
utility. I admit I haven't gone though the documentation, but I think I
have a general idea of what the library is trying to achieve immediately
(provide extrinsic conversions between unrelated types). What about beyond
that immediate goal? I, too, had an application that required unrelated
types to be interconverted, and generic algorithms and data structures that
required these conversions. I initially constructed a generic framework
similar to that defined by Boost.Conversion, but ultimately I discarded this
framework in favor of the conversions being handled by a function object
with signature operator()(const Srce& srce, type_tag< Dest >) -> Dest, since
the conversions required additional context than that given by the Srce
object. So my experience may be coloring my perceptions.

I hope I don't sound too negative :/

- Jeff


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk