Boost logo

Boost :

Subject: [boost] Boost.Conversion: request for interest
From: vicente.botet (vicente.botet_at_[hidden])
Date: 2009-04-16 18:01:05


Hi,

I've developed a utility library Boost.Conversion. I would like to know if the Boost community is interested in this library.
If this is the case, this library could be scheduled for review quite soon.

This library is related to LexicalCast, NumericConversion and (String)Convert, but much less specific. It provides a convert_to generic function (like swap) which can be specialized by the user to convert specific unrelated types, and why not, he could use one of the preceding libraries. This convert_to function can be seen as an extrinsec explicit conversion operator. The livrary provides also an assign_to generic function that can be seen as an extrinsec explicit assignement operator.

Please let me know what do you think. What is wrong? What is missing? How can I improve it?

Thanks,
Vicente

== Boost.Conversion ==

Links:http://www.boostpro.com/vault/index.php?action=downloadfile&filename=conversion.zip&directory=Utilities&
Categories: Utilities
Description: Generic explicit conversion between unrelated types. Boost.Conversion provides:
 * a generic convert_to function which can be specialized by the user to make explicit conversion between unrelated types.
 * a generic assign_to function which can be specialized by the user to make explicit assignation between unrelated types.
 * conversion between std::pair of explicitly convertible types.
 * conversion between boost::optional of explicitly convertible types.
 * conversion between boost::chrono::time_point and boost::ptime.
 * conversion between boost::chrono::duration and boost::time_duration.
Soon conversion between std::vector, boost::array, and boost::tuple of explicitly convertible types.

Motivation
Imagine you need to convert unrelated types Source and Target. You can get it by defining a specific function such as

Target SourceToTarget(Source& v);
Imagine now that you need to convert a std::pair<Source, Source> to a std::pair<Target, Target>. Well you can again define a specific function

std::pair<Target,Target> PairOfSourceToPairOfTarget(std::pair<Source,Source>& v) {
    return std::make_pair(SourceToTarget(v.fisrt), SourceToTarget(v.second));
}
While the SourceToTarget could be specific, the PairOfSourceToPairOfTarget sould be generic

template <typename Target1, typename Target2, typename Source1, typename Source2)
std::pair<Target1,Target2> ConvertPair(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 ConvertPair can be defined as

template <typename Target1, typename Target2, typename Source1, typename Source2)
std::pair<Target1,Target2> ConvertPair(std::pair<Source1,Source2>& v) {
    return std::make_pair(convert_to(v.fisrt), convert_to(v.second));
}
The issue is that we need to specialize the convert_to function for the classes Source and Target. We can do it as follows

template <>
Target convert_to<Target, Source>(Source& v) {return SourceToTarget(v);}
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>>) do not compiles because the conversion of std::pair is named ConvertPair. So we need to specialize the function convert_to for pairs. With C++0x there is no major problem as it allows partial specialization of function templates. With C++98, we need to use a trick; as it allows partial specialization of classes we can define convert_to by as relying to a specific function of a class, as follows:

namespace partial_specialization_workaround {
  template < typename Target, typename Source >
  struct convert_to {
    static Target apply(const Source& val);
  };
}

template < typename Target, typename Source >
Target convert_to(const Source& val) {
  return partial_specialization_workaround::convert_to<Target,Source>::apply(val);
}
So now we can specialize partial_specialization_workaround::convert_to for pairs as follows:

namespace partial_specialization_workaround {
    template <typename Target1, typename Target2, typename Source1, typename Source2)
    struct convert_to< std::pair<Target1,Target2>, std::pair<Source1,Source2> > {
        inline static std::pair<Target1,Target2> apply(std::pair<Source1,Source2>& v) {
        {
            return std::pair<T1,T2>(convert_to<T1>(from.first), convert_to<T2>(from.second));
        }
    };

}
There is one more issue. The preceding design works well with unrelated classes, but what about classes that already define some kind of conversion, using a constructor or a conversion operator. Do we need to make specialization for these conversion? The answer is no. We need just to define the default implementation of the partial_specialization_workaround::convert_to::apply function to just return the explicit conversion.

namespace partial_specialization_work_around {
  template < typename T, typename U>
    struct convert_to {
      inline static T apply(const U& val)
      {
        return T(val);
      }
   };
}
What have we learned? Classes or algorithms relying on a conversion by construction or by the conversion operator can be made more generic by relaying in a function that explicitly states this conversion. So instead of requiring

Target(from)
requires

convert_to<Target>(from)
The same applies to classes or algorithms relying on the assignment operator. So instead of requiring

to = from
requires

assign_to(from, to);
The default implementation of assign_to relies on the assignement operator

namespace partial_specialization_workaround {
    template < typename Target, typename Source >
    struct assign_to {
        inline static To& apply(const Source& from, Target& to)
        {
            to = from;
            return to;
        }
    };
}
template < typename Target, typename Source >
To& assign_to(const Source& from, Target& to) {
    return partial_specialization_workaround::assign_to<Target,Source>::apply(from, to);
}
So one of the advantages of using this common functions is uniformity. The other is that now we are able to find all the explicit conversions to one type, as we can do with explict casts.


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