[review] Convert library

classic Classic list List threaded Threaded
79 messages Options
1234
Reply | Threaded
Open this post in threaded view
|

[review] Convert library

Jeroen Habraken
Hi,

As promised my review of the boost::convert library. Note that the
questions were answered in a seemingly random order, thus I hope it all
comes together. Secondly, being the author of the boost::coerce library
[2][3] I may be a little biased at times, please forgive me.

-- What is your evaluation of the design?

There are two parts to this, the design of the backend and the design of
the interface that the user predominantly sees. I have little to say on the
first, the notion of multiple backend converters is a big step forwards and
their interface is sane.

I'm far less convinced when it comes to the user-facing interface as I
believe the simple tasks should be simple and intuitive, whereas the harder
tasks may be harder.

The most trivial use of boost::convert, and boost::lexical_cast and
boost::coerce simply don't compare in my opinion.

    boost::cstringstream_converter cnv;
    int i = boost::convert<int>::from("23", cnv).value();

versus

    int i = boost::lexical_cast<int>("23");
    int j = boost::coerce::as<int>("23");

When the convert library makes it into boost I'd want to mentally deprecate
lexical_cast as a library I should use, and always use convert instead.
This means convert should have a default converter, which may well be
backed by lexical_cast with an interface such as:

    int i = boost::convert<int>::from("23").value();

This still doesn't completely satisfy me as I'd like to write

    int i = boost::convert<int>::from("23");

but it'd be an improvement in my opinion. Now I'm aware lexical_cast
doesn't have a non-throwing interface, and it probably never will, but I've
solved the problem as follows in boost::coerce:

    int i = boost::coerce::as_default<int>("XXX");
    int j = boost::coerce::as_default<int>("XXX", tag::hex());
    int k = boost::coerce::as_default<int>("XXX", 23);
    int l = boost::coerce::as_default<int>("XXX", tag::hex(), 23);

The former two returning the default-constructed int, 0, the latter two
returning 23. That said, I'll be the first to admit the coerce interface
isn't perfect, the fact that I need SFINAE to distinguish between
as_default calls with two parameters, the second either being a tag
(somewhat similar to your converters but not quite) or the default value
goes to show just that. However I'd prefer

    int i = boost::convert<int>::from("23", cnv);
    int i = boost::convert<int>::from_default("23", cnv, 0);

to the current interface using value_or(0). This is subjective though, and
quite possibly a matter of taste.

In summary, before this turns into too long a rant, I think the current
user-facing interface can be improved substantially, but I'm well aware
this is subjective.

-- What is your evaluation of the implementation?

I've only had a quick glance at the code and it still looks fine. The gripe
I had two years ago regarding the use of reserved names (include guards
prefixed with "__") has been fixed. A few comments though:

In the boost::lexical_cast based converter there is a try { } catch (...) {
} which I believe to be an overly broad catch as lexical_cast explicitly
throws a boost::bad_lexical_cast. If in an unlikely event a std::bad_alloc
were to be thrown for example I'd rather not have boost::convert hide it
from me.

In the std::strtol based converter errno isn't checked, and it must be
noted that strtol accepts leading whitespace which may or may not be
wanted. I've implemented what I believe to be a correct converter using
strtol which checks for the leading whitespace in [0], but it comes with no
guarantees as it's quite tricky indeed. The new C++11 wrapper std::stol and
friends in [1] may be of use here, but unfortunately they only have a
throwing interface.

All converters are added to the main boost namespace which is crowded
enough as it is -- hi boost::_1 --, and libraries should add their
functionality to their own namespaces in my opinion. With the current
design it's not possible to add them in the boost::convert namespace but I
believe they should be in their own namespace, may I suggest
boost::converter.

It must be said that the first two comments are minor at best, the last
regarding the namespace is more important but potentially quite trivial to
fix.

-- What is your evaluation of the documentation?

I've found it to be very hard to write documentation as the library author
as at some point you're so intimately familiar with your library that it's
hard to get a feel for what the user needs. Whilst there are some rough
edges I think the current documentation is decent enough for it not to pose
a problem. Again a few comments:

The motivation should probably be addressed first, not near the end.

The printf and std::strtol based converters aren't documented, and the
converters should probably be grouped into a sub-section along with an
explanation of how to write your own.

Last of all I'd like to see an examples directory showing the examples from
the documentation, you already have a test/encryption.hpp which probably
belongs there.

-- What is your evaluation of the potential usefulness of the library?

In the previous review I've argued the library its functionality should be
added to boost::lexical_cast. Whilst I still hold that position for the
previous version of the library I believe this is no longer the case for
the present version, largely due to the added support for multiple
backends. There would be no practical way for lexical_cast to add this, and
I believe it to be very useful indeed.

-- Did you try to use the library? With what compiler? Did you have any
problems?

I've tried the library with Clang 3.2 and 3.4, and GCC 4.6 and 4.8 on Mac
OS X 10.9.3 and the examples from the documentation using the sstream
converter compile and run fine.

When I tried the printf_converter something as simple as:

    #include <boost/convert/api.hpp>
    #include <boost/convert/converter/printf.hpp>

    int main(int, char *[])
    {
       return boost::convert<int>::from("23",
boost::printf_converter()).value();
    }

failed to compile. It's missing includes as adding the following to
converter/printf.hpp fixes the problem:

    #include <boost/mpl/find.hpp>
    #include <boost/mpl/vector.hpp>

This probably indicates that you're trying to test too much in a single
file, and had the includes included via a different boost library.

In the previous review I noticed that boost::convert<int>::from('1',
cnv).value() spew a rather long error message. This has since been fixed,
the error message is now considerably shorter making it easier -- will it
ever be easy with C++ -- to figure out what's going on. I must note here
that the error message the latest versions of GCC produced is actually
easier to figure out than the one Clang produced.

-- How much effort did you put into your evaluation? A glance? A quick
reading? In-depth study?

Something between a quick reading and an in-depth study, now probably
leaning towards the latter.

-- Are you knowledgeable about the problem domain?

As the author of boost::coerce, which I've admittedly ignored for far too
long, I'm familiar with the conversion domain. Consequently I've seen the
pitfalls and endured the hardships in designing and implementing such a
library, my utmost respect for having it reviewed not once, but twice!

And finally:

-- Do you think the library should be accepted as a Boost library?

In my previous review this was a straight no, stating that the
functionality should be added to boost::lexical_cast. With the current
design supporting multiple backends this no longer holds true as I've
stated in the evaluation of the potential usefulness of the library.

Again, the library is quite useful, but, I so strongly disagree with the
current user-facing interface that I can't straight-out accept it. Once the
library is accepted this interface is set in stone and like marriage it's
"speak now or forever hold your peace".

As such I'm voting YES, CONDITIONALLY on the interface being simplified for
the seemingly simple cases as most of my other comments were minor or quite
trivial to fix.

With that out of the way I'd like to propose the following if you're
interested. Let's have a look at whether it may be possible to merge the
functionally of boost::convert and boost::coerce, as they seem to have
grown towards each other with the possibility to support multiple backends.
Two years ago I'd never have proposed this, but I've mellowed a bit since
then.

You seem to have focussed on backends primarily backed by std::stringstream
and friends whereas I have focussed on a backend backed by boost::spirit.
These may end up complementing leading to a single library capable of
supporting both, being more generic and powerful to the user when done
right.

Regardless of whether we end up going this way I'd love to have a
discussion on the interface, either on the mailing list or offline as this
is currently a major stumbling block for me.

Jeroen

[0]
https://github.com/VeXocide/coerce/blob/master/libs/coerce/example/backend.cpp
[1] http://en.cppreference.com/w/cpp/string/basic_string/stol
[2] https://github.com/VeXocide/coerce/
[3] http://vexocide.org/coerce/

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: [review] Convert library

Vladimir Batov
Jeroen, Thank you for your review it is very much appreciated. And, indeed, by your own admission you've "mellowed a bit since" a few years ago. Still, I see some bits left of "so strongly disagree"... should I have waited for a little bit longer? :-)

Jeroen Habraken wrote
Hi,

As promised my review of the boost::convert library. Note that the
questions were answered in a seemingly random order, thus I hope it all
comes together. Secondly, being the author of the boost::coerce library
[2][3] I may be a little biased at times, please forgive me.
Now that you mention "coerce" I do remember you mentioning it back then during review #1. I completely forgot about it and I still can't see it in Boost. Now when I look for "coerce" Google only gives 2012 links. :-(

-- What is your evaluation of the design?

There are two parts to this, the design of the backend and the design of
the interface that the user predominantly sees. I have little to say on the
first, the notion of multiple backend converters is a big step forwards and
their interface is sane.

I'm far less convinced when it comes to the user-facing interface as I
believe the simple tasks should be simple and intuitive, whereas the harder
tasks may be harder.

The most trivial use of boost::convert, and boost::lexical_cast and
boost::coerce simply don't compare in my opinion.

    boost::cstringstream_converter cnv;
    int i = boost::convert<int>::from("23", cnv).value();

versus

    int i = boost::lexical_cast<int>("23");
    int j = boost::coerce::as<int>("23");
Here we go again... :-) lexical_cast will be haunting me to my grave. :-)

For starters, please stop using that silly lexical_cast deployment example as our benchmark. Such deployment does not exist. lexical_cast cannot possibly be deployed like that in any real application. The correct and responsible deployment is

int i = -1;
int j = -1;

try { i = boost::lexical_cast<int>(str); }
catch (...) {}
try { j = boost::lexical_cast<int>(str); }
catch (...) {}

which is not exactly the two-liner that you sited! Compared to that the "convert" usage is

    boost::cstringstream_converter cnv;
    int i = boost::convert<int>(str, cnv).value_or(-1);
    int j = boost::convert<int>(str, cnv).value_or(-1);

does look simple and intuitive enough IMO, configurable and potentially more efficient. (In the post_review branch the convert::from API has been retired).

When the convert library makes it into boost I'd want to mentally deprecate
lexical_cast as a library I should use, and always use convert instead.
This means convert should have a default converter, which may well be
backed by lexical_cast with an interface such as:

    int i = boost::convert<int>::from("23").value();

This still doesn't completely satisfy me as I'd like to write

    int i = boost::convert<int>::from("23");
Again, I can't possibly stress more that the (deceivingly simple) usage examples you insist on do not exist in real applications -- they are functionally-incomplete (as they do not handle all possible use-cases). Still, with the post_review branch one can responsibly(!) write

    int i = boost::convert<int>(str, cnv).value_or(-1);

I think I can live with that idea of using lexical_cast-based converter by default... However, I personally favor explicitness/readability and, therefore,

    int i = boost::convert<int>(str, boost::lexical_cast_converter()).value_or(-1);

but it'd be an improvement in my opinion. Now I'm aware lexical_cast
doesn't have a non-throwing interface, and it probably never will, but I've
solved the problem as follows in boost::coerce:

    int i = boost::coerce::as_default<int>("XXX");
    int j = boost::coerce::as_default<int>("XXX", tag::hex());
    int k = boost::coerce::as_default<int>("XXX", 23);
    int l = boost::coerce::as_default<int>("XXX", tag::hex(), 23);

The former two returning the default-constructed int, 0,
1) For the first 2 deployments: I personally cannot justify the former two returning int(). The questionable perk of not typing a few more characters comes at the expense of introducing considerable ambiguity and potential misinterpretation.

2) For all 4 deployments: Again, no matter how temping it is to have a "simple" interface like that (similar to lexical_cast), it simply does not cut the mustard (so to speak) in real life... simply because it is not complete. Namely, it is non-deterministic:

    int k = boost::coerce::as_default<int>("-1", -1);

the latter two
returning 23. That said, I'll be the first to admit the coerce interface
isn't perfect, the fact that I need SFINAE to distinguish between
as_default calls with two parameters, the second either being a tag
(somewhat similar to your converters but not quite) or the default value
goes to show just that. However I'd prefer

    int i = boost::convert<int>::from("23", cnv);
    int i = boost::convert<int>::from_default("23", cnv, 0);

to the current interface using value_or(0). This is subjective though, and
quite possibly a matter of taste.
The "convert" signature (I keep referring to the post_review branch) is actually dead simple:

std::tr2::optional<TypeOut> convert(TypeIn const&, Converter const&);

The rest, namely, value(), value_or(), throwing behavior come from std::tr2::optional. I personally find its interface very intuitive. I do understand that you insist on a different interface for "convert"... as in lexical_cast and "coerce"... however, as I stated in the docs, in real life that API makes quite a few legitimate process/program flows difficult and awkward to implement.

In summary, before this turns into too long a rant, I think the current
user-facing interface can be improved substantially, but I'm well aware
this is subjective.
Yes, I believe it is crucially important we come up with process-flow-complete or functionally-complete(!) API. So far,

std::tr2::optional<TypeOut> convert(TypeIn const&, Converter const&);
 
seems the best. I tried to explain why alternative examples you provided do not seem to always work.

-- What is your evaluation of the implementation?
...
Snipped for brevity. I agree with all mentioned points and will be addressing them if we decide to go ahead with "convert".

In my defence it all was put together in haste (especially converters). What is *actually* important to me at this stage is nailing the "convert" interface which so far is

std::tr2::optional<TypeOut> convert(TypeIn const&, Converter const&);

If we decide to go ahead with "convert", I'll be addressing all comments so far to the best of my abilities. Putting converters into boost::converter namespace sounds like a good idea... and I am hoping that people with more specific knowledge step in and provide excellent converters catering for different purposes... like coerce-based? :-)
 
And finally:

-- Do you think the library should be accepted as a Boost library?

In my previous review this was a straight no, stating that the
functionality should be added to boost::lexical_cast. With the current
design supporting multiple backends this no longer holds true as I've
stated in the evaluation of the potential usefulness of the library.

Again, the library is quite useful, but, I so strongly disagree with the
current user-facing interface that I can't straight-out accept it. Once the
library is accepted this interface is set in stone and like marriage it's
"speak now or forever hold your peace".

As such I'm voting YES, CONDITIONALLY on the interface being simplified for
the seemingly simple cases as most of my other comments were minor or quite
trivial to fix.
Well, I personally do not believe "speak now or forever hold your peace" holds true and stops any potential improvements if/when necessary. Given your familiarity with Spirit I'd expect you to be aware of it going through major incompatible revisions over time. And Spirit is not an exception here.

Here, again, IMO the "convert" interface is the center-piece of "convert" design. Converters' signature, et al. are secondary and can change without affecting users (much). The "convert" interface is crucial. So, if you "so strongly disagree" with it, you cannot possibly vote "yes"... even conditionally... especially when your "condition" is to abandon the existing design. :-) Having said that I feel it needs to be pointed out that saying "I do not like it" without providing a suitable alternative is not sufficient. Unfortunately, so far I have not seen any serious alternatives to the existing proposed "convert" API. Please do not get me wrong. Give us an interface covering all use-cases (hopefully mentioned in the docs). If community decides it's better, I'll incorporate it.

With that out of the way I'd like to propose the following if you're
interested. Let's have a look at whether it may be possible to merge the
functionally of boost::convert and boost::coerce, as they seem to have
grown towards each other with the possibility to support multiple backends.
Two years ago I'd never have proposed this, but I've mellowed a bit since
then.

You seem to have focused on backends primarily backed by std::stringstream
and friends whereas I have focused on a backend backed by boost::spirit.
These may end up complementing leading to a single library capable of
supporting both, being more generic and powerful to the user when done
right.

Regardless of whether we end up going this way I'd love to have a
discussion on the interface, either on the mailing list or offline as this
is currently a major stumbling block for me.
Work together? Sure. Again, I actually see this proposal as an attempt to gather all input that I might incorporate for everyone's benefit. So, in this review I am not a "visionary" but the requirements gatherer and implementer. :-)  For me it's not about ego but about having something community-accepted and standardized (I am a sucker for standardization and unification and homogenization :-))

That said, you might like to soften a bit your "so strongly disagree" stance. :-) Everyone brings their designs to the table. We pick the most functionally-complete. Nothing personal.

What I see important at this stage is to settle on the "convert" user API. Currently,

std::tr2::optional<TypeOut> convert(TypeIn const&, Converter const&);

Then, I'd focus on std::sstream-based converter and you might bring coerce-based one for everyone's benefit. What do you think?

The discussion of the converter signature would be close second on the list. I am resisting but inevitably drifting towards the signature Alex proposed:

void operator()(TypeIn const&, std::tr2::optional<TypeOut>&);

as it seems potentially more efficient and less restrictive than current

bool operator()(TypeIn const&, TypeOut&);

and my attempts to deploy

std::tr2::optional<TypeOut> operator()(TypeIn const&);

signature fail to cover all deployment cases so far.

Your contributions are most welcome.
Reply | Threaded
Open this post in threaded view
|

Re: [review] Convert library

Nelson, Erik - 2
Vladimir Batov wrote on Saturday, May 24, 2014 9:40 PM

>> Habraken wrote
>> The most trivial use of boost::convert, and boost::lexical_cast and
>> boost::coerce simply don't compare in my opinion.
>>
>>     boost::cstringstream_converter cnv;
>>     int i = boost::convert <int>::from("23", cnv).value();
>>
>> versus
>>
>>     int i = boost::lexical_cast<int>("23");
>>     int j = boost::coerce::as<int>("23");
>
>Here we go again... :-) lexical_cast will be haunting me to my grave. :-)
>
>For starters, please stop using that silly lexical_cast deployment example as our benchmark. Such deployment does not exist. >lexical_cast cannot possibly be deployed like that in any real application. The correct and responsible deployment is
>
>int i = -1;
>int j = -1;
>
>try { i = boost::lexical_cast<int>(str); } catch (...) {} try { j = boost::lexical_cast<int>(str); } catch (...) {}
>
>which is not exactly the two-liner that you sited! Compared to that the "convert" usage is
>
>    boost::cstringstream_converter cnv;
>    int i = boost::convert<int>(str, cnv).value_or(-1);
>    int j = boost::convert<int>(str, cnv).value_or(-1);
>
>does look simple and intuitive enough IMO, configurable and potentially more efficient. (In the post_review branch the >convert::from API has been retired).

I guess I'm silly, but such deployment does exist in my code base, to good effect.  Convert "something" to "something else".  If it doesn't work, throw an exception.  That's a typical C++ idiom.  Any implementation that requires some cryptic object-creation-before-I-call-a-function doesn't make sense to me.  Your example adds default-value functionality, which is usually (in my use cases) inappropriate.

>> When the convert library makes it into boost I'd want to mentally
>> deprecate lexical_cast as a library I should use, and always use
>> convert instead.
>> This means convert should have a default converter, which may well be
>> backed by lexical_cast with an interface such as:
>>
>>     int i = boost::convert<int>::from("23").value();
>>
>> This still doesn't completely satisfy me as I'd like to write
>>
>>     int i = boost::convert<int>::from("23");
>
>Again, I can't possibly stress more that the (deceivingly simple) usage examples you insist on do not exist in real >applications -- they are functionally-incomplete (as they do not handle all possible use-cases).
>Still, with the post_review branch one can responsibly(!) write
>
>    int i = boost::convert<int>(str, cnv).value_or(-1);
>

I can't possibly stress more that those cases do exist.  I know that in my application, I almost never know some default value_or, so if the conversion doesn't work, I *want* an exception.  Forcing the programmer to decide appropriate default values for every conversion isn't silly- I don't know, I just want a valid conversion or an exception.

At any rate, I largely agree with Jeroen's points, and I find the Convert interface excessively complex.  Specifically, if I have to create a conversion object that I then immediately pass as an argument to a conversion function, that seems excessively complex to me.

Erik

----------------------------------------------------------------------
This message, and any attachments, is for the intended recipient(s) only, may contain information that is privileged, confidential and/or proprietary and subject to important terms and conditions available at http://www.bankofamerica.com/emaildisclaimer.   If you are not the intended recipient, please delete this message.

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: [review] Convert library

Vladimir Batov
Nelson, Erik - 2 wrote
Vladimir Batov wrote on Saturday, May 24, 2014 9:40 PM

>> Habraken wrote
>> ...
>> This still doesn't completely satisfy me as I'd like to write
>>
>>     int i = boost::convert<int>::from("23");
>
>Again, I can't possibly stress more that the (deceivingly simple) usage examples you insist on do not exist in real >applications -- they are functionally-incomplete (as they do not handle all possible use-cases).
>Still, with the post_review branch one can responsibly(!) write
>
>    int i = boost::convert<int>(str, cnv).value_or(-1);

I can't possibly stress more that those cases do exist.  I know that in my application, ...
Well, we seem to be playing that echo-chamber game. If so, then "I can't possibly stress more that", if you read Jeroen's post and my reply again, then you'll notice that he provided

int i = boost::lexical_cast<int>("23");
int j = boost::coerce::as<int>("23");

as the "benchmark" usage examples. I replied that those usage cases do not exist because in real life they are rather uglier:

int i = -1;

try { i = boost::lexical_cast<int>(str); }
catch (...) {}

Then, you step in and say "... but such deployment does exist in my code base...  Convert "something" to "something else".  If it doesn't work, throw an exception."

Don't you describe my deployment example rather than Jeroen's?

... I almost never know some default value_or, so if the conversion doesn't work, I *want* an exception.  Forcing the programmer to decide appropriate default values for every conversion isn't silly- I don't know, I just want a valid conversion or an exception.
I dare to ask, did not even bother reading "convert" docs? No one is *forcing* you to do anything. If you *want* an exception, there is nothing stopping you from having it.

try
{
    int i = boost::lexical_cast<int>(str);
    int j = boost::convert<int>(str, cnv).value();
}
catch(...) {}

At any rate, I largely agree with Jeroen's points, and I find the Convert interface excessively complex.  Specifically, if I have to create a conversion object that I then immediately pass as an argument to a conversion function, that seems excessively complex to me.
I am not sure how to answer this. Essentially you are saying that

    int i = boost::lexical_cast<int>(str);
    int j = boost::convert<int>(str, lex_cast_converter()).value();

is "too complex" and your particular deployment so far did not need any of that additional "complexity".

I guess, what you perceive as complexity is the price one has to pay for flexibility and wide applicability -- the qualities that we usually strive to have in libraries.




Reply | Threaded
Open this post in threaded view
|

Re: [review] Convert library

Jeroen Habraken
In reply to this post by Vladimir Batov
Hi,

Having slept on this last night, and with a fresh mug of coffee in hand let
me see if I can clarify some things below.

On 25 May 2014 03:40, Vladimir Batov <[hidden email]> wrote:

> Jeroen, Thank you for your review it is very much appreciated. And, indeed,
> by your own admission you've "mellowed a bit since" a few years ago. Still,
> I see some bits left of "so strongly disagree"... should I have waited for
> a
> little bit longer? :-)
>

My first reaction was that it was hard to say, and only time would tell.

On second thought however I think you should not only have waited a little
longer, but gotten a few real world users. It's very hard to argue against
three to five real world users who are using your code in production
stating they want it in boost. At that point most of the obvious
implementation bugs should have been ironed out and they're convinced of
your interface to the point of actually using it. That's testament!

That said I'm well aware this isn't easy, I was delighted to have
boost::coerce reviewed in [4] without my input. At times a mail would also
pop up complaining my library stopped working in the latest version of,
surprise, MSVC. I have no idea who's using it though unfortunately.

Jeroen Habraken wrote

> > Hi,
> >
> > As promised my review of the boost::convert library. Note that the
> > questions were answered in a seemingly random order, thus I hope it all
> > comes together. Secondly, being the author of the boost::coerce library
> > [2][3] I may be a little biased at times, please forgive me.
>
> Now that you mention "coerce" I do remember you mentioning it back then
> during review #1. I completely forgot about it and I still can't see it in
> Boost. Now when I look for "coerce" Google only gives 2012 links. :-(


I've been meaning to put the finishing touches on it and get it reviewed
but then life happened, and as I'm sure you're perfectly aware by now these
reviews are no walk in the park.

> -- What is your evaluation of the design?
> >
> > There are two parts to this, the design of the backend and the design of
> > the interface that the user predominantly sees. I have little to say on
> > the
> > first, the notion of multiple backend converters is a big step forwards
> > and
> > their interface is sane.
> >
> > I'm far less convinced when it comes to the user-facing interface as I
> > believe the simple tasks should be simple and intuitive, whereas the
> > harder
> > tasks may be harder.
> >
> > The most trivial use of boost::convert, and boost::lexical_cast and
> > boost::coerce simply don't compare in my opinion.
> >
> >     boost::cstringstream_converter cnv;
> >     int i = boost::convert
> > <int>
> > ::from("23", cnv).value();
> >
> > versus
> >
> >     int i = boost::lexical_cast
> > <int>
> > ("23");
> >     int j = boost::coerce::as
> > <int>
> > ("23");
>
> Here we go again... :-) lexical_cast will be haunting me to my grave. :-)
>
> For starters, please stop using that silly lexical_cast deployment example
> as our benchmark. Such deployment does not exist. lexical_cast cannot
> possibly be deployed like that in any real application. The correct and
> responsible deployment is
>
> int i = -1;
> int j = -1;
>
> try { i = boost::lexical_cast<int>(str); }
> catch (...) {}
> try { j = boost::lexical_cast<int>(str); }
> catch (...) {}
>
> which is not exactly the two-liner that you sited! Compared to that the
> "convert" usage is
>
>     boost::cstringstream_converter cnv;
>     int i = boost::convert<int>(str, cnv).value_or(-1);
>     int j = boost::convert<int>(str, cnv).value_or(-1);
>
> does look simple and intuitive enough IMO, configurable and potentially
> more
> efficient. (In the post_review branch the convert::from API has been
> retired).


This is a fair point but as other have argued boost::lexical_cast is used
without the try-catch in a lot of places. Simply expecting it not to throw
and dropping everything on the ground when it does isn't the nicest way to
go about things but it does get things done.

I guess the question is whether we should make it harder to use the library
in such a way, and I think we shouldn't. If a users wants to potentially
blow off a foot that's their call.

 > When the convert library makes it into boost I'd want to mentally

> > deprecate
> > lexical_cast as a library I should use, and always use convert instead.
> > This means convert should have a default converter, which may well be
> > backed by lexical_cast with an interface such as:
> >
> >     int i = boost::convert
> > <int>
> > ::from("23").value();
> >
> > This still doesn't completely satisfy me as I'd like to write
> >
> >     int i = boost::convert
> > <int>
> > ::from("23");
>
> Again, I can't possibly stress more that the (deceivingly simple) usage
> examples you insist on do not exist in real applications -- they are
> functionally-incomplete (as they do not handle all possible use-cases).
> Still, with the post_review branch one can responsibly(!) write
>
>     int i = boost::convert<int>(str, cnv).value_or(-1);
>
> I think I can live with that idea of using lexical_cast-based converter by
> default... However, I personally favor explicitness/readability and,
> therefore,
>
>     int i = boost::convert<int>(str,
> boost::lexical_cast_converter()).value_or(-1);


Here I disagree but this is a matter of taste. Let me propose the
following, a customisation point where users can choose their default
converter within their application, defaulting to the
lexical_cast_converter. This prevents people from having to instantiate a
converter everywhere, either explicitly or inline, and it gives them the
option to switch the converter for their whole application in a single
place.

> but it'd be an improvement in my opinion. Now I'm aware lexical_cast
> > doesn't have a non-throwing interface, and it probably never will, but
> > I've
> > solved the problem as follows in boost::coerce:
> >
> >     int i = boost::coerce::as_default
> > <int>
> > ("XXX");
> >     int j = boost::coerce::as_default
> > <int>
> > ("XXX", tag::hex());
> >     int k = boost::coerce::as_default
> > <int>
> > ("XXX", 23);
> >     int l = boost::coerce::as_default
> > <int>
> > ("XXX", tag::hex(), 23);
> >
> > The former two returning the default-constructed int, 0,
>
> 1) For the first 2 deployments: I personally cannot justify the former two
> returning int(). The questionable perk of not typing a few more characters
> comes at the expense of introducing considerable ambiguity and potential
> misinterpretation.
>
> 2) For all 4 deployments: Again, no matter how temping it is to have a
> "simple" interface like that (similar to lexical_cast), it simply does not
> cut the mustard (so to speak) in real life... simply because it is not
> complete. Namely, it is non-deterministic:
>
>     int k = boost::coerce::as_default<int>("-1", -1);
>
>
> > the latter two
> > returning 23. That said, I'll be the first to admit the coerce interface
> > isn't perfect, the fact that I need SFINAE to distinguish between
> > as_default calls with two parameters, the second either being a tag
> > (somewhat similar to your converters but not quite) or the default value
> > goes to show just that. However I'd prefer
> >
> >     int i = boost::convert
> > <int>
> > ::from("23", cnv);
> >     int i = boost::convert
> > <int>
> > ::from_default("23", cnv, 0);
> >
> > to the current interface using value_or(0). This is subjective though,
> and
> > quite possibly a matter of taste.
>
> The "convert" signature (I keep referring to the post_review branch) is
> actually dead simple:
>
> std::tr2::optional<TypeOut> convert(TypeIn const&, Converter const&);
>
> The rest, namely, value(), value_or(), throwing behavior come from
> std::tr2::optional. I personally find its interface very intuitive. I do
> understand that you insist on a different interface for "convert"... as in
> lexical_cast and "coerce"... however, as I stated in the docs, in real life
> that API makes quite a few legitimate process/program flows difficult and
> awkward to implement.
>
>
> > In summary, before this turns into too long a rant, I think the current
> > user-facing interface can be improved substantially, but I'm well aware
> > this is subjective.
>
> Yes, I believe it is crucially important we come up with
> process-flow-complete or functionally-complete(!) API. So far,
>
> std::tr2::optional<TypeOut> convert(TypeIn const&, Converter const&);
>
> seems the best. I tried to explain why alternative examples you provided do
> not seem to always work.


Let me respond to this below.


>  > -- What is your evaluation of the implementation?
> > ...
>
> Snipped for brevity. I agree with all mentioned points and will be
> addressing them if we decide to go ahead with "convert".
>
> In my defence it all was put together in haste (especially converters).
> What
> is *actually* important to me at this stage is nailing the "convert"
> interface which so far is
>
> std::tr2::optional<TypeOut> convert(TypeIn const&, Converter const&);
>
> If we decide to go ahead with "convert", I'll be addressing all comments so
> far to the best of my abilities. Putting converters into boost::converter
> namespace sounds like a good idea... and I am hoping that people with more
> specific knowledge step in and provide excellent converters catering for
> different purposes... like coerce-based? :-)
>

As said most of it was minor at best, and I wholeheartedly agree nailing
the interface is the most important at this point. It worries me a little
that the converters were put together in haste, these things shouldn't be
rushed.

> And finally:
> >
> > -- Do you think the library should be accepted as a Boost library?
> >
> > In my previous review this was a straight no, stating that the
> > functionality should be added to boost::lexical_cast. With the current
> > design supporting multiple backends this no longer holds true as I've
> > stated in the evaluation of the potential usefulness of the library.
> >
> > Again, the library is quite useful, but, I so strongly disagree with the
> > current user-facing interface that I can't straight-out accept it. Once
> > the
> > library is accepted this interface is set in stone and like marriage it's
> > "speak now or forever hold your peace".
> >
> > As such I'm voting YES, CONDITIONALLY on the interface being simplified
> > for
> > the seemingly simple cases as most of my other comments were minor or
> > quite
> > trivial to fix.
>
> Well, I personally do not believe "speak now or forever hold your peace"
> holds true and stops any potential improvements if/when necessary. Given
> your familiarity with Spirit I'd expect you to be aware of it going through
> major incompatible revisions over time. And Spirit is not an exception
> here.
>

That's true, however such a major revision should not be required within
the first years of inclusion.


> Here, again, IMO the "convert" interface is the center-piece of "convert"
> design. Converters' signature, et al. are secondary and can change without
> affecting users (much). The "convert" interface is crucial. So, if you "so
> strongly disagree" with it, you cannot possibly vote "yes"... even
> conditionally... especially when your "condition" is to abandon the
> existing
> design. :-) Having said that I feel it needs to be pointed out that saying
> "I do not like it" without providing a suitable alternative is not
> sufficient. Unfortunately, so far I have not seen any serious alternatives
> to the existing proposed "convert" API. Please do not get me wrong. Give us
> an interface covering all use-cases (hopefully mentioned in the docs). If
> community decides it's better, I'll incorporate it.


Let me first say that I probably worded the "so strongly disagree" too
harshly, and note that I didn't vote against the library, just the current
interface. This is getting to the meat of the discussion though and looking
back there are two distinct points I'm trying to make.

First of all I think there should be a default converter, not requiring
people to explicitly construct it everywhere. Above I proposed a
customisation point where people can select the default converter for their
project, defaulting to the boost::lexical_cast based converter. This feels
like a fair middle-ground to me offering a more powerful interface to the
user, and we can have the bike shed discussion on which converter it should
default to. The lexical_cast one makes sense to me.

Secondly I'm not a big fan of the .value() and .value_or(-1) interface but
I can live with this. Especially since the std::optional also has the
operator* and operator-> allowing

    int i = *boost::convert<int>::from("23");

which will make sense when people become more accustomed to std::optional.
Note that for this to be efficient I'd like to see the optional to be
movable, which isn't yet the case with boost::optional if I'm not mistaken.

 > With that out of the way I'd like to propose the following if you're

> > interested. Let's have a look at whether it may be possible to merge the
> > functionally of boost::convert and boost::coerce, as they seem to have
> > grown towards each other with the possibility to support multiple
> > backends.
> > Two years ago I'd never have proposed this, but I've mellowed a bit since
> > then.
> >
> > You seem to have focused on backends primarily backed by
> std::stringstream
> > and friends whereas I have focused on a backend backed by boost::spirit.
> > These may end up complementing leading to a single library capable of
> > supporting both, being more generic and powerful to the user when done
> > right.
> >
> > Regardless of whether we end up going this way I'd love to have a
> > discussion on the interface, either on the mailing list or offline as
> this
> > is currently a major stumbling block for me.
>
> Work together? Sure. Again, I actually see this proposal as an attempt to
> gather all input that I might incorporate for everyone's benefit. So, in
> this review I am not a "visionary" but the requirements gatherer and
> implementer. :-)  For me it's not about ego but about having something
> community-accepted and standardized (I am a sucker for standardization and
> unification and homogenization :-))
>
> That said, you might like to soften a bit your "so strongly disagree"
> stance. :-) Everyone brings their designs to the table. We pick the most
> functionally-complete. Nothing personal.
>

As said above that was worded unnecessarily harsh, and wasn't mean that way.


> What I see important at this stage is to settle on the "convert" user API.
> Currently,
>
> std::tr2::optional<TypeOut> convert(TypeIn const&, Converter const&);
>
> Then, I'd focus on std::sstream-based converter and you might bring
> coerce-based one for everyone's benefit. What do you think?
>

Exactly, once we nail the interface this is where we should be heading.
Since you mentioned rushing the converters I suspect they may need a little
love, and I can definitely integrate boost::coerce into boost::convert.

I should expand on this as I don't mean simply adding a converter which
calls coerce but fully porting over its functionality to convert. At some
point I argued that coerce its functionality may be added to boost::spirit,
but this would be a better home.


> The discussion of the converter signature would be close second on the
> list.
> I am resisting but inevitably drifting towards the signature Alex proposed:
>
> void operator()(TypeIn const&, std::tr2::optional<TypeOut>&);
>
> as it seems potentially more efficient and less restrictive than current
>
> bool operator()(TypeIn const&, TypeOut&);
>
> and my attempts to deploy
>
> std::tr2::optional<TypeOut> operator()(TypeIn const&);
>
> signature fail to cover all deployment cases so far.
>
> Your contributions are most welcome.
>

I'm slowly warming up to the optional-based interface as it eliminates the
throwing / non-throwing interface discussion, which is a good thing. Once
again two comments, first of which is what's going to happen until the
std::optional (whether in TR2 or elsewhere) is widely accepted. The
optional provided by boost has a slightly different interface and isn't
movable.

The second is performance, with boost::coerce a lot of time was invested to
prevent the interface from slowing down the conversion itself. I simply
don't know how this interface will behave in that regard, when I have more
time I'll try some benchmarking.

Jeroen

[4] http://www.kumobius.com/2013/08/c-string-to-int/

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: [review] Convert library

Vladimir Batov
Jeroen, Thank you for your input. My apologies for snipping (a lot) -- I want to focus on the immediate. If we clear this one up, we'll get to the rest next. If we do not clear this one up, we won't get to the rest. :-)

Jeroen Habraken wrote
...

as other have argued boost::lexical_cast is used
without the try-catch in a lot of places. Simply expecting it not to throw
and dropping everything on the ground when it does isn't the nicest way to
go about things but it does get things done.
Jeroen, seriously, we are serious people writing serious s/w. Should we be discussing children playing with matches here? :-)

I guess the question is whether we should make it harder to use the library
in such a way, and I think we shouldn't.
It is the wrong angle IMO. First, we should make it work properly. Then we make it as simple to use as possible within the constraints imposed by the first point.

Note that for this to be efficient I'd like to see the optional to be
movable, which isn't yet the case with boost::optional if I'm not mistaken.
You might have noticed that I mention std::tr2::optional... which has what you are after. Andrzej is working on incorporating/backporting those features to boost::optional. "Life happened" to him also :-) but I am hoping, if we are extra nice, he might just might to get the interface we are after into 1.56. Andrzej, ple-e-e-ease, pretty pretty please!?

I'm slowly warming up to the optional-based interface as it eliminates the
throwing / non-throwing interface discussion, which is a good thing. Once
again two comments, first of which is what's going to happen until the
std::optional (whether in TR2 or elsewhere) is widely accepted. The
optional provided by boost has a slightly different interface and isn't
movable.
It is

std::tr2::optional<TypeOut> convert(TypeIn const&, Converter const&);

you are warming up to, right? If it is so, I am really glad to hear that. I am not married to it but all things considered it seems the most generic and flexible, functionally-complete, not too verbose... and sufficiently close to lexical_cast :-) for people not to feel alienated.

Given the immense variability of conversion-related deployments I see this interface as the only one that everyone can deploy -- exceptions? no problem; delayed exceptions? easy; no exceptions but failure-condition returned instead? piece of cake; can't be bothered processing the failure-condition and wanna proceed with the default? coming right up; want to process failure but still go with the default? anything to keep the user happy!

So far, no one (to my knowledge) has come up with another interface which simpler without sacrificing those mentioned important qualities (given we are talking about a library-grade interface).

The second is performance, with boost::coerce a lot of time was invested to
prevent the interface from slowing down the conversion itself. I simply
don't know how this interface will behave in that regard, when I have more
time I'll try some benchmarking.
Ah, that should not be a problem. I don't think. I actually expect the convert() interface to be wiped out altogether when optimized. That is, all power to you, the converter writer. So, what do you say? Are we in business?.. So to speak? :-)
Reply | Threaded
Open this post in threaded view
|

Re: [review] Convert library

Roland Bock-2
In reply to this post by Vladimir Batov
Hi,

On 2014-05-25 03:40, Vladimir Batov wrote:

>> The most trivial use of boost::convert, and boost::lexical_cast and
>> > boost::coerce simply don't compare in my opinion.
>> >
>> >     boost::cstringstream_converter cnv;
>> >     int i = boost::convert
>> > <int>
>> > ::from("23", cnv).value();
>> >
>> > versus
>> >
>> >     int i = boost::lexical_cast
>> > <int>
>> > ("23");
>> >     int j = boost::coerce::as
>> > <int>
>> > ("23");
> Here we go again... :-) lexical_cast will be haunting me to my grave. :-)
>
No need for that :-)

First of all, I think I would not start each and every section of the
documentation with how to do something with lexical_cast...

Apart from that:
I think the main objection here is the amount of code you have to write
in order to get the most simple case: I have a value of type X and want
to convert it to a value of type Y.

I think it would be relatively simple to get to a much leaner interface.
For instance,

  * convert could have a converter template parameter, defaulting to the
    stringstream converter
  * convert::from() could have an overload that just takes the "from"
    parameter and provides the default-constructed converter itself.
  * convert could have a conversion operator for the OutType, which does
    the same as convert::value()

Then I would have

int i = boost::convert<int>::from("17");

As far as I can see, conversion errors are handled by

 1. throwing exception (and letting the developer handle that where and
    how appropriate)
 2. using a default value
 3. calling a function (e.g. report the problem and provide a default
    value or throw an exception)

With 1) being the default, I could have overloads for 2) and 3), e.g.

int i = boost::convert<int>::from("17", 0);
int i = boost::convert<int>::from("17", []{
   std::cerr << "conversion problem, using default" << std::endl;
   return 0;} );

And if I want to use a specific converter that, I could use something like

MyConverter cnv{some args};
int i = boost::convert<int>::with(cnv).from("17", 0);

Or, if the converter can be default constructed:

int i = boost::convert<int, MyConverter>::from("17", 0);

In a similar way you could create the functors, for instance to be used
by algorithms:

auto f = boost::convert<int>::functor();
// no need for the <string> template argument if the call operator
// is a template, I'd say

Without having looked at the code that, I assume that an API like that
would be rather easy to accomplish.



I am not sure if my opinion is strong enough to be counted as a review,
but here it comes anyway:

> What is your evaluation of the design?/
I am not too fond of the frontend API as is. There are several
suggestions for a leaner API in the reviews. I second Julian's opinion
that it would be beneficial to mention parameters in the order [from,
to] instead of [to, from].

> What is your evaluation of the implementation?

The few files I looked at seemed ok, but I did not really do a code review.

> What is your evaluation of the documentation?

Not being familiar with boost::lexical cast, I had difficulties finding
the things which are really about boost::convert (the note in "getting
started") does not help...

The documentation also leaves a lot to guesswork, IMHO. For instance,
the examples suggest, that convert<int>::result has a conversion
operator to bool which returns true, if the conversion was successful,
and false otherwise. If references to lexical_cast were omitted, there
would be enough room for explanations.

> What is your evaluation of the potential usefulness of the library?

Personally, I have to admit, that I do not really see myself using the
library as is. I would write wrappers around it to get an interface
closer to what I want. But then, I have to wonder why I don't use the
converters directly? And since the converters are mere wrappers to
existing stuff afaict, I would probably use the underlying things directly.

So unless the API get's cleaned up in a way that does not make me want
to wrap it, I see no real use.

> Did you try to use the library? With what compiler? Did you have any
problems?

No.

> How much effort did you put into your evaluation? A glance? A quick
reading? In-depth study?

About 3hours including writing this mail. Feel free not to count this as
a full review.

> Are you knowledgeable about the problem domain?

I'd say yes, having written a few converters myself, having used quite a
few more.

And finally:

    Do you think the library should be accepted as a Boost library?

I try to avoid conversions like cats try to avoid water. The library
does not make we want to go swimming, so to speak.

Yes (since I need to swim sometimes), under the condition that the API
and the documentation get cleaned up, especially

  * parameters to -> from
  * default parameter for the converter
  * ability for the user of the library to provide methods to be called
    in case of conversion error
      o non-throwing methods would return a default value
  * Consider convert to be of a value on its own, not just in comparison
    to lexical_cast. If I need be annoyed by lexical_cast in order to
    understand the value of convert, there is something wrong...
  * Actually explain the library instead of just giving examples and
    letting the reader guess.

Regards,

Roland



_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: [review] Convert library

Jeroen Habraken
In reply to this post by Vladimir Batov
On 25 May 2014 14:03, Vladimir Batov <[hidden email]> wrote:

> Jeroen, Thank you for your input. My apologies for snipping (a lot) -- I
> want
> to focus on the immediate. If we clear this one up, we'll get to the rest
> next. If we do not clear this one up, we won't get to the rest. :-)
>

You're more than welcome, you may have snipped a little too much but I'll
get to that below.


> Jeroen Habraken wrote
> > ...
> >
> > as other have argued boost::lexical_cast is used
> > without the try-catch in a lot of places. Simply expecting it not to
> throw
> > and dropping everything on the ground when it does isn't the nicest way
> to
> > go about things but it does get things done.
>
> Jeroen, seriously, we are serious people writing serious s/w. Should we be
> discussing children playing with matches here? :-)


Unfortunately I believe we should, whilst we might be writing serious
software there are enough who aren't -- you'd be surprised how often people
are using boost::lexical_cast without the try - catch at the place I
currently work. Again, unfortunately.


> > I guess the question is whether we should make it harder to use the
> > library
> > in such a way, and I think we shouldn't.
>
> It is the wrong angle IMO. First, we should make it work properly. Then we
> make it as simple to use as possible within the constraints imposed by the
> first point.


Agreed.


> > Note that for this to be efficient I'd like to see the optional to be
> > movable, which isn't yet the case with boost::optional if I'm not
> > mistaken.
>
> You might have noticed that I mention std::tr2::optional... which has what
> you are after. Andrzej is working on incorporating/backporting those
> features to boost::optional. "Life happened" to him also :-) but I am
> hoping, if we are extra nice, he might just might to get the interface we
> are after into 1.56. Andrzej, ple-e-e-ease, pretty pretty please!?


Wonderful, I'd love to see that make it into 1.56.0.


>  > I'm slowly warming up to the optional-based interface as it eliminates
> the
> > throwing / non-throwing interface discussion, which is a good thing. Once
> > again two comments, first of which is what's going to happen until the
> > std::optional (whether in TR2 or elsewhere) is widely accepted. The
> > optional provided by boost has a slightly different interface and isn't
> > movable.
>
> It is
>
> std::tr2::optional<TypeOut> convert(TypeIn const&, Converter const&);
>
> you are warming up to, right? If it is so, I am really glad to hear that. I
> am not married to it but all things considered it seems the most generic
> and
> flexible, functionally-complete, not too verbose... and sufficiently close
> to lexical_cast :-) for people not to feel alienated.
>
> Given the immense variability of conversion-related deployments I see this
> interface as the only one that everyone can deploy -- exceptions? no
> problem; delayed exceptions? easy; no exceptions but failure-condition
> returned instead? piece of cake; can't be bothered processing the
> failure-condition and wanna proceed with the default? coming right up; want
> to process failure but still go with the default? anything to keep the user
> happy!
>
> So far, no one (to my knowledge) has come up with another interface which
> simpler without sacrificing those mentioned important qualities (given we
> are talking about a library-grade interface).


Close, I'd like an overload without the Converter const & as well,
defaulting to a converter chosen via a customisation point. This is what I
believe you've unintentionally snipped, and I'm curious as to your opinion
on it as I believe it to be a crucial part of the interface.


> > The second is performance, with boost::coerce a lot of time was invested
> > to
> > prevent the interface from slowing down the conversion itself. I simply
> > don't know how this interface will behave in that regard, when I have
> more
> > time I'll try some benchmarking.
>
> Ah, that should not be a problem. I don't think. I actually expect the
> convert() interface to be wiped out altogether when optimized. That is, all
> power to you, the converter writer. So, what do you say? Are we in
> business?.. So to speak? :-)
>

We're getting there.

Jeroen

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: [review] Convert library

Hartmut Kaiser
In reply to this post by Vladimir Batov
First of all, let me tip my hat to Jeroen for his very thorough and
thoughtful review. I fully agree with all what he said.

Let me also get out of the way that I have not looked at the library itself.
'm just following the discussions.

> Jeroen, Thank you for your input. My apologies for snipping (a lot) -- I
> want
> to focus on the immediate. If we clear this one up, we'll get to the rest
> next. If we do not clear this one up, we won't get to the rest. :-)
>
> Jeroen Habraken wrote
> > ...
> >
> > as other have argued boost::lexical_cast is used
> > without the try-catch in a lot of places. Simply expecting it not to
> throw
> > and dropping everything on the ground when it does isn't the nicest way
> to
> > go about things but it does get things done.
>
> Jeroen, seriously, we are serious people writing serious s/w. Should we be
> discussing children playing with matches here? :-)

A large part of the software I write (which is no child's play, at least in
my eyes) needs simple solutions to complex problems. The simpler a solution
the better. I as a library writer often leave the handling of exceptions
which are unrelated to the task at hand to the user of my library. Thus
having a conversion throw an exception deep in the guts of my library is a
perfect solution. This is particularly true if my library has no way of
telling what would be a proper default value.

I might be biased as I feel to have my share in the interface design of
Jeroen's Coerce library. From that experience (and all the other library
work I'm doing) I wouldn't like to have library interfaces which are more
verbose than absolutely needed. That said, I very much like the interface
design of Coerce, as it is as straight as it can get for the common case and
allows to customize its behavior with little effort, without me having to
remember shrouded syntax.

Also, as Jeroen stated, library interfaces should not get in the way of
performance. But more about this further down.

> > I guess the question is whether we should make it harder to use the
> > library
> > in such a way, and I think we shouldn't.
>
> It is the wrong angle IMO. First, we should make it work properly. Then we
> make it as simple to use as possible within the constraints imposed by the
> first point.
>
> > Note that for this to be efficient I'd like to see the optional to be
> > movable, which isn't yet the case with boost::optional if I'm not
> > mistaken.
>
> You might have noticed that I mention std::tr2::optional... which has what
> you are after. Andrzej is working on incorporating/backporting those
> features to boost::optional. "Life happened" to him also :-) but I am
> hoping, if we are extra nice, he might just might to get the interface we
> are after into 1.56. Andrzej, ple-e-e-ease, pretty pretty please!?

Honestly, when I first realized your convert() returns an optional I felt
something like revulsion. This is for two reasons:
 
  a) While an optional exposes an operator*() and an operator->(), the
resulting conversion syntax turns into something more complex than it has to
be.
  b) Using optional means you need an additional allocation (at least
sometimes if you have an improved version using small object optimization
internally). I tend to say that this alone can turn into a knockout criteria
for a supposedly high performance, low level conversion library. However I
have not done any measurements - so all this is pure conjecture.

There have been done plenty of conversion library comparisons by different
people. It might be a good idea to show performance results of your solution
comparing to the existing libraries.

> > I'm slowly warming up to the optional-based interface as it eliminates
> the
> > throwing / non-throwing interface discussion, which is a good thing.
> Once
> > again two comments, first of which is what's going to happen until the
> > std::optional (whether in TR2 or elsewhere) is widely accepted. The
> > optional provided by boost has a slightly different interface and isn't
> > movable.
>
> It is
>
> std::tr2::optional<TypeOut> convert(TypeIn const&, Converter const&);
>
> you are warming up to, right? If it is so, I am really glad to hear that.
> I
> am not married to it but all things considered it seems the most generic
> and
> flexible, functionally-complete, not too verbose... and sufficiently close
> to lexical_cast :-) for people not to feel alienated.
>
> Given the immense variability of conversion-related deployments I see this
> interface as the only one that everyone can deploy -- exceptions? no
> problem; delayed exceptions? easy; no exceptions but failure-condition
> returned instead? piece of cake; can't be bothered processing the
> failure-condition and wanna proceed with the default? coming right up;
> want
> to process failure but still go with the default? anything to keep the
> user
> happy!
>
> So far, no one (to my knowledge) has come up with another interface which
> simpler without sacrificing those mentioned important qualities (given we
> are talking about a library-grade interface).

See Jeroen's Coerce library for an example. It's as simple as lexical_cast
or as complex as a full blown Spirit parser and everything in between.

> > The second is performance, with boost::coerce a lot of time was invested
> > to
> > prevent the interface from slowing down the conversion itself. I simply
> > don't know how this interface will behave in that regard, when I have
> more
> > time I'll try some benchmarking.
>
> Ah, that should not be a problem. I don't think. I actually expect the
> convert() interface to be wiped out altogether when optimized.

I used to think that as well. Sadly, more often than not compilers are not
able to optimize all of it. But there is hope that they will get better over
time. At least I would like to see numbers confirming your statement.

Regards Hartmut
---------------
http://boost-spirit.com
http://stellar.cct.lsu.edu




_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: [review] Convert library

Thijs (M.A.) van den Berg
In reply to this post by Roland Bock-2


On May 25, 2014, at 2:47 PM, Roland Bock <[hidden email]> wrote:

> I think it would be relatively simple to get to a much leaner interface.
> For instance,
>
>  * convert could have a converter template parameter, defaulting to the
>    stringstream converter
>  * convert::from() could have an overload that just takes the "from"
>    parameter and provides the default-constructed converter itself.

In C++11 has std::to_string and various flavours of stoul, stoull, stof. To me it makes more sense to use those for conversions between strings and integral types. Not just because of portability, but also because the interface is so simple.

The value of convert to me seems to be more in the context of generic programming for a wide range of types with a uniform interface than for a simple interface for specific types for which there are already simple & standard alternatives.

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: [review] Convert library

feverzsj
In reply to this post by Roland Bock-2
I agree the converter concept is a step forward. And it's also a good idea to support both exception and defualt value on failure. The functor interface is useful, too.
But as other reviewer mentioned, the convert interface seems less intuitive and lack a default convenient interface.

Personally, I'd prefer something like:
 
// cast like interface
T t = conv_to<T>(from, cnv);                    // compare to T t = convert<T>::from(from, cnv).value();
T t = conv_to<T>(from, default_v, cnv);    // compare to T t = convert<T>::from(from, cnv).value_or(default_v);

// plain interface
bool re = convert(from, to, cnv);

// functor interface
std::transfrom(first, last, out, convert<T>(from, cnv)));
std::transfrom(first, last, out, convert<T>(from, default_v, cnv)));

** with defualt interface version, "cnv" will be ignored

Using only two names, we can get something simple and intuitive. The implemention can be also effectively simplfied.


regards.

Reply | Threaded
Open this post in threaded view
|

Re: [review] Convert library

feverzsj
I'm sorry. There is a copy-paste mistake in previous mail:

>> // functor interface
>> std::transfrom(first, last, out, convert<T>(from, cnv)));
>> std::transfrom(first, last, out, convert<T>(from, default_v, cnv)));

should be:

// functor interface
std::transfrom(first, last, out, convert<T>(cnv)));
std::transfrom(first, last, out, convert<T>(default_v, cnv)));


regards.
Reply | Threaded
Open this post in threaded view
|

Re: [review] Convert library

Vladimir Batov-5
In reply to this post by Thijs (M.A.) van den Berg

On 05/26/2014 12:59 AM, Thijs (M.A.) van den Berg wrote:

> On May 25, 2014, at 2:47 PM, Roland Bock <[hidden email]> wrote:
>
>> I think it would be relatively simple to get to a much leaner interface.
>> For instance,
>>
>>   * convert could have a converter template parameter, defaulting to the
>>     stringstream converter
>>   * convert::from() could have an overload that just takes the "from"
>>     parameter and provides the default-constructed converter itself.
> In C++11 has std::to_string and various flavours of stoul, stoull, stof. To me it makes more sense to use those for conversions between strings and integral types. Not just because of portability, but also because the interface is so simple.
>
> The value of convert to me seems to be more in the context of generic programming for a wide range of types with a uniform interface than for a simple interface for specific types for which there are already simple & standard alternatives.
>

Indeed. Thank you for summing it up. When *I* try answering it turns
into 4-volume "War and Piece" and by the time I finish one half is
asleep and the other has left. :-)

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: [review] Convert library

Vladimir Batov-5
In reply to this post by feverzsj

On 05/26/2014 04:04 AM, feverzsj wrote:

> I agree the converter concept is a step forward. And it's also a good idea to
> support both exception and defualt value on failure. The functor interface
> is useful, too.
> But as other reviewer mentioned, the convert interface seems less intuitive
> and lack a default convenient interface.
>
> Personally, I'd prefer something like:
>  
> // cast like interface
> T t = conv_to<T>(from, cnv);                    // compare to T t =
> convert<T>::from(from, cnv).value();
> T t = conv_to<T>(from, default_v, cnv);    // compare to T t =
> convert<T>::from(from, cnv).value_or(default_v);
>
> // plain interface
> bool re = convert(from, to, cnv);
>
> // functor interface
> std::transfrom(first, last, out, convert<T>(cnv)));
> std::transfrom(first, last, out, convert<T>(default_v, cnv)));
>
> ** with defualt interface version, "cnv" will be ignored
>
> Using only two names, we can get something simple and intuitive. The
> implemention can be also effectively simplfied.
>

1) I do like

T t = convert<T>(from, cnv); // Throws on failure
T t = convert<T>(from, default_v, cnv); // Returns 'def' on failure

and, if my memory serves me, that was pretty much review#1 interface
(minus converters). What it misses though is handling one use-case --
deterministic non-throwing handling of conversion failure. When #2
returns default_v, we do not know if it is due to a failure or "from"
just happened to convert to "default_v".

2) I can see two potential problems with a converter provided by default:

a) it might be tricky separating

convert(TypeIn const&, Converter const& =some_default());
convert(Converter const& =some_default());

b) different people might feel quite strongly what converter to be the
default... and I'll be on the receiving end of their fury. :-(



_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: [review] Convert library

Vladimir Batov-5
In reply to this post by Hartmut Kaiser
Hartmut,

Thank you for your input. It's much appreciated. From the critical tone
of your post I'll take it you are voting "no". If you did not state it
by accident, then I am happy to do that for you. If you did not state it
intentionally, then I think you should as you should not be shying away
from your community responsibilities. :-) Please find my replies below.

On 05/26/2014 12:37 AM, Hartmut Kaiser wrote:

> First of all, let me tip my hat to Jeroen for his very thorough and
> thoughtful review. I fully agree with all what he said.
>
> Let me also get out of the way that I have not looked at the library itself.
> 'm just following the discussions.
>
>> Jeroen, Thank you for your input. My apologies for snipping (a lot) -- I
>> want
>> to focus on the immediate. If we clear this one up, we'll get to the rest
>> next. If we do not clear this one up, we won't get to the rest. :-)
>>
>> Jeroen Habraken wrote
>>> ...
>>>
>>> as other have argued boost::lexical_cast is used
>>> without the try-catch in a lot of places. Simply expecting it not to
>> throw
>>> and dropping everything on the ground when it does isn't the nicest way
>> to
>>> go about things but it does get things done.
>> Jeroen, seriously, we are serious people writing serious s/w. Should we be
>> discussing children playing with matches here? :-)
> A large part of the software I write (which is no child's play, at least in
> my eyes) needs simple solutions to complex problems. The simpler a solution
> the better.

Well, no one argues that "the simpler a solution the better" and we all
want "simple solutions to complex problems" and "more pay for less
work", etc. Unfortunately, it is not always the case, is it? Surely, you
are not saying that you won't accept a moderate solution to your complex
problem if you can't find a simple one.

> I as a library writer often leave the handling of exceptions
> which are unrelated to the task at hand to the user of my library. Thus
> having a conversion throw an exception deep in the guts of my library is a
> perfect solution. This is particularly true if my library has no way of
> telling what would be a proper default value.

Yes, your deployment case (of a library developer) makes sense to me.
Unfortunately, my deployment cases (of an application developer) are
quite different. In my neck of woods reading invalid configuration
parameter and abandoning the ship with an exception is, well, an
overkill (put very mildly). What I am driving at is that we have wildly
differing deployment scenarios. And IMO a library (generic solution)
should be able to cater for them all. The conundrum a conversion-related
library in general is inevitably facing (and seemingly cannot solve...
as we have none so far) is that every single user comes in and says --
my deployment use-case is dead-simple; why should I deploy that
"complicated" API instead? IMO that "small-town" thinking has no place
in commercial s/w development where modularization and generic solutions
are quite important... even probably at the expense of efficiency, etc.
Please do not take it as an insult. It's not directed at you or anyone
in particular. It's merely my experience in managing considerable
projects with finite budgets and rigid timeframes and revolving staff
and real customers breathing down your neck for a reliably working solution.

> I might be biased as I feel to have my share in the interface design of
> Jeroen's Coerce library. From that experience (and all the other library
> work I'm doing) I wouldn't like to have library interfaces which are more
> verbose than absolutely needed. That said, I very much like the interface
> design of Coerce, as it is as straight as it can get for the common case and
> allows to customize its behavior with little effort, without me having to
> remember shrouded syntax.

With all due respect this reminds me of the situation during review#1
when the Spirit team was very vocal and uniform (and did not mince their
words) in their rejection of the proposal siting that Spirit was opening
up unheard-of horizons in the conversion domain. Feel free to call me a
dimwit but I personally find Spirit's syntax arcane and using your words
"more verbose than absolutely needed" for my conversion purposes. Three
years down the track and we are here with you expressing love for the
library (Version: 0.2, Last upload: 2010 October 07) that I cannot
possibly responsibly use in our commercial development environment due
to lack of penetration, reliable support, active development and
adequate (for my purposes) flexibility, extensibility and
functional-completeness. And, curiously, it does not seem to handle *my*
mundane deployment pattern -- non-throwing failure-detection:

optional<T> res = convert<T>(from, cnv);

if (!res)
{
     log("Invalid ignored. Proceeding with the default");
     res = some_default;
}

> Also, as Jeroen stated, library interfaces should not get in the way of
> performance. But more about this further down.
>
>>> I guess the question is whether we should make it harder to use the
>>> library
>>> in such a way, and I think we shouldn't.
>> It is the wrong angle IMO. First, we should make it work properly. Then we
>> make it as simple to use as possible within the constraints imposed by the
>> first point.
>>
>>> Note that for this to be efficient I'd like to see the optional to be
>>> movable, which isn't yet the case with boost::optional if I'm not
>>> mistaken.
>> You might have noticed that I mention std::tr2::optional... which has what
>> you are after. Andrzej is working on incorporating/backporting those
>> features to boost::optional. "Life happened" to him also :-) but I am
>> hoping, if we are extra nice, he might just might to get the interface we
>> are after into 1.56. Andrzej, ple-e-e-ease, pretty pretty please!?
> Honestly, when I first realized your convert() returns an optional I felt
> something like revulsion. This is for two reasons:
>    
>    a) While an optional exposes an operator*() and an operator->(), the
> resulting conversion syntax turns into something more complex than it has to
> be.

Yes, I understand. Unfortunately, my experience of deploying
lexical_cast and my own conversions tells me that in my case the simple
deployment interface does not cut it.

>    b) Using optional means you need an additional allocation (at least
> sometimes if you have an improved version using small object optimization
> internally). I tend to say that this alone can turn into a knockout criteria
> for a supposedly high performance, low level conversion library. However I
> have not done any measurements - so all this is pure conjecture.

Here I am not sure I immediately see what penalties std::tr2::optional
introduces. My impression and analysis of the code tells me otherwise.
Special requirements (like high performance) require special solutions.
Still, that fact did not kill or diminish in any way the existence of
generic libraries.

More so, during this review I was pleasantly surprised to see more
people with similar to mine needs -- they value std::sstream-based
converter the most, i.e. functional richness even if not blindly fast.
Is "coerce" is the same league?

> There have been done plenty of conversion library comparisons by different
> people. It might be a good idea to show performance results of your solution
> comparing to the existing libraries.

There is the "Performance" section in the docs. The current design
though is different so that "convert" itself has no performance metrics.
It's all defined by the plugged-in converter. So, "convert" can perform
as fast as "coerce" :-) when coerce-based converter is plugged-in.

>>
>> It is
>>
>> std::tr2::optional<TypeOut> convert(TypeIn const&, Converter const&);
>>
>> you are warming up to, right? If it is so, I am really glad to hear that.
>> I
>> am not married to it but all things considered it seems the most generic
>> and
>> flexible, functionally-complete, not too verbose... and sufficiently close
>> to lexical_cast :-) for people not to feel alienated.
>>
>> Given the immense variability of conversion-related deployments I see this
>> interface as the only one that everyone can deploy -- exceptions? no
>> problem; delayed exceptions? easy; no exceptions but failure-condition
>> returned instead? piece of cake; can't be bothered processing the
>> failure-condition and wanna proceed with the default? coming right up;
>> want
>> to process failure but still go with the default? anything to keep the
>> user
>> happy!
>>
>> So far, no one (to my knowledge) has come up with another interface which
>> simpler without sacrificing those mentioned important qualities (given we
>> are talking about a library-grade interface).
> See Jeroen's Coerce library for an example. It's as simple as lexical_cast
> or as complex as a full blown Spirit parser and everything in between.

I did. I do not believe it covers all deployment scenarios. I can be
wrong though. That said, I have to qualms stepping back and letting
Jeroen to bring "coerce" back to life. I'll take someone else's work any
time (with commercial-development-specifics caveats). Less work and
hassle for me. Honestly, it'll be a relief. I am already exhausted just
going through this 2 weeks review.

>
>>> The second is performance, with boost::coerce a lot of time was invested
>>> to
>>> prevent the interface from slowing down the conversion itself. I simply
>>> don't know how this interface will behave in that regard, when I have
>> more
>>> time I'll try some benchmarking.
>> Ah, that should not be a problem. I don't think. I actually expect the
>> convert() interface to be wiped out altogether when optimized.
> I used to think that as well. Sadly, more often than not compilers are not
> able to optimize all of it. But there is hope that they will get better over
> time. At least I would like to see numbers confirming your statement.
>

 From what I see it should be fairly straightforward to eliminate
"convert" api. But then again, one has to walk the walk to judge
responsibly... and I am not in optimization business.


_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: [review] Convert library

feverzsj
In reply to this post by Vladimir Batov-5
> 1) I do like
>
> T t = convert<T>(from, cnv); // Throws on failure
> T t = convert<T>(from, default_v, cnv); // Returns 'def' on failure
>
> and, if my memory serves me, that was pretty much review#1 interface
> (minus converters). What it misses though is handling one use-case --
> deterministic non-throwing handling of conversion failure. When #2
> returns default_v, we do not know if it is due to a failure or "from"
> just happened to convert to "default_v".

Using a default value means user knows the default value won't be a valid input or it is used as a fallback or just not cared. If conversion failure must be explicitly catched, other interface should be used.

> 2) I can see two potential problems with a converter provided by default:
>
> a) it might be tricky separating
>
> convert(TypeIn const&, Converter const& =some_default());
> convert(Converter const& =some_default());
>

how about write another overload:

convert(TypeIn const&, Converter const&);
convert(TypeIn const&);  // using default converter


> b) different people might feel quite strongly what converter to be the
> default... and I'll be on the receiving end of their fury. :-(

A default converter may be used in most of the daily job. It's important for boost lib to offer such convenience.
If one wants different converter, he can always make himself one.

regards.
Reply | Threaded
Open this post in threaded view
|

Re: [review] Convert library

Vladimir Batov-5
In reply to this post by Vladimir Batov-5

On 05/26/2014 11:32 AM, Vladimir Batov wrote:
>
> I did. I do not believe it covers all deployment scenarios. I can be
> wrong though. That said, I have to qualms stepping back and letting
> Jeroen to bring "coerce" back to life. I'll take someone else's work
> any time (with commercial-development-specifics caveats). Less work
> and hassle for me. Honestly, it'll be a relief. I am already exhausted
> just going through this 2 weeks review.
>

Meant to say "I have NO qualms stepping back". In fact, I am beginning
to think that I might have to retreat defeated (no drama). It is
because, if my memory serves me, the majority of reviews had
"conditional yes" votes which seems good on the surface of it. The
complication (as I see it) though is that those conditions were along
the lines "I vote yes if you change the interface to what I like"...
which renders "no" for the interface under consideration.

I am happy to keep going (if decided so) but, truth be told, only a
couple of reviews were in support of proposed interface and the rest
were essentially "no"s due to "un-meet-able" :-) conditions... unless we
can sum-up all the interface-related input into some concrete function
signature... which I am happy to implement.



_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: [review] Convert library

Vladimir Batov-5
In reply to this post by feverzsj

On 05/26/2014 12:01 PM, feverzsj wrote:

>> 1) I do like
>>
>> T t = convert<T>(from, cnv); // Throws on failure
>> T t = convert<T>(from, default_v, cnv); // Returns 'def' on failure
>>
>> and, if my memory serves me, that was pretty much review#1 interface
>> (minus converters). What it misses though is handling one use-case --
>> deterministic non-throwing handling of conversion failure. When #2
>> returns default_v, we do not know if it is due to a failure or "from"
>> just happened to convert to "default_v".
> Using a default value means user knows the default value won't be a valid
> input or it is used as a fallback or just not cared. If conversion failure
> must be explicitly catched, other interface should be used.

My problem is that I personally have great distaste for exceptions on
mandate ordinary program-flow level. With regard to conversion that'd
turn my 10-lines code block into 50-lines monster that I have
difficulties reading. So, I handle it with

optional<T> res = convert<T>(from, cnv);

if (!res)
{
     log("Invalid ignored. Proceeding with the default");
     res = some_default;
}

no exceptions, no default-related non-determinism. Happy. And you want
to take it way. :-(


>> 2) I can see two potential problems with a converter provided by default:
>>
>> a) it might be tricky separating
>>
>> convert(TypeIn const&, Converter const& =some_default());
>> convert(Converter const& =some_default());
>>
> how about write another overload:
>
> convert(TypeIn const&, Converter const&);
> convert(TypeIn const&);  // using default converter

There is

convert(Converter const&)

It'll probably clash with

convert(TypeIn const&)

>> b) different people might feel quite strongly what converter to be the
>> default... and I'll be on the receiving end of their fury. :-(
> A default converter may be used in most of the daily job. It's important for
> boost lib to offer such convenience.
> If one wants different converter, he can always make himself one.
>

Understood. What I am trying to say is that your preferred default might
well be not mine. That said, I'll go with the flow. What the majority
decides on.



_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: [review] Convert library

Vladimir Batov-5
In reply to this post by Jeroen Habraken

On 05/26/2014 12:24 AM, Jeroen Habraken wrote:

> On 25 May 2014 14:03, Vladimir Batov <[hidden email]> wrote:
>
> You're more than welcome, you may have snipped a little too much but
> I'll get to that below.
>> Jeroen Habraken wrote
>>> ...
>>>
>>> as other have argued boost::lexical_cast is used
>>> without the try-catch in a lot of places. Simply expecting it not to
>> throw
>>> and dropping everything on the ground when it does isn't the nicest way
>> to
>>> go about things but it does get things done.
>> Jeroen, seriously, we are serious people writing serious s/w. Should we be
>> discussing children playing with matches here? :-)
>
> Unfortunately I believe we should, whilst we might be writing serious
> software there are enough who aren't -- you'd be surprised how often people
> are using boost::lexical_cast without the try - catch at the place I
> currently work. Again, unfortunately.

Sorry, I just can't resist picturing that we are developing domestic gas
stoves... Whilst we might be developing/manufacturing essential
appliance for food-preparation purposes we should be considerate of
those who'll be sticking their heads in and blowing their houses up
using our appliances. Uhm, I do not think so.  :-)

That said, if you have an interface that keeps me (ordinary user) happy
and works for those thrill-seekers by blowing their heads off... and the
community is happy... I am happy... Let's adapt it...


>> ...
>> So far, no one (to my knowledge) has come up with another interface which
>> simpler without sacrificing those mentioned important qualities (given we
>> are talking about a library-grade interface).
>
> Close, I'd like an overload without the Converter const & as well,
> defaulting to a converter chosen via a customisation point. This is what I
> believe you've unintentionally snipped, and I'm curious as to your opinion
> on it as I believe it to be a crucial part of the interface.

Sure thing. Currently "convert" has two overloads. Can you write all
overloads that you are proposing? With defaults and stuff. I suspect
some might not survive simply because their signatures clash.

As for "defaulting to a converter chosen via a customization point", so
far I am OK with it... unless we hit some unexpected implementation
issue. I only remember Andrzej's "no globals" view. A global would have
to be thread-safe and all.



_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: [review] Convert library

Hartmut Kaiser
In reply to this post by Vladimir Batov-5
Volodya,

> Thank you for your input. It's much appreciated. From the critical tone
> of your post I'll take it you are voting "no". If you did not state it
> by accident, then I am happy to do that for you. If you did not state it
> intentionally, then I think you should as you should not be shying away
> from your community responsibilities. :-) Please find my replies below.

I intentionally have not cast a vote as I don't have put enough effort into
a proper review. My main goal was to support Jeroen in his review.

In any case, I wanted to mention a possible alternative to the design using
optional<>. Boost.Filesystem and Boost.Asio give a nice precedent on how to
construct a library interface supporting both, throwing and non-throwing
functions without introducing penalties for either case (based on
Boost.System). Using something similar for Convert seems to be a good idea.

Regards Hartmut
---------------
http://boost-spirit.com
http://stellar.cct.lsu.edu



_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
1234