
Hi, ----- Original Message ----- From: "vicente.botet" <vicente.botet@wanadoo.fr> To: <boost@lists.boost.org> Sent: Saturday, October 24, 2009 11:01 PM Subject: Re: [boost] [conversion] Motivation for two NEW generic conver_toand assign_to functions
Hi, ----- Original Message ----- From: "Jeffrey Hellrung" <jhellrung@ucla.edu> To: <boost@lists.boost.org> Sent: Saturday, October 24, 2009 9:53 PM Subject: Re: [boost] [conversion] Motivation for two NEW generic conver_to and assign_to functions
vicente.botet wrote:
Two comments (for now):
1) It's not uncommon that conversion to a "To" type from a "From" type needs more information than just an instance of a From. One example would be when dynamic memory allocation is necessary to construct the To type, e.g., when converting from a boost::array to a std::vector.
How do you know the To to which do you want to convert, if not given explictly?
It is given explicitly.
In my application, I have to "upgrade" (i.e., convert) extended-but-fixed precision floats (which are statically allocated) to arbitrary precision floats (which are dynamically allocated), and the allocator_type of the arbitrary precision float shouldn't generally be default-constructed. I've worked around this by requiring my generic convert function to take a ConvertPolicy parameter (defaulted to some empty struct null_convert_policy_t), whose interpretation is up to the particular overload chosen for the conversion. I wouldn't mind seeing a better system in place. Have you considered this situation?
I'm interested on seen how you have solved the problem you found. and please could you post your example and show why it can not be specialized with the proposed convert_to or assign_to interface? Non conforming use case will improve the final library.
I use overloads of the following signature:
template< class From, class To, class ConvertPolicy > To convert(const From& from, type_tag<To>, const ConvertPolicy& convert_policy)
struct type_tag is just an empty struct used to avoid having to provided the "To" template parameter explicitly. I thought maybe a function signature which didn't require explicit template parameters to call might be more ADL-friendly, but admittedly I have nothing to back this up with...
Do you mean that in order to use ADL you need to include the type_tag<To>?
For (one of) my specific use cases, I need to convert among user-defined numeric types, some of which are statically allocated and some of which are dynamically allocated. Or even a conversion from double to some dynamically-allocated numeric type might be desired. Either way, converting to a dynamically-allocated numeric type (think an arbitrary-precision rational) needs an allocator to construct it, so how would you convert a double to an arbitrary_precision_rational< T, allocator_type > without additionally providing an instance of allocator_type?
The convert function work well from converting one type in another. Well I would try wit this conversion
arbitrary_precision_rational< T, allocator_type > convert_to<arbitrary_precision_rational< T, allocator_type >, std::pair<double, allocator_type > >( std::pair<double, allocator_type > from);
And use it as double d; arbitrary_precision_rational< T, allocator_type > apr = convert_to<arbitrary_precision_rational< T, allocator_type > >(make_pair(d, allocator_type()));
So the general case don't pay for the specific one. Do you find something wrong with this approach?
To get around this issue, I ultimately pass an instance of the allocator_type as the ConvertPolicy parameter, and it is interpreted appropriately by the specific overload that implements the conversion from double to arbitrary_prec...< T, allocator_type >.
Of course, this begs the question: How does the code that calls "convert" know to pass allocator_type as the ConvertPolicy? It generally doesn't, of course, so it also needs to be provided a ConvertPolicy parameter by *its* client. At some point up the call hierarchy (even if this has to be at main scope), it is known what family of types one needs to convert among, hence the appropriate ConvertPolicy would be known and initiated then.
Is that an adequate explanation?
Yes. I understand now your use case, and how you have solved it.
2) Assuming I understand the Conversion library correctly, what are the advantages to using both overloading via ADL and function template specialization as opposed to just the former? I'm not familiar of any other free functions used in boost (or anywhere else, for that matter) that rely on (perhaps I should say "allow"?) function template specialization, so I'm wondering what the motivation and pros/cons are (cons other than compiler support). I would guess that function template specializations must reside in the same namespace as the primary template...?
The single think I can answer is that the same question is already on the documentation, unfortuantley with no answer. The answer I can give you now is that I don't master ADL to the point of using it to imlement what I have in mind. I'm unable to saying you if this is possible or not. The question of using partial specializations of template functions is for the moment secondary, if there is a better way to achieve the same goal. My main concern is if we need the generic functions convert_to and assign_to. If this can be implemented in a better way using only ADL, we could do this way. Of course I'm intersted in knowing how this can be implemented using only ADL, even if this is evident for most of you.
This is definitely a problem that needs to be solved (correctly) in a generic fashion, so I'll try to follow your progress.
I'm open to any sugestion. If we can implement it using only ADL, I will do it once I'll know how.
You might want to read Herb Sutter's GotW articles on ADL, linked from Wikipedia:
Thanks for the pointer.
BTW, can we add functions on the 'std' namespace?
I've got the feeling that this is generally frowned upon, but not sure... :/
If it is not possible, how can ADL help to convert std::pair<A,B> to std::pair<C,D>? IMO, we need partial specialization to cover this case.
I start to understand how ADL can simplify things. Maybe I can do as the bosst::swap function does.
Either: * A function with the signature convert_to<Target>(Source const&) is available via argument dependent lookup Or: * A template specialization of boost::conversion::convert_to exists for Target and Source Or: * Target is copy constructible from Source (default implementation)
namespace boost { namespace conversion { // include here my current implementation of convert_to template <typename Target, typename Source> Target convert_to(const Source& val) { return Target(from); } }}
namespace boost_convert_to_impl { template <typename Target, typename Source> Target convert_to(Source const& from) { using namespace boost::conversion; //use boost::conversion::convert_to if ADL fails convert_to<Target>(from); } }
namespace boost { template <typename Target, typename Source> Target convert_to(Source const& from) { ::boost_convert_to_impl::convert_to<To>(from); } }
And use the tag_type<Target> trick if needed for ADL. Do you think that this interface is more satisfactory?
I have modified the convert_to implementation with your suggestions (see below) and now we can do: B b; A a1 (convert_to(b, type_tag<A>() )); The user can add overloading on the namespace of the class From, or specialize the boost::conversion::convert_to function. I have defined the tag with a defaulted parameter value type_tag<Target> tag=type_tag<Target>(), so we can also do A a1 (convert_to<A>(b)); I have added still an overloading to simplify some uses as a = convert_to(b, a); The Target parameter is not used other than to retrieve the type. template <typename Target, typename Source> Target convert_to(Source const& from, Target const&); What do you think? Does this interface satisfy your expectations? Best, Vicente _______________________________ namespace boost { template <typename T> struct type_tag {}; namespace conversion { template < typename To, typename From > To convert_to(const From& val, type_tag<To>) { return To(val); } } } namespace boost_conversion_impl { template <typename Target, typename Source> Target convert_to_impl(Source const& from) { using namespace boost::conversion; //use boost::conversion::convert_to if ADL fails return convert_to(from, boost::type_tag<Target>()); } } namespace boost { template <typename Target, typename Source> Target convert_to(Source const& from, type_tag<Target> tag=type_tag<Target>()) { return ::boost_conversion_impl::convert_to_impl<Target>(from); } template <typename Target, typename Source> Target convert_to(Source const& from, Target const&) { return ::boost_conversion_impl::convert_to_impl<Target>(from); } }