|
Boost : |
Subject: Re: [boost] [conversion] Motivation for two NEW generic conver_to and assign_to functions
From: vicente.botet (vicente.botet_at_[hidden])
Date: 2009-10-26 14:11:16
Hi,
----- Original Message -----
From: "vicente.botet" <vicente.botet_at_[hidden]>
To: <boost_at_[hidden]>
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_at_[hidden]>
> To: <boost_at_[hidden]>
> 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:
>>
>> http://en.wikipedia.org/wiki/Argument_dependent_name_lookup
>
> 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);
}
}
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk