Boost logo

Boost :

Subject: Re: [boost] [convert] are you mixing default_value and error_value?
From: Vladimir Batov (vladimir.batov_at_[hidden])
Date: 2009-07-06 18:03:37


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

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.

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

> 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);

> 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. Having convert<Type>::result played a role as well.

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?

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

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

convert<direction>::result d = convert<direction>::from(s, direction::up);
if (!d) conversion failed.

That said, convert_trait seems tempting. What others think?

V.


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