Boost logo

Boost :

Subject: [boost] [conversion] conversion domains
From: Gordon Woodhull (gordon_at_[hidden])
Date: 2011-08-29 14:22:17


Hi Vicente, all,

I've been thinking a bit about Conversion's ODR problems and the annoyance of headers that customize something globally (using e.g. template specialization) when they are included.

I think I would prefer a design that allowed to define sets of conversions, or conversion domains. When creating a converter, you would specify the list of conversions which you want. That way a conversion domain is specified in one place, and not because you #include something. Of course it also means there's no default domain, for better or worse.

I tried a number of approaches but only found one way that would work. This uses function overloading over an inheritance chain of converter classes, each of which brings its parent's convert() function(s) into scope with 'using'.

Yes, it is requires ugly, specific code to define a converter (and mpl::quoting the converters when specifying the domain yuk), and I wonder if anyone could find a better way to do this.

(I would prefer if it were possible to use partial specialization of template classes, but I guess there is no way to combine specializations of a nested template using inheritance, and it invokes a weird bug in gcc [1] if you try to do so.)

Vicente, have you considered similar approaches?

Cheers,
Gordon

[1] http://stackoverflow.com/questions/1413158/partial-specialization-of-a-class-template-in-derived-class-affects-base-class
This cites a claim that the bug was fixed in 4.5.0 but I am still seeing it in 4.5.3 - have not tried more recent.

#include <iostream>
#include <string>

#include <boost/mpl/vector.hpp>
#include <boost/mpl/fold.hpp>
#include <boost/mpl/apply_wrap.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/utility.hpp>
#include <boost/numeric/conversion/cast.hpp>
#include <boost/icl/type_traits/is_numeric.hpp>

namespace mpl = boost::mpl;

template<typename T> struct wrap {};

struct converter_start {
  struct dummy;
  dummy convert(dummy, wrap<dummy>);
};

template<typename Tail>
struct x2string : Tail {
  using Tail::convert;
  template<typename Source>
  std::string convert(Source const& x, wrap<std::basic_string<char> >) {
    return boost::lexical_cast<std::string>(x);
  }
};

template<typename Tail>
struct string2x : Tail {
  using Tail::convert;
  template<typename Target>
  Target convert(std::string const& s, wrap<Target>) {
    return boost::lexical_cast<Target>(s);
  }
};

template<typename Tail>
struct numeric : Tail {
  using Tail::convert;
  template<typename Target, typename Source>
  typename boost::enable_if_c<boost::icl::is_numeric<Target>::value &&
                               boost::icl::is_numeric<Source>::value, Target>
    ::type convert(Source const& x, wrap<Target>) {
    return boost::numeric_cast<Target>(x);
  }
};

typedef mpl::vector<mpl::quote1<string2x>, mpl::quote1<x2string>, mpl::quote1<numeric> > my_conversions;

struct my_converter : mpl::fold<my_conversions, converter_start, mpl::apply_wrap1<mpl::_2, mpl::_1> >::type {
  template<typename Target, typename Source>
  Target cast(Source const& source) {
    return convert(source, wrap<Target>());
  }
};

int main() {
  my_converter cvt;
  std::cout << cvt.cast<std::string>(1.7) << " " << cvt.cast<int>(std::string("17")) << std::endl;
  std::cout << cvt.cast<int>(1.7) << " " << cvt.cast<float>(17)/10.f << std::endl;
  return 0;
}


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