
On Mon, Aug 29, 2011 at 10:08 AM, Vicente J. Botet Escriba < vicente.botet@wanadoo.fr> 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