Boost logo

Boost :

Subject: Re: [boost] [review] convert library
From: Vladimir Batov (Vladimir.Batov_at_[hidden])
Date: 2014-05-25 19:01:21

On 05/25/2014 07:13 PM, Rob Stewart wrote:
> On May 19, 2014 9:36:40 PM EDT, Vladimir Batov <Vladimir.Batov_at_[hidden]> wrote:
>> ... I personally like
>> d1 = boost::convert<double>(x, xcnv).value();
>> much better than some obscure
>> d1 = x.to_double();
>> d2 = y.extract_double(param);
>> d2 = z.simplify();
>> as boost::convert advertizes known/familiar/instantly recognizable
>> behavior when the seemingly innocuous "to_double()" and others are dark
>> horses that I need to spend time getting to know. When one multiplies
>> that effort manifold, the advantages of using familiar interfaces
>> become clear.
> Your version is better only in the sense that it provides a common interface, of I understand your point correctly. however, the resulting behavior is not clearly implied by those calls because one must know what the converters actually do in each case. (This is exemplified by the rest compilation error you resolved due to not explicitly specifying the locale.)
> Now, switch to a free function and compare it to your proposal and we can make some progress. For the case with to_double(), it might look like this:
> d1 = to_double(x);
> That interface can be as standard as yours, and is perfectly understandable. They are different ways of spelling a conversion operation. That mine is more succinct makes it superior in many ways. As it is, it lacks two things yours offers, however: extensibility and configurability.
> Extensibility can be added by making it a function template like so:
> d1 = to<double>(x);
> Configurability can be added with an overload:
> d1 = to<double>(x, cnv);
> Did I miss something? At a glance, this offers everything your more verbose syntax offers.

Rob, the only thing missing in your version is the handling of the
conversion-failure case in a non-throwing manner. If we add this to your
example, then it becomes

optional<double> d1 = to<double>(x, cnv);

It seems identical to what we've come up so far for the "convert" user API:

optional<double> d1 = boost::convert<double>(x, cnv);

If you and others feel that "to" reflects the purpose better than
"convert", I am fine with it... I think Jeroen used "as" in his "coerce":

optional<double> d1 = boost::convert<double>(x, cnv);
optional<double> d1 = boost::to<double>(x, cnv);
optional<double> d1 = boost::as<double>(x, cnv);

I'll adapt what the community feels reflects the purpose/intention better.

>> The same advantage here.
>> string encripted = encript(str);
>> The above is better *if* and *after* you spent time getting to know
>> what
>> "encript" actually does, its shortcomings, etc. On large scale all that
>> additional knowledge accumulates and expensive.
> The same can be said about the encrypting converter one would use with your framework to effect encryption. What you're suggesting is that your spelling somehow clarifies that an encrypting conversion is occurring, and how it happens, and I don't buy it.

I am certainly not suggesting that my spelling is in any way superior.
What I was trying to say is that a well-known familiar interface sends a
clearer message compared to home-made obscure one. I feel that, when I see


#1 tells me more than #2.

>> The problem (as I see
>> it) is that s/w developers often seem to focus on the small picture and
>> lose the big one. It's not an insult but an observation and is the
>> result of daily attention to details. When focus shifts to having to
>> deal with lots and lots of (often unfamiliar) code, say, code reviews,
>> maintenance, then ability to reliably understand the code as in
>> d1 = boost::convert<double>(x, xcnv).value();
>> d2 = boost::convert<double>(y, ycnv).value();
>> d2 = boost::convert<double>(z, zcnv).value();
>> rather than *guess* what it might be doing as in
>> d1 = x.to_double();
>> d2 = y.extract_double(param);
>> d2 = z.simplify();
>> is important. All IMO of course.
> You merely shifted the knowledge burden to three converter classes from three member functions. I'll grant that once you understand those the converters, you'll understand their application in multiple contexts, so that advantage does partially justify your claim.

I do see your point. I truly do. Still, I do not believe the issue is
black or white, i.e. for you for be correct I have to be wrong. Please
let me try again and explain my point. For starters, let's distance
ourselves from "convert" and focus on the usability issue itself. Let's
take a well-known instantly recognizable API -- lexical_cast -- and compare

string s1 = lexical_cast<string>(x);
string s2 = lexical_cast<string>(y);
string s3 = lexical_cast<string>(z);

with, say,

string s1 = x.str();
string s2 = y.stringinize();
string s3 = z.to_string();

Here, by paraphrasing your statement lexical_cast "merely shifts the
knowledge burden" to

std::istream& operator>>(std::istream&, TypeX&);
std::ostream& operator<<(std::ostream&, TypeX const&);
std::istream& operator>>(std::istream&, TypeY&);
std::ostream& operator<<(std::ostream&, TypeY const&);
std::istream& operator>>(std::istream&, TypeZ&);
std::ostream& operator<<(std::ostream&, TypeZ const&);

and I myself wrote quite a number of these and I hate writing them...
Still, I was doing that because that one-time pain was countered IMO by
many-times usage/deployment gain. I feel that uniform lexical_cast-based
deployment sends a far-clearer message about intentions and purpose...
not to mention advertises well-known user-facing interface and behavior.
For me, reading #1 flows (even though as you pointed out it does not
convey all information). I feel stumbling on every line while reading #2
because mentally I have to actually read it (usually people do not read
but recognize patterns, i.e. I do not read l-e-x-i-c-a-l but gulp it as
one-unit pattern... if that's a familiar one... that's, for example, why
it's quite hard spotting typos); then I have to stop and mentally
decipher what "str" might actually mean; then I have to apply my
interpretation to the context to see if it matches.

Hmm, just re-read the paragraph above... looks awfully boring. :-)
Still, it is probably the best I can do within the confinement of a
post. Apologies if I failed again. :-)

Another probably easier-to-grasp advantage of #1 over #2 is the
orthogonality of provided conversion functionality and
flexibility/adaptability of

optional<double> d1 = boost::convert<double>(x, cnv);
optional<double> d1 = boost::to<double>(x, cnv);

compared to #2.

Boost list run by bdawes at, gregod at, cpdaniel at, john at