$include_dir="/home/hyper-archives/boost/include"; include("$include_dir/msg-header.inc") ?>
Subject: Re: [boost] [convert] are you mixing default_value and error_value?
From: vicente.botet (vicente.botet_at_[hidden])
Date: 2009-07-08 06:42:52
----- Original Message ----- 
From: "Vladimir Batov" <vladimir.batov_at_[hidden]>
To: <boost_at_[hidden]>
Sent: Tuesday, July 07, 2009 12:03 AM
Subject: Re: [boost][convert] are you mixing default_value and error_value?
> 
>> vicente.botet <vicente.botet <at> wanadoo.fr> writes:
>> I have read the documentation and I think that you are mixing default
>> value with error value. 
> 
> I am not sure I can agree. In my book there is *no* separate default value.
> There is only one value -- the conversion-failure (called default, error, etc.)
> value. 
> 
>> The fact that a
>> type is not DefaultConstructible do not implies that the user
>> wants to have an exception when the
>> conversion fails. This are two orthogonal dimensions. 
> 
> I think you are saying that "The fact that a type is not DefaultConstructible
> does not mean that the user *does not* want to have an exception when the
> conversion fails" because when the default/error value is provided, there is
> *no* exception on failure.
Yes, this is what I wanted to say. On the case of the direction class, you are forced to give an *initial value* because direction is not default constructible, but with your interface we can't have an exception in this case. Is for this reason I say you are mixin both.
 
> And indeed, I strongly agree that these two issues -- the throwing behavior and
> providing/not-providing the default/error value -- are indeed orthogonal. I am
> very glad you mention that because IMHO it is a subtle but important issue that
> is often missed or misunderstood.
So, do you plan to separate them?
 
>> I think that for these non Default constructible types you can use
>> a trait that gives you a default value for
>> types not having a default constructor. E.g.
>> 
>> template <typename T> 
>> struct default_value_trait {
>>     static const T value=T();
>> };
>> 
>> template <> 
>> struct default_value_trait<direction> {
>>     static const direction value=direction::up;
>> };
>> 
>> So now you don't need any more to pass the default value as 
>> parameter, and the following should compile
>> 
>> direction dir = convert<direction>::from (str);  
>> 
>> and could  throw if conversion fails after minor modification
>> on the implementation.
> 
> Yes, it is an interesting approach and I like it as it allows to deploy throwing
> 'convert' uniformly for DefaultConstructible and NonDefaultConstructible types.
> The only hesitation that pops to my mind is that I feel 'convert' is fairly
> straightforward and needs to be used without much/any preparation. (That's how I
> tend to use it anyway). That's why I was whining about the named parameters'
> interface as it requires a round trip to the top of the file to add 'using'. If
> lazy-me has to do the same for the default_value_trait<direction>
> specialization, then I'll probably try avoiding that. 
This is surely because you are mixing the returned value when a failure occur and the initial value when the class is not default convertible.
> On the other hand,
> integration of a Type into the lexical_cast/convert framework does require some
> preparation -- namely op>>() and op<<().  We could keep it for backward
> compatibility but might offer a new trait-based way of integrating a Type into
> the lexical_cast/convert framework. Like
> 
> template<>
> struct convert_trait<Type>
> {
> }
> 
> where we might put anything related to the integration of the Type into the
> convert framework. Is it too far-fetched? Thinking aloud really.
Why not?
 
>> I suppose you can reach the same effect with the error_value
>> needed when the user want no_thow semantics.
>> 
>> template <typename T> 
>> struct error_value_trait {
>>     static const T value=T();
>> };
>> 
>> template <> 
>> struct error_value_trait<direction> {
>>     static const direction value=direction::down;
>> };
>> 
>> So the following 
>> 
>> direction dir = convert<direction>::from (str)(_thows=false);  
>> 
>> will return direction::down when conversion fails after
>> some modification on the implementation
> 
> That feels over-engineered. Somehow I feel more comfortable with more explicit
> 
> direction dir = convert<direction>::from(str, direction::dn);  
You are right the failure returned value should be given at the call. What about  passing it statically
    direction dir = convert<direction, no_throws<direction::dn> >(str);  
or dynamically
    direction dir = convert<direction>(str, (_no_throws=direction::dn));  
>> One more question raise now. Should the fact the conversion throws
>> or not when failing be part of the
>> function type or a function parameter?
>> 
>> Do we need 
>> 
>> direction dir = convert<direction, no_thows<> >::from (str);  
>> 
>> or 
>> 
>> direction dir = convert<direction>::from (str)(_thows=false);  
> 
> Yes, that's a fair question. I considered convert<Type, Throw> before and back
> then it seemed that it was more flexible to configure the throwing behavior via
> a parameter. 
Have you considered to return failure setting errno? How can your current interface be adapted to this use case?
With a coverter template parameter you can do something like: statically
    direction dir = convert<direction, set_errno<EINVALID> >(str);  
or dynamicaly
    direction dir = convert<direction>(str, (_set_errno=EINVALID));  
> Having convert<Type>::result played a role as well.
Have you considered to convert returns just a pair<T, bool> or optional<T>? How can your current interface be adapted to these use cases?
With a template parameter you can do something like
pair<direction, bool> dirp = convert<direction, return_pair >(str);  
optional<direction> opt_dir = convert<direction, return_optional >(str);  
or
pair<direction, bool> dirp = convert<direction>(str, _return_pair);  
optional<direction> opt_dir = convert<direction>(str, _return_optional);  
 
> To me, in principle, there is no much difference between supplying a parameter
> as a template or as an argument. So, I chose the one that looked less
> restrictive and scaled better. Do you feel the template-based could do better? 
Templating the convert function with a converter parameter allows to have different return types that depend on the converter parameter. 
A model of converter is the functor class realizing the conversion using the conversion parameters
template <typename To>
struct converter {
    typedef related to To result_type;
    converter();
    converter(converter_parameters<To> const&);
    template <typenam From> result_type operator()(From);
};
The convert function can return the out type  (To) instead of the internal converter which is implicitly convertible to the To type.
template <typename To, typename Converter, typename From >
Converter::retult_type convert(From f, Converter const&cv) { 
    return cv(f);
}
 
>> Resumein, I think these modifications are important, 
>> so the convert function can have always only the From
>> parameter, and the throw semantics is preserved for non
>> DefaultConstructible types.
Rectification, the convert function can have two parameters, first the >From type and second the converter (parameters). The converter class must define operator()(From).
 
>>From my usage pattern I do not think I want "the convert function to always have
> only the From parameter". In fact, truth to be told, for me it is the other way
> around -- I *never* use the convert function with only the From parameter. :-) I
> prefer having the failure value immediately visible as in the following (typical
> for my usage) example:
> 
> int v = convert<int>::from(s, MAX_INT);
> if (v == MAX_INT) conversion failed.
What about
    int v = convert<int>(s, _no_thows=MAX_INT);
    if (v == MAX_INT) conversion failed.
 
> convert<direction>::result d = convert<direction>::from(s, direction::up);
> if (!d) conversion failed.
And 
optional<direction> d = convert<direction>(str, _return_optional );  
if (!d) conversion failed.
 
> That said, convert_trait seems tempting. What others think?
We can see 
_no_thows=MAX_INT
_return_pair
_return_optional
and the other parameters as elements of a DSL configuring the conversion. 
This view of the convert function with a converter parameter joins a little bit the one proposed by Hartmut in a recent post. What others think?
Best,
Vicente