Boost logo

Boost :

Subject: Re: [boost] Boost.Convert. Take 2.
From: Vladimir Batov (Vladimir.Batov_at_[hidden])
Date: 2014-02-10 16:22:56


Andrzej, thank you for your input. It's much appreciated. Please find my
replies below.

On 02/11/2014 05:12 AM, Andrzej Krzemienski wrote:
> ...
> There are at least two reasons why I would prefer your library to
> lexical_cast. First, it lets me customize the locale, which I was always
> missing. Second, it doesn't force me to throw exceptions (and make the
> program go slower, if I just want to test if my string is convertible to
> int).
Same here. I had no choice but to unfortunately put boost::lexical_cast
aside as my classes did not have DefaultConstructibility, I needed
formatting and our conversion failures were not exceptional to warrant
exceptions. Back then I tried hard to persuade boost::lexical_cast
extended. I guess its original designers and maintainers had their
reasons not to extend the class to provide the functionality I
needed/requested.
> But there are two reasons, that discourage me from using the library: first
> that it "pretends" it returns my T, whereas in fact it returns a wrapper
> convertible to T.
Well, with all due respect I have to disagree here. convert<T> does not
pretend to return T, it returns convert<T>::result. Yes, indeed, to this
day convert<T>::result still has an implicit converter to T. However,
it's a mere convenience -- I only have it because I use it all the time
for simple conversions:

     int a = convert<int>::from(not_int_str, -1, ccnv);

If there is a strong feeling that it needs to be disabled, then so be
it. I'll be just fine with explicit

     int a = convert<int>::from(not_int_str, -1, ccnv).value();

Still, I think I do understand where your "that it pretends it returns
my T" actually comes from. IMO the user has been spoiled (and misled) by
boost::lexical_cast, by its seeming simplicity -- give it a string, get
a converted T back. Yes, the only problem is that boost::lexical_cast
was/is able to do that because it by-design casts out *many*
conversion-related use-cases -- the reason I had to stop using it in the
first place. convert<T> takes a different -- all-inclusive -- approach
and, therefore, can only return something that might have a converted
value or it might not, i.e. something like Alexandrescu's Expected<T>,
boost::optional, tr1::optional, convert<T>::result. I hope after A.
Alexandrescu presentation on Expected<T> our programming community is
more open to and more mature to *accept* that sometimes a conversion
*request* cannot return T... even though boost::lexical_cast does it. :-)

> Users will get a lot of surprise, when they try to write:
>
> auto my_str = convert<std::string>::from(1, ..);
> my_str.size();
Yes... if they choose not to read the documentation. :-) ... and that's
relevant to any library, isn't it? Again, IMO the actual source or
(perceived) potential confusion here is the false expectation that
convert<T> must return T... like lexical_cast does. I do not believe
it's possible. I hope with people getting accustomed with A.
Alexandrescu's Expected<T> they'll be more open for return classes with
"expected" behavior.

> Also, I am likely to select an incorrect function overload, if I pass the
> result to the function template.

Again, if there is enough conviction behind the implicit conversion
causing havoc, it's no drama to take it out. Will it solve the problem
you mention?

> Second thing that bothers me is that for the result type to be able to
> offer all these checks an otherwise unnecessary runtime overhead is
> imposed. 'result' stores both optional<T> and a flag indicating how to deal
> with the lack of value. since, in order to write into T you need to
> default-construct it anyway (or move construct it), it looks it should be
> enough to have 'result' contain a non-optional T and more sophisticated
> flag that also stores the information about having a real T.
Well, I have to disagree about "unnecessary checks". If one does not
need checks, then boost::lexical_cast will suffice. As for the current
implementation, then I consider it a rough draft and an implementation
detail which I am not focusing on right now. Your suggestion might well
be the ultimate way to go. I think, we need to get the fundamentals
right first.

> I would like to suggest two things:
>
> 1. Would it not be possible to have an overload that does not take the
> stream object? It would mean we want to use some default char-based stream
> with any locale. The newbies would not have to bother with what it is, when
> they only want to convert an int to a string?

In the code I used/required explicit converter. In actual code the
converter is defaulted to the one I listed explicitly. Will it suffice?

> 2. Would it be possible to consider returning tr2::optional instead of
> 'result'? It is different than boost::optional in a couple of aspects.
> After a lots of changes, the
> final shape of it is here:
> http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3793.html and the
> reference implementation is here: https://github.com/akrzemi1/Optional
I am very much looking forward to optional being standardized and I'll
re-read the proposal. I started using optional more in my code and I
can't stop now. :-) Managing without it was a real pain with
cut-n-pasted half-baked solutions in relevant classes. Currently I am
using boost::optional as in production we are quite behind -- still
supporting gcc-3.3! So, some things are not available to me right now. I
am sure, if there is enough interest generated in convert<T>, we'll have
to think of a much better implementation.
> tr2::optional offers 4 ways of dealing with the missing (unset) value:
> First: if (rslt) use (*rslt); // check it manually
> Second: use (rslt.value_or(1)); // use default value
> Third: use (rslt.value()); // throw if no value
> Fourth: use (*rslt); // use without checks
>
> the fourth result is not to be underestimated. Sometimes I am sure my int
> will successfully convert to a string, and I do not want to pay the price
> for an extra check.
>
> Returning an optional would somehow separate responsibilities: conversion
> function only converts, and optional is a well-understood general-purpose
> wrapper for dealing with a potentially missing value.
>
> I am pretty sure we could adapt boost::optional to tr2::optional's
> interface.
>
> Or are there reasons not to use optional?
>

Well, I personally do not see any reasons *not* to use optional... in
fact, I use it inside convert<T>::result... well, not tr1::optional yet.
If I see that tr1::optional can do all convert<T>::result does, I'll
switch without hesitation.


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