Boost logo

Boost :

Subject: Re: [boost] [review] convert library
From: Rob Stewart (robertstewart_at_[hidden])
Date: 2014-05-25 22:04:35


On May 25, 2014 7:01:21 PM EDT, Vladimir Batov <Vladimir.Batov_at_[hidden]> wrote:
>
>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.)

More on this below.

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

My examples followed yours.

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

Without the directional cue that "from" provided, "convert" loses clarity. "to" provides directionality.

>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":

"as" works equally well for me.

>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
>
>lexical_cast<string>(x);
>x.str();
>
>#1 tells me more than #2.

Perhaps. It tells you more about the mechanism, I'll grant.

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

Not quite, I think.

>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
>
>#1
>string s1 = lexical_cast<string>(x);
>string s2 = lexical_cast<string>(y);
>string s3 = lexical_cast<string>(z);
>
>with, say,
>
>#2
>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.

I value consistency of interface. The difference between what you've proposed and what lexical_cast offers is that yours defers to an arbitrary converter while lexical_cast always uses IOStreams, though that does defer to type specific insertion and extraction operators. The operators, however, are type specific, and your converters are orthogonal to the types (unless using an IOStreams-based converter).

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

Using a standard interface over whimsical and varying type specific interfaces is helpful. I'm just saying that one must understand the behavior of a potentially large number of converters. The standard interface doesn't help to understand how the conversion is done.

(I'll submit a review tomorrow.)

___
Rob

(Sent from my portable computation engine)


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