[conversion] conversion domains

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

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. Yes, this is a big annoyance. 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'. Your design has several advantages over the one I use: it avoids ODR and allows multiple conversions on different domains. The user could even define a mixin that uses the intrinsic implicit/explicit conversions the C++ langage provides and include it in
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 guess that the interface can be improved (e.g. the one used in the
Le 29/08/11 20:22, Gordon Woodhull a écrit : the set of possible overloads. proposed library Boost.TypeErasure) so all the metafunction is not pressent at the user side, so it could be more friendly.
(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?
Not exactly in this form and context. I have made an extension to Boost.Endian that make endian conversion depending on a domain parameter that maps each one of the leaves to its intended endiannes.Your converter maps, if I can say, each pair of source+target types to an overloaded convert function. I really think your design merits to be explored :) Best, Vicente

Hi Vicente, On Aug 29, 2011, at 6:02 PM, Vicente J. Botet Escriba wrote:
Le 29/08/11 20:22, Gordon Woodhull a écrit :
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'. Your design has several advantages over the one I use: it avoids ODR and allows multiple conversions on different domains. The user could even define a mixin that uses the intrinsic implicit/explicit conversions the C++ langage provides and include it in the set of possible overloads.
Thanks! It completely escapes ODR because a conversion domain is defined in terms of the individual conversions. In your library, what are the advantages/disadvantages of the two customization mechanisms, template specialization and function overloads? I guess the function overloads found through ADL have a nicer syntax, as shown in your example of a substitute for explicit conversions. It allows definition (as friend) within the class. But you list template specialization first, and that has the advantage of not having to take a dummy parameter. Are there other differences between the two techniques that would make one choose one or the other? I ask because I struggled to try to make template specialization over a hierarchy work in the 'domains' code I sent yesterday. But if function overloading is as good, I guess it's almost useable. Disadvantages of the 'domains' approach I can see: * metaprogramming = slower compile times (this compiles in less than a second but still..). I can see how to implement it more cheaply with variadics, also nicer syntax that way.. but not really an option unless with a macro wrapper. * details exposed in the converter interface: it's annoying to have to derive from a template parameter and then bring in overloads with 'using'. I don't see any way around this except with macros. If you look at how operations are defined in TypeErasure [1], you can see Steven is running into similar problems. Maybe some indecipherable boilerplate is unavoidable. * no default domain (so it's really not a substitute for overloadable static_cast anymore) And of course my mind goes to, isn't this really just a polymorphic function object? Can this be solved in one line with Phoenix? :-D
Vicente, have you considered similar approaches? Not exactly in this form and context. I have made an extension to Boost.Endian that make endian conversion depending on a domain parameter that maps each one of the leaves to its intended endiannes.Your converter maps, if I can say, each pair of source+target types to an overloaded convert function.
Yes, it is really the same as your approach, just generated using metaprogramming. Cheers, Gordon [1] http://lists.boost.org/Archives/boost/2006/08/108701.php
participants (2)
-
Gordon Woodhull
-
Vicente J. Botet Escriba