On May 25, 2014 7:01:21 PM EDT, Vladimir Batov
On May 19, 2014 9:36:40 PM EDT, Vladimir Batov
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
On 05/25/2014 07:13 PM, Rob Stewart wrote: 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.
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)