[boost] Boost.Convert. Take 2.

57 views
Skip to first unread message

Vladimir Batov

unread,
Feb 9, 2014, 10:58:48 PM2/9/14
to bo...@lists.boost.org

Another day I came across a topic called "String to T conversions -
getting it right this time
<http://groups.google.com/a/isocpp.org/group/std-proposals/t/a9905ba8e4f33f3e>"
on the "ISO C++ Standard - Future Proposals
<https://groups.google.com/a/isocpp.org/forum/#%21forum/std-proposals>"
that got me interested/curious. Unfortunately, it seemed more of a
discussion rather than a proposed/deployable solution to the string
conversion issue. Then, I searched Boost archives to see if there was
any development in that string-to-type conversion domain since my
unsuccessful proposal about three years ago:

From: Edward Diener <eldiener <at> tropicsoft.com>
Subject: [Review] Boost.Convert, review manager analysis
<http://news.gmane.org/find-root.php?message_id=iq12fm%24k6c%241%40dough.gmane.org>
Newsgroups: gmane.comp.lib.boost.devel
<http://news.gmane.org/gmane.comp.lib.boost.devel>,
gmane.comp.lib.boost.user <http://news.gmane.org/gmane.comp.lib.boost.user>
Date: 2011-05-06 15:01:06 GMT (2 years, 39 weeks, 6 days, 23 hours and
47 minutes ago)

Unfortunately I was not able to find anything. Apologies if I missed
something major/serious (I was not exactly closely monitoring the list).
Is anyone aware of any work done in the area? If not, then I am thinking
if we could revisit the issue the original (and failed) Boost.Convert
tried to address. I am not sure about others but for me that quite
essential and seemingly simple task is still quite important. I was
watching the development of the lexical_cast but did not see it going
beyond its original frugal design. So, maybe we might have another look
at the code that Boost.Convert has become and that I've been
using/working on?.. Convert V2... From past experience calling it
Boost.Convert seems quite premature. :-) I only put Boost.Convert in the
title as a reminder for those who participated in the original review.

So, the code is at https://github.com/yet-another-user/boost.xtra. The
docs are old (from the original proposal) so, if anyone interested, I'd
suggest jumping right to the test/example code in libs/convert/test.
Apologies for the inconvenience but for now I'll try to see if I should
put any effort updating the docs for possible submission.

The main difference from the original proposal is that the complicated
blob was split in to two separate components -- Convert API facade and a
converter. Here it seems very similar to (same as?) boost::lexical_cast
which provides a uniform API facade which in turn deploys additional
functionality (op<<, op>>). Similarly, Convert V2 requires a converter
to be specified for conversion:

boost::cstringstream_based_converter ccnv; // char converter
boost::wstringstream_based_converter wcnv; // wchar_t converter

Then,

int a000 = convert<int>::from(not_int_str, -1, ccnv);
int a001 = convert<int>::from(std_str, -1, ccnv);
int a002 = convert<int>::from(c_str, -1, ccnv);
int a003 = convert<int>::from(wstd_str, -1, wcnv);
int a004 = convert<int>::from(wc_str, -1, wcnv);

Now all the configuration (formatting, locales, etc.) is separate from
the API facade and provided (or not) by the plugged in converter. The
only available converter tringstream_based_converter allows something like:

std::locale rus_locale (rus_locale_name);
std::locale eng_locale ("");

// Set locale, case, precision,
ccnv(std::setprecision(4))(std::scientific)(std::nouppercase);

string double_rus = convert<string>::from(double_v01,
ccnv(rus_locale)).value();
string double_eng = convert<string>::from(double_v01,
ccnv(eng_locale)).value();

Gosh, it's getting too long... cutting it short. The above is probably
sufficient for those who participated in the original review. Those who
did not might have a look at the (original and old) docs and the
test/example code.

V.








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

Edward Diener

unread,
Feb 10, 2014, 10:23:42 AM2/10/14
to bo...@lists.boost.org
I would strongly suggest updating the docs for the library. Also either
add in to the new docs the differences between the original version and
the new version, or just include both the old and new version docs so
that end-users could easily see what has changed.

I would be glad to be the review manager of the new library, if
necessary, if you wanted the new library added to the Boost review queue
once you felt everything was complete. OTOH I would also understand if
you wanted someone else to be the review manager.
Offhand this looks better. As I recall a general criticism was that you
were trying to do too much in the convert library rather than just
focusing in on 'convert' as a better, more flexible replacement than
lexical_cast. Sometimes les is more <g>.

>
> Gosh, it's getting too long... cutting it short. The above is probably
> sufficient for those who participated in the original review. Those who
> did not might have a look at the (original and old) docs and the
> test/example code.



Andrzej Krzemienski

unread,
Feb 10, 2014, 1:12:45 PM2/10/14
to bo...@lists.boost.org
Hi Vladimir,
I never participated in the original review, so pardon me if what I bring
up was already addressed or dismissed.

There are at least two reasons why I would prefer your library to
lexical_cast. First, it lets me customize the locale, which I was always
missing. Second, it doesn't force me to throw exceptions (and make the
program go slower, if I just want to test if my string is convertible to
int).

But there are two reasons, that discourage me from using the library: first
that it "pretends" it returns my T, whereas in fact it returns a wrapper
convertible to T. Users will get a lot of surprise, when they try to write:

auto my_str = convert<std::string>::from(1, ..);
my_str.size();

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

Second thing that bothers me is that for the result type to be able to
offer all these checks an otherwise unnecessary runtime overhead is
imposed. 'result' stores both optional<T> and a flag indicating how to deal
with the lack of value. since, in order to write into T you need to
default-construct it anyway (or move construct it), it looks it should be
enough to have 'result' contain a non-optional T and more sophisticated
flag that also stores the information about having a real T.

I would like to suggest two things:

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

2. Would it be possible to consider returning tr2::optional instead of
'result'? It is different than boost::optional in a couple of aspects.
After a lots of changes, the
final shape of it is here:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3793.html and the
reference implementation is here: https://github.com/akrzemi1/Optional

tr2::optional offers 4 ways of dealing with the missing (unset) value:
First: if (rslt) use (*rslt); // check it manually
Second: use (rslt.value_or(1)); // use default value
Third: use (rslt.value()); // throw if no value
Fourth: use (*rslt); // use without checks

the fourth result is not to be underestimated. Sometimes I am sure my int
will successfully convert to a string, and I do not want to pay the price
for an extra check.

Returning an optional would somehow separate responsibilities: conversion
function only converts, and optional is a well-understood general-purpose
wrapper for dealing with a potentially missing value.

I am pretty sure we could adapt boost::optional to tr2::optional's
interface.

Or are there reasons not to use optional?

Regards,
&rzej

Vladimir Batov

unread,
Feb 10, 2014, 4:22:56 PM2/10/14
to bo...@lists.boost.org
Andrzej, thank you for your input. It's much appreciated. Please find my
replies below.

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

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

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

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

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

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

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

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

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

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

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

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

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

Vladimir Batov

unread,
Feb 10, 2014, 4:45:58 PM2/10/14
to bo...@lists.boost.org

On 02/11/2014 02:23 AM, Edward Diener wrote:
> On 2/9/2014 10:58 PM, Vladimir Batov wrote:
>> ...
> I would strongly suggest updating the docs for the library. Also
> either add in to the new docs the differences between the original
> version and the new version, or just include both the old and new
> version docs so that end-users could easily see what has changed.

Indeed. Docs are must. I started re-writing over the weekend but then
thought I'd check with the community first -- maybe there was already a
solution that I missed or there was no interest to begin with.

>
> I would be glad to be the review manager of the new library, if
> necessary, if you wanted the new library added to the Boost review
> queue once you felt everything was complete. OTOH I would also
> understand if you wanted someone else to be the review manager.
Thank you for your offer of managing the proposal (if we come to that).
It's much and truly appreciated. I was very happy with the way you
managed the first proposal and your thorough analysis. I can't fathom
why I might want someone else.

> ...
>> std::locale rus_locale (rus_locale_name);
>> std::locale eng_locale ("");
>>
>> // Set locale, case, precision,
>> ccnv(std::setprecision(4))(std::scientific)(std::nouppercase);
>>
>> string double_rus = convert<string>::from(double_v01,
>> ccnv(rus_locale)).value();
>> string double_eng = convert<string>::from(double_v01,
>> ccnv(eng_locale)).value();
>
> Offhand this looks better. As I recall a general criticism was that
> you were trying to do too much in the convert library rather than just
> focusing in on 'convert' as a better, more flexible replacement than
> lexical_cast. Sometimes les is more <g>.

"Too much and too messy" was indeed my ultimate feeling back then. Now
the split seems to make quite a difference; looks dead simple and more
configurability.

Andrzej Krzemienski

unread,
Feb 11, 2014, 2:24:55 AM2/11/14
to bo...@lists.boost.org
2014-02-10 22:22 GMT+01:00 Vladimir Batov <Vladimi...@constrainttec.com>
:

> Andrzej, thank you for your input. It's much appreciated. Please find my
> replies below.
>
> On 02/11/2014 05:12 AM, Andrzej Krzemienski wrote:
>
>> ...
>>
>
> Also, I am likely to select an incorrect function overload, if I pass the
>> result to the function template.
>>
>
> Again, if there is enough conviction behind the implicit conversion
> causing havoc, it's no drama to take it out. Will it solve the problem you
> mention?


Yes, my opposition is towards the implicit conversion to T rather than to
returning a wrapper. In fact, I agree with you that a composed type "either
T or nothing" is a more natural and more desired interface.


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


I am sorry if I did not express my intent clearly. I do agree that the
feature "check if we have a value and if not: react" is a desired one. The
statement about "unnecessary runtime overhead" was referring to
implementation details, but I accept that it is to early to discuss the
implementation details.


>
>
> I would like to suggest two things:
>>
>> 1. Would it not be possible to have an overload that does not take the
>> stream object? It would mean we want to use some default char-based stream
>> with any locale. The newbies would not have to bother with what it is,
>> when
>> they only want to convert an int to a string?
>>
>
> In the code I used/required explicit converter. In actual code the
> converter is defaulted to the one I listed explicitly. Will it suffice?


I must admit I do not understand this statement. So, let me ask an
auxiliary question. Will I be able to use the library like this:

string s = convert<string>::from(-5).value();

Regards,
&rzej

Vladimir Batov

unread,
Feb 11, 2014, 5:52:30 AM2/11/14
to bo...@lists.boost.org
Andrzej Krzemienski <akrzemi1 <at> gmail.com> writes:

> 2014-02-10 22:22 GMT+01:00 Vladimir Batov <Vladimir.Batov <at>
constrainttec.com>
> >
> > Again, if there is enough conviction behind the implicit conversion
> > causing havoc, it's no drama to take it out. Will it solve the problem you
> > mention?
>
> Yes, my opposition is towards the implicit conversion to T rather than to
> returning a wrapper. In fact, I agree with you that a composed type
> "either T or nothing" is a more natural and more desired interface.

OK. To prevent implicit converter haunting me I simply removed that. So, now
it's always "either T or nothing" return type plain and simple.

> >> ...
> > Well, I have to disagree about "unnecessary checks". If one does not
> > need
> > checks, then boost::lexical_cast will suffice. As for the current
> > implementation, then I consider it a rough draft and an implementation
> > detail which I am not focusing on right now. Your suggestion might well
> > be
> > the ultimate way to go. I think, we need to get the fundamentals right
> > first.
>
> I am sorry if I did not express my intent clearly. I do agree that the
> feature "check if we have a value and if not: react" is a desired one. The
> statement about "unnecessary runtime overhead" was referring to
> implementation details, but I accept that it is to early to discuss the
> implementation details.

Indeed, I have no doubt that the current first-cut proof-of-the-concept
implementation is crap (pardon my French). :-) When I look at the stuff that
is in Boost and that I expect to be dead simple... gosh... don't I feel
stupid?! We are not there yet... I do not get if we get there... Currently,
I'd like to focus on user-visible part -- the public API. If we settle on
that, we'll be able to improve implementation as much as we like behind the
interface without anyone noticing. :-)

> > I would like to suggest two things:
> >>
> >> 1. Would it not be possible to have an overload that does not take the
> >> stream object? It would mean we want to use some default char-based stream
> >> with any locale. The newbies would not have to bother with what it is,
> >> when
> >> they only want to convert an int to a string?
> >>
> >
> > In the code I used/required explicit converter. In actual code the
> > converter is defaulted to the one I listed explicitly. Will it suffice?
>
> I must admit I do not understand this statement. So, let me ask an
> auxiliary question. Will I be able to use the library like this:
>
> string s = convert<string>::from(-5).value();

Apologies, typed my previous reply in a hurry and was far from clear. What I
meant to say was that in the code that I posted on git and showed in my
first email I specified the need for an explicit converter to highlight the
orthogonality of the new design. In the actual production code I have the
converter parameter defaulted to that mentioned stream-based converter. So,
there is no need to always specify the converter. That is

string s = convert<string>::from(-5).value();
string s = convert<string>::from(-5, "bummer").value();

will pick that mentioned stream-based converter. Now though I am not sure if
that should be the converter to default to. I've added lexical_cast-based
converter and now wondering if that should be the default converter instead.

V.

Vladimir Batov

unread,
Feb 13, 2014, 11:01:25 PM2/13/14
to bo...@lists.boost.org

On 02/11/2014 08:45 AM, Vladimir Batov wrote:
> On 02/11/2014 02:23 AM, Edward Diener wrote:
>> I would strongly suggest updating the docs for the library. Also
>> either add in to the new docs the differences between the original
>> version and the new version, or just include both the old and new
>> version docs so that end-users could easily see what has changed.
>
> Indeed. Docs are a must. I started re-writing over the weekend but
> then thought I'd check with the community first -- maybe there was
> already a solution that I missed or there was no interest to begin with.

Finished the docs. The whole thing is at
https://github.com/yet-another-user/boost.xtra.

Was surprised that for user-types it performed two times faster than
boost::lexical_cast with no optimizations whatsoever... must be the
design/deployment difference.

Wondering if that's the time to request a formal review?.. Never figured
out how the process goes.

Any input is most welcome... although the list seems pretty quiet for
some reason.

Best,
V.

Brian Ravnsgaard Riis

unread,
Feb 14, 2014, 3:06:28 AM2/14/14
to bo...@lists.boost.org
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

On 2014-02-14 05:01, Vladimir Batov wrote:
> Finished the docs. The whole thing is at
> https://github.com/yet-another-user/boost.xtra.
>
> Was surprised that for user-types it performed two times faster
> than boost::lexical_cast with no optimizations whatsoever... must
> be the design/deployment difference.
>
> Wondering if that's the time to request a formal review?.. Never
> figured out how the process goes.
>
> Any input is most welcome... although the list seems pretty quiet
> for some reason.

I guess it's a typo. In libs/convert/doc/05_getting_serious.qbk line
52 and 53 you use a type of convert<int>result. Maybe you're missing a
couple of colons on each line...? :-)

Reading on.

/Brian
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.12 (GNU/Linux)
Comment: Using GnuPG with Thunderbird - http://www.enigmail.net/

iQEcBAEBAgAGBQJS/c56AAoJEFES4N8QrEodv74H/R61WwZJjZ34xsU3Y4+/YoCf
jeG+LUebvUTS3ym08aOM66+VIRau8+YhBmOIPq7VxVXn188wGMgvjBXpfCNlflZj
0/e27WeTHn10mOdf2ly4ah55IHJVAFPS2WG6fC5UFBF0Ih2ddGmcQRrjWxEso8X/
O3LRVTLuOs1sCgljf/beNFUASQKNbwzERg6Dp2pKdZL7BYwJSvOCz+i9r/E+zIe8
Z6lFA6V2iomTdhU+wyTgirjqQ4YC87PMJMZU0rw4cf+N+Qyo3ZnBkebPvGbl99hI
/rv1Z/vMc5Xu0fmVu6knz93ATtRaw02MFk+t3FNP1BoMTLZteLjUJlrjgQ9T5+A=
=g5Be
-----END PGP SIGNATURE-----

Vladimir Batov

unread,
Feb 14, 2014, 4:43:52 AM2/14/14
to bo...@lists.boost.org
Brian Ravnsgaard Riis <brian <at> ravnsgaard.net> writes:
> On 2014-02-14 05:01, Vladimir Batov wrote:
> > ...
> > Any input is most welcome... although the list seems pretty quiet
> > for some reason.
>
> I guess it's a typo. In libs/convert/doc/05_getting_serious.qbk line
> 52 and 53 you use a type of convert<int>result. Maybe you're missing a
> couple of colons on each line...?

Brian, yes, it was a typo... fixed... thank you for reading... Andrzej
pointed out that the html docs are, well, messed up... damn, embarrassing...
will fix it tomorrow...

Paul A. Bristow

unread,
Feb 14, 2014, 4:52:48 AM2/14/14
to bo...@lists.boost.org
> -----Original Message-----
> From: Boost [mailto:boost-...@lists.boost.org] On Behalf Of Vladimir Batov
> Sent: Friday, February 14, 2014 9:44 AM
> To: bo...@lists.boost.org
> Subject: Re: [boost] Boost.Convert. Take 2.
>
> Brian Ravnsgaard Riis <brian <at> ravnsgaard.net> writes:
> > On 2014-02-14 05:01, Vladimir Batov wrote:
> > > ...
> > > Any input is most welcome... although the list seems pretty quiet
> > > for some reason.
> >
> > I guess it's a typo. In libs/convert/doc/05_getting_serious.qbk line
> > 52 and 53 you use a type of convert<int>result. Maybe you're missing a
> > couple of colons on each line...?
>
> Brian, yes, it was a typo... fixed... thank you for reading... Andrzej pointed out that the html
docs are,
> well, messed up... damn, embarrassing...
> will fix it tomorrow...

Moral - use Quickbook snippets in/from the example C++ code!

The what has compiled is always what you see :-)

Paul

---
Paul A. Bristow,
Prizet Farmhouse, Kendal LA8 8AB UK
+44 1539 561830 07714330204
pbri...@hetp.u-net.com

Edward Diener

unread,
Feb 14, 2014, 9:46:59 AM2/14/14
to bo...@lists.boost.org
On 2/13/2014 11:01 PM, Vladimir Batov wrote:
>
> On 02/11/2014 08:45 AM, Vladimir Batov wrote:
>> On 02/11/2014 02:23 AM, Edward Diener wrote:
>>> I would strongly suggest updating the docs for the library. Also
>>> either add in to the new docs the differences between the original
>>> version and the new version, or just include both the old and new
>>> version docs so that end-users could easily see what has changed.
>>
>> Indeed. Docs are a must. I started re-writing over the weekend but
>> then thought I'd check with the community first -- maybe there was
>> already a solution that I missed or there was no interest to begin with.
>
> Finished the docs. The whole thing is at
> https://github.com/yet-another-user/boost.xtra.

Is the library called "xtra" or "convert" ?

The library should be in modular-boost structure. You have it in the old
SVN structure.

Running b2 in the doc directory gives all sorts of errors because you
are still using the old SVN structure. So it is impossible to see the
documentation.

Vladimir Batov

unread,
Feb 14, 2014, 2:16:52 PM2/14/14
to bo...@lists.boost.org
On 02/15/2014 01:46 AM, Edward Diener wrote:
> On 2/13/2014 11:01 PM, Vladimir Batov wrote:
>> ...
>> Finished the docs. The whole thing is at
>> https://github.com/yet-another-user/boost.xtra.
>
> Is the library called "xtra" or "convert" ?
The library si still called the same "convert". Housed it in the
"boost.xtra" dir to distinguish it from the real boost dir.
>
> The library should be in modular-boost structure. You have it in the
> old SVN structure.
OK. I'll look into it.
>
> Running b2 in the doc directory gives all sorts of errors because you
> are still using the old SVN structure. So it is impossible to see the
> documentation.

Yes, did not check all (duh!) docs yesterday. Fixed now.

Edward Diener

unread,
Feb 15, 2014, 10:58:25 AM2/15/14
to bo...@lists.boost.org
On 2/14/2014 2:16 PM, Vladimir Batov wrote:
> On 02/15/2014 01:46 AM, Edward Diener wrote:
>> On 2/13/2014 11:01 PM, Vladimir Batov wrote:
>>> ...
>>> Finished the docs. The whole thing is at
>>> https://github.com/yet-another-user/boost.xtra.
>>
>> Is the library called "xtra" or "convert" ?
> The library si still called the same "convert". Housed it in the
> "boost.xtra" dir to distinguish it from the real boost dir.
>>
>> The library should be in modular-boost structure. You have it in the
>> old SVN structure.
> OK. I'll look into it.
>>
>> Running b2 in the doc directory gives all sorts of errors because you
>> are still using the old SVN structure. So it is impossible to see the
>> documentation.
>
> Yes, did not check all (duh!) docs yesterday. Fixed now.

These lines in the doc look wrong. The expression 'convert<int>result'
does not look possible in C++. I think this should be
'convert<int>::result'.

convert<int>result r1 = boost::convert<int>(str1, cnv); // Does not
throw on conversion failure
convert<int>result r2 = boost::convert<int>(str2, cnv); // Does not
throw on conversion failure

I think you need to be precise in what a user-defined type needs to
provide in order to be 'convertible from' and 'convertible to' within
the convert framework. If that is too complicated in a design as a
general form of using convert, then limit the library to conversion
either to and from a std::basic_string<T> and specify what the single
type being converted from or to a std::basic_string<T> needs to supply
in order to work with convert. I actually see nothing wrong with such a
limitation if it is necessary to accomplish more easily what you
envision. I did not understand this information from the docs. Perhaps
you have worked this out but you need to explain it quite methodically.

I would also suggest that callables ( std::function types ) be used
instead of hardcoded function names when supplying user-defined
conversion functionality. This provides a much more flexible ability to
provide conversion for a user-defined type since the full retinue of
functor creation within the standard C++ library and from within Boost
can be used by the end-user to provide appropriate conversion routines.
Once you hardcode some conversion member function within a class you are
throwing out all this functor creation ability. Of course if such a
design is too complicated based on your own design for convert then stay
with what you currently have, but at least consider it if you understand
the power of C++ callables to provide user-defined functionality.

Andrzej Krzemienski

unread,
Feb 15, 2014, 4:35:12 PM2/15/14
to bo...@lists.boost.org
2014-02-14 20:16 GMT+01:00 Vladimir Batov <Vladimi...@constrainttec.com>
:

> On 02/15/2014 01:46 AM, Edward Diener wrote:
>
>> On 2/13/2014 11:01 PM, Vladimir Batov wrote:
>>
>>> ...
>>>
>>> Finished the docs. The whole thing is at
>>> https://github.com/yet-another-user/boost.xtra.
>>>
>>
>>
I have a suggestion regarding the documentation. It is abut the first page.
I am expressing my preference, but I am pretty sure I am not the only
person here with this expectation. I am a very impatient man and I expect
of the first page a number of answers quickly, or otherwise I will be not
interested. When I see "Conversion" in the name of the library, I will need
the following questions answered quickly (I guess I am not more arrogant
than an average stressed programmer):

1. Is it only string to T and T to string conversions, or arbitrary T to U
conversion?
2. Will it consider locale?
3. Will it return optional<T> (or some such) so that I can decide myself
how to deal with conversion failure?
4. How will I use it. Give me a minimum example.

In other words, I need to see from the first page what this library will
and will not give me.
I would also suggest not to start with the comparison with lexical_cast.
Your users may not even know lexical_cast. Also, there is something
discouraging when I read how your library differs from some other library
rather than learning what your library is.

Two other suggestions for the initial page. 1. Mention that it works with
non-DefaultConstructibel types. It is unusual (in the positive sense) for a
conversion library. 2. Since you mentioned that convert can be used without
specifying the second (streamer) argument, show this in the initial
example: let it be really simple.

And one other thought (it is not really a suggestion for Boost.Convert, but
a general observation regarding string conversions). Your library is mainly
about converting string to T. T to string will be less common. And I find
it hard to imagine that someone would use it as T to U conversions. When
converting from T to string, you do not really need to return optional<T>,
because it is not possible that this conversion can fail. Any T always has
a string representation, doesn't it? I wonder (but I do not have a good
answer) if conversion in this direction should have the same interface.

Regards,
&rzej

Vladimir Batov

unread,
Feb 16, 2014, 3:56:47 PM2/16/14
to bo...@lists.boost.org
Addressed all three comments... I think. Checked in.

Vladimir Batov

unread,
Feb 16, 2014, 5:13:24 PM2/16/14
to bo...@lists.boost.org
On 02/16/2014 08:35 AM, Andrzej Krzemienski wrote:
>
> I have a suggestion regarding the documentation. It is abut the first page.
> I am expressing my preference, but I am pretty sure I am not the only
> person here with this expectation. I am a very impatient man and I expect
> of the first page a number of answers quickly, or otherwise I will be not
> interested. When I see "Conversion" in the name of the library, I will need
> the following questions answered quickly (I guess I am not more arrogant
> than an average stressed programmer):
>
> 1. Is it only string to T and T to string conversions, or arbitrary T to U
> conversion?
> 2. Will it consider locale?
> 3. Will it return optional<T> (or some such) so that I can decide myself
> how to deal with conversion failure?
> 4. How will I use it. Give me a minimum example.
>
> In other words, I need to see from the first page what this library will
> and will not give me.

I've extended the Introduction section to address your #1&2 comments. As
for #3&4 I suspect I am lacking the ability to express myself succinctly
enough to squeeze those into the Introduction... without turning it into
"War and Peace"... which would work against me with the impatient kind. ;-)

> I would also suggest not to start with the comparison with lexical_cast.
> Your users may not even know lexical_cast. Also, there is something
> discouraging when I read how your library differs from some other library
> rather than learning what your library is.
I see your point. Unfortunately, I feel somewhat paranoid as
lexical_cast comparisons dogged my V1 proposal all the way through. Back
then people's reactions were quite natural -- we already have a
conversion API which seems to be a potentially suitable foundation; why
not use it? They were at the beginning of the "road" I already went but
I had to answer the same (not always kind and polite) questions about
lexical_cast over and over again. And youngsters are usually "quick
shooters" -- quick and easy with grandiose statements and opinions, slow
and reluctant with their own research beforehand. So, now I put in
(indeed) a lot of stuff related to lexical_cast -- design, performance,
functionality comparisons... just in case... in fact, if we get to the
review/discussion stage, I am sure people will be asking -- why not
lexical_cast, why does not it behave like lexical_cast?
> Two other suggestions for the initial page. 1. Mention that it works with
> non-DefaultConstructibel types. It is unusual (in the positive sense) for a
> conversion library. 2. Since you mentioned that convert can be used without
> specifying the second (streamer) argument, show this in the initial
> example: let it be really simple.
I hope I've addressed your #1 in the Introduction section. Please see if
you find it satisfactory. As for #2 I have to admit I reconsidered my
original position. Indeed, I had the converter parameter in
convert<T>::from(value_in, converter) defaulted to sstream_converter in
my own code. Because I was lazy and did not care for its performance (I
have other chunks "eating" so much more).

Now, as I put it up for everyone to see, it's a different story. My
original lazy approach had two drawbacks -- convert/api.hpp had
sstream_converter.hpp included (an unnecessary coupling) and
convert<T>::from was creating a converter every time it was called. As I
described in the Performance section it has quite a detrimental effect
on performance. As I said, *I* am not concerned (for my current
applications)... but I do not want to give that loophole to people to
explore, discover that performance sucks in their settings and come back
swinging... just the same why I took the implicit converter to T from
convert<T>::result as soon as you mentioned it -- I *personally* find it
very convenient (no need for "value") but defending it is a loosing
battle IMO.

> And one other thought (it is not really a suggestion for Boost.Convert, but
> a general observation regarding string conversions). Your library is mainly
> about converting string to T. T to string will be less common.
Hmm, here with all due respect I really have to disagree on various
levels. :-)

In my neck of the woods, string-to-T and T-to-string are represented
quite equally. Say, we consider the management of configuration files.
Reading values (string-to-T) from cfg files is (to me, anyway) on the
same scale as writing updated values back (T-to-string). Same goes for a
component in a processing pipe or a node in a network --
reading/converting a lot of XML, converting/writing a lot of XML.
> And I find
> it hard to imagine that someone would use it as T to U conversions.
Again, I am far from sure about that. I do currently have to have
OS-native MBCS (MSWin and Solaris) to UCS-2 and UCS-4 string
conversions. They are a separate lib. Can't immediately see anything
wrong in incorporating it into the 'convert' framework. Then I remember
Vicente having a 'conversion' proposal to address that T-to-U conversion
in a generic way. So, he must have had a need for it also. I am not
saying you are wrong. I just do not know. Consider the templates
example. Stroustrup purposefully designed them in a generic way. Their
deployment exploded often in surprising ways. I am certainly no
Stroustrup but you get the idea.
> When
> converting from T to string, you do not really need to return optional<T>,
> because it is not possible that this conversion can fail. Any T always has
> a string representation, doesn't it? I wonder (but I do not have a good
> answer) if conversion in this direction should have the same interface.
Uhm, I would not be that quick saying that T-to-string conversion can
never fail. It depends on complexity of the conversion and the used
character set, etc. What if T is a complex class which in its conversion
depends on other classes to behave and they don't? What if the
conversion depends on, say, formatting specification and it is not met
or is invalid?

On top of it, uniformity (and, therefore, predictability) of API and
behavior is quite important IMO. Special (and questionable I might say)
handling of one special case IMO is not worth it (again IMO).

V.

Rob Stewart

unread,
Feb 16, 2014, 11:10:33 PM2/16/14
to bo...@lists.boost.org
--
Sent from my Android device with K-9 Mail. Please excuse my brevity.

Andrzej Krzemienski

unread,
Feb 17, 2014, 4:10:12 AM2/17/14
to bo...@lists.boost.org
2014-02-16 23:13 GMT+01:00 Vladimir Batov <Vladimi...@constrainttec.com>
:

> On 02/16/2014 08:35 AM, Andrzej Krzemienski wrote:
>
>>
>> I have a suggestion regarding the documentation. It is abut the first
>> page.
>> I am expressing my preference, but I am pretty sure I am not the only
>> person here with this expectation. I am a very impatient man and I expect
>> of the first page a number of answers quickly, or otherwise I will be not
>> interested. When I see "Conversion" in the name of the library, I will
>> need
>> the following questions answered quickly (I guess I am not more arrogant
>> than an average stressed programmer):
>>
>> 1. Is it only string to T and T to string conversions, or arbitrary T to U
>> conversion?
>> 2. Will it consider locale?
>> 3. Will it return optional<T> (or some such) so that I can decide myself
>> how to deal with conversion failure?
>> 4. How will I use it. Give me a minimum example.
>>
>> In other words, I need to see from the first page what this library will
>> and will not give me.
>>
>
> I've extended the Introduction section to address your #1&2 comments. As
> for #3&4 I suspect I am lacking the ability to express myself succinctly
> enough to squeeze those into the Introduction... without turning it into
> "War and Peace"... which would work against me with the impatient kind. ;-)


Now, I can definitely find all my answers in the Introduction. What I would
like to suggest as an option (but please do not treat this as a requirement
or an expression of dissatisfaction -- I can keep giving suggestions
forever, sometimes in a cycle) is to put a very very short code snippet
that will show me how I will be using the library.


>
> I would also suggest not to start with the comparison with lexical_cast.
>> Your users may not even know lexical_cast. Also, there is something
>> discouraging when I read how your library differs from some other library
>> rather than learning what your library is.
>>
> I see your point. Unfortunately, I feel somewhat paranoid as lexical_cast
> comparisons dogged my V1 proposal all the way through. Back then people's
> reactions were quite natural -- we already have a conversion API which
> seems to be a potentially suitable foundation; why not use it? They were at
> the beginning of the "road" I already went but I had to answer the same
> (not always kind and polite) questions about lexical_cast over and over
> again. And youngsters are usually "quick shooters" -- quick and easy with
> grandiose statements and opinions, slow and reluctant with their own
> research beforehand. So, now I put in (indeed) a lot of stuff related to
> lexical_cast -- design, performance, functionality comparisons... just in
> case... in fact, if we get to the review/discussion stage, I am sure people
> will be asking -- why not lexical_cast, why does not it behave like
> lexical_cast?


Comparison with lexical_cast, and (even more importantly) answering the
questions "why would I use Boost.Convert library instead of lexical_cast"
and "why did you not extend lexical_cast instead" and "why would I need
lexical_cast anymore" are very important for your library's documentation
-- I agree. It is just that I am not sure these explanations belong to the
Introduction page. But I leave it open. I am satisfied with the improved
introduction as it is (except for the one tiny suggestion below).
Ok, I get it: you do not want to suggest an inefficient use.


>
>
> And one other thought (it is not really a suggestion for Boost.Convert,
>> but
>> a general observation regarding string conversions). Your library is
>> mainly
>> about converting string to T. T to string will be less common.
>>
> Hmm, here with all due respect I really have to disagree on various
> levels. :-)
>
> In my neck of the woods, string-to-T and T-to-string are represented quite
> equally. Say, we consider the management of configuration files. Reading
> values (string-to-T) from cfg files is (to me, anyway) on the same scale as
> writing updated values back (T-to-string). Same goes for a component in a
> processing pipe or a node in a network -- reading/converting a lot of XML,
> converting/writing a lot of XML.
>
> And I find
>> it hard to imagine that someone would use it as T to U conversions.
>>
> Again, I am far from sure about that. I do currently have to have
> OS-native MBCS (MSWin and Solaris) to UCS-2 and UCS-4 string conversions.
> They are a separate lib. Can't immediately see anything wrong in
> incorporating it into the 'convert' framework. Then I remember Vicente
> having a 'conversion' proposal to address that T-to-U conversion in a
> generic way. So, he must have had a need for it also. I am not saying you
> are wrong. I just do not know. Consider the templates example. Stroustrup
> purposefully designed them in a generic way. Their deployment exploded
> often in surprising ways. I am certainly no Stroustrup but you get the idea.


So, you say, it could be used to convert between two different string
encodings? I didn't think of it but it looks really useful. So useful that
it deserves a bullet in the Introduction. Can it be used to convert from
string to wstring?

For some background, Vicente's library IIRC tried to emulate to some extent
the non-member conversion operator. It was supposed to convert between two
types that were meant to represent the same abstraction but written by two
different programmers/libraries:

time_t t1;
ptime t2;
MyLib::Time t3;

they all represent the same thing and the library should offer a universal
way to convert between them.


> When
>> converting from T to string, you do not really need to return optional<T>,
>> because it is not possible that this conversion can fail. Any T always has
>> a string representation, doesn't it? I wonder (but I do not have a good
>> answer) if conversion in this direction should have the same interface.
>>
> Uhm, I would not be that quick saying that T-to-string conversion can
> never fail. It depends on complexity of the conversion and the used
> character set, etc. What if T is a complex class which in its conversion
> depends on other classes to behave and they don't? What if the conversion
> depends on, say, formatting specification and it is not met or is invalid?
>

This is an interesting point. If a conversion fails because it required to
acquire some resources and it failed to get them, or because the object was
in a disallowed state (some assertion-like thing failed), do you want to
return an empty result, or just throw?


>
> On top of it, uniformity (and, therefore, predictability) of API and
> behavior is quite important IMO. Special (and questionable I might say)
> handling of one special case IMO is not worth it (again IMO).


Agreed.

Regards,
&rzej

Vladimir Batov

unread,
Feb 17, 2014, 4:25:39 PM2/17/14
to bo...@lists.boost.org
> On 02/17/2014 08:10 PM, Andrzej Krzemienski wrote:
> Now, I can definitely find all my answers in the Introduction.
> What I would like to suggest ... is to put a very very short code
> snippet that will show me how I will be using the library.

Done.

> Comparison with lexical_cast, and (even more importantly)
> answering the questions "why would I use Boost.Convert library
> instead of lexical_cast" and "why did you not extend lexical_cast
> instead" and "why would I need lexical_cast anymore" are very
> important for your library's documentation -- I agree. It is just
> that I am not sure these explanations belong to the
> Introduction page. But I leave it open. I am satisfied with the
> improved introduction as it is (except for the one tiny suggestion
> below).

Understood. Given that in the Intro lexical_cast is only mentioned once
in passing it should not pose a problem, right?

>> Again, I am far from sure about that. I do currently have to have
>> OS-native MBCS (MSWin and Solaris) to UCS-2 and UCS-4 string
>> conversions. They are a separate lib. Can't immediately see anything
>> wrong in incorporating it into the 'convert' framework. Then I
>> remember Vicente having a 'conversion' proposal to address that
>> T-to-U conversion in a generic way. So, he must have had a need for
>> it also. I am not saying you are wrong. I just do not know. Consider
>> the templates example. Stroustrup purposefully designed them in a
>> generic way. Their deployment exploded often in surprising ways. I am
>> certainly no Stroustrup but you get the idea.
> So, you say, it could be used to convert between two different string
> encodings? I didn't think of it but it looks really useful. So useful
> that it deserves a bullet in the Introduction. Can it be used to
> convert from string to wstring?

The thing is that the 'convert' API facade (the convert<T>::from) does
embarrassingly very little. Still, it ensures the uniformity of
interface and lays out the rules for the converters. So, if one has a
"callable", say, an old-fashioned function

bool my_converter(std::string const& value_in, std::wstring& result_out);

then it can be plugged into the 'convert' framework and used as

std::wstring res = convert<std::wstring>::from(std_string,
my_converter).value();

Still, I am far from sure that this particular application of 'convert'
will take off. I just do not know. The reason is that TypeIn and TypeOut
are known in advance, i.e. I know that I want std::string converted to
std::wstring. So, there is seemingly very little gain from calling

std::wstring res = convert<std::wstring>::from(std_string,
my_converter).value();

instead of direct

std::string value_in = ...;
std::wstring result_out;
bool success = my_converter(value_in, result_out);

When I, myself, look at convert<T>::from in isolation, I think why on
Earth I need convert<T>::from to begin with? But convert<T>::from
appears to be quite important (IMO) to build modular extendible system
as convert<T>::from API allows me to write code without knowing all the
types/conversions that will be used by that system. More so, from
maintenance point of view I find it much easier to see (and recognize) a
familiar API instead of guessing what " bool success =
my_converter(value_in, result_out)" might *actually* do.

So, the short answer to your "Can it be used to convert from string to
wstring?" question is "yes". Will it be useful? I am not sure. I've not
used "convert" it that fashion so I have no strong feelings one way or
another. So, I added a section about potentially doing that but tucked
it to the end. :-) I just to not want to get in to trouble bragging
about it too much. :-)

>> Uhm, I would not be that quick saying that T-to-string conversion can
>> never fail. It depends on complexity of the conversion and the used
>> character set, etc. What if T is a complex class which in its
>> conversion depends on other classes to behave and they don't? What if
>> the conversion depends on, say, formatting specification and it is
>> not met or is invalid?
> This is an interesting point. If a conversion fails because it
> required to acquire some resources and it failed to get them, or
> because the object was in a disallowed state (some assertion-like
> thing failed), do you want to return an empty result, or just throw?

I am not sure I can answer that question definitively. I suspect it
depends on how a particular failure is interpreted/seen. If that's
something disastrous/exceptional, then throw; otherwise return
failure... not exactly a precise answer, is it? :-)

When I said "a complex class which in its conversion depends on other
classes to behave" I had more "modest" conversion failures in mind. For
example, I write scheduling s/w for airlines and there there is the
concept of a leg or a sector -- a chunk of uninterrupted flight from A
to B. Quite often I want a leg/sector's string representation printed
out. So,

std::string info = convert<string>::from(leg, cnv).value_or("bad");

The problem is that there are many parameters in that leg/sector
structure. So, the call above can easily fail. Say, I forgot to set the
destination port! So, in this example conversion to string can fail. If
I had the "conversion to string can never fail" rule I could return "",
"invalid", etc. but I think the code above is far better -- I
immediately see what string represents conversion failure.

V.

Andrzej Krzemienski

unread,
Feb 17, 2014, 5:28:39 PM2/17/14
to bo...@lists.boost.org
2014-02-17 22:25 GMT+01:00 Vladimir Batov <Vladimi...@constrainttec.com>
:

> > On 02/17/2014 08:10 PM, Andrzej Krzemienski wrote:
> > Now, I can definitely find all my answers in the Introduction.
> > What I would like to suggest ... is to put a very very short code
>
> > snippet that will show me how I will be using the library.
>
> Done.


Wow, I failed to appreciate what this library can do. Now I can see you
described this "lazy evaluation" trick later in the docs, but I made my
judgement based solely on Introduction. I really am lazy. I will read
thoroughly all the documentation before I make any further comments.

Regards,
&rzej

Vladimir Batov

unread,
Feb 17, 2014, 5:48:45 PM2/17/14
to bo...@lists.boost.org
Andrzej, it's certainly flattering to hear that but truth be told
'convert' does embarrassingly *very* little by itself -- when
tr1::optional (or extended boost::optional) are be available, 'convert'
will simply use/deploy the functionality provided by "optional". So, in
the end, 'convert' is trying to become famous on the backs of smarter
people. ;-)

On 02/18/2014 09:28 AM, Andrzej Krzemienski wrote:
> Wow, I failed to appreciate what this library can do. Now I can see
> you described this "lazy evaluation" trick later in the docs, but I
> made my judgement based solely on Introduction. I really am lazy. I
> will read thoroughly all the documentation before I make any further
> comments.

Andrzej Krzemienski

unread,
Feb 20, 2014, 10:47:59 AM2/20/14
to bo...@lists.boost.org
2014-02-14 5:01 GMT+01:00 Vladimir Batov <Vladimi...@constrainttec.com>:

>
> On 02/11/2014 08:45 AM, Vladimir Batov wrote:
>
>> On 02/11/2014 02:23 AM, Edward Diener wrote:
>>
>>> I would strongly suggest updating the docs for the library. Also either
>>> add in to the new docs the differences between the original version and the
>>> new version, or just include both the old and new version docs so that
>>> end-users could easily see what has changed.
>>>
>>
>> Indeed. Docs are a must. I started re-writing over the weekend but then
>> thought I'd check with the community first -- maybe there was already a
>> solution that I missed or there was no interest to begin with.
>>
>
> Finished the docs. The whole thing is at
> https://github.com/yet-another-user/boost.xtra.
>

Regarding "Type Requirements" section in documentation:
TypeOut needs to be

*Copy Constructible.*
Is it not enough to require that TypeOut be *MoveConstructible*?

Regards,
&rzej

Edward Diener

unread,
Feb 20, 2014, 4:31:10 PM2/20/14
to bo...@lists.boost.org
On 2/16/2014 3:56 PM, Vladimir Batov wrote:
> Addressed all three comments... I think. Checked in.

This looks good. I think you have successfully focused on what is needed
for a more flexible lexical_cast-like version this time.

Vladimir Batov

unread,
Feb 20, 2014, 5:48:18 PM2/20/14
to bo...@lists.boost.org

On 02/21/2014 02:47 AM, Andrzej Krzemienski wrote:
>
> Regarding "Type Requirements" section in documentation:
> TypeOut needs to be
>
> *Copy Constructible.*
> Is it not enough to require that TypeOut be *MoveConstructible*?
>

I readily admit I am shaky about new C++11 stuff... even though it's
2014 outside... I've not had (cannot afford) any exposure to C++11 as I
am still stuck with C++03... well, at least for 2 more months... which
in the airline industry is more likely 6-12 months. :-(

Still I suspect some interfaces require to be Copy Constrictible. For
example,

template<typename TypeOut>
struct boost::convert<TypeOut>::result
{
...
template<typename FallbackType>
out_type value_or(FallbackType const& fallback) const
{
return good_ ? value_ : fallback;
}
};

Above "out_type" is returned by-value as "fallback" might be of
FallbackType -- potentially other than (by convertible to) TypeOut type.
For example:

std::string res = convert<std::string>::from(25, cnv).value_or("C string");

However, I do not immediately see if we can have "value_" destroyed by
"move". So, Copy Constructible... I think.

I'd expect that to be the same in tr1::optional... unless you figured
out how to work around that issue... or I simply do not understand how
clever it can be. I'll happily accept your lead on this issue.

V.

Vladimir Batov

unread,
Feb 20, 2014, 6:27:42 PM2/20/14
to bo...@lists.boost.org
On 02/21/2014 08:31 AM, Edward Diener wrote:
> On 2/16/2014 3:56 PM, Vladimir Batov wrote:
>> Addressed all three comments... I think. Checked in.
>
> This looks good.

Thanks, Edward, it's much appreciated.

> I think you have successfully focused on what is needed for a more
> flexible lexical_cast-like version this time.
>

I am especially surprised by the fact that its performance seems quite
adequate (without any optimization):

user-defined type: lcast/convert=0.52/0.27 seconds.
int type: scanf/lcast/convert=0.12/0.16/0.21 seconds.

It beats lexical_cast for user-defined types and not that far behind for
built-in types (I tested with "int"). I have a few doubts though
regarding the performance tests as lexical_cast documentation lists
quite different results:

scanf: 24
string-to-int 7
std::stringstream without construction (my convert): 20

and puts lexical_cast so far in front for gcc 4.6.3 that I used for
tests. On the other hand the test could not be any more straightforward:

double p4 = clock();

for (int k = 0; k < local::num_cycles; ++k)
sscanf("12345", "%d", &v);

double p5 = clock();

for (int k = 0; k < local::num_cycles; ++k)
boost::lexical_cast<int>("12345");

double p6 = clock();

for (int k = 0; k < local::num_cycles; ++k)
boost::convert<int>::from("12345", ccnv).value();

double p7 = clock();

printf("scanf/lcast/convert=%.2f/%.2f/%.2f seconds.\n",
(p5 - p4) / CLOCKS_PER_SEC,
(p6 - p5) / CLOCKS_PER_SEC,
(p7 - p6) / CLOCKS_PER_SEC);

I have to say that when I read my own :-) documentation I do feel that
"convert" provides quite useful functionality and quite a step forward
compared to frugal lexical_cast (well, for my use anyway). What concerns
me though is that when I look at the code, I think -- is it all?! There
is so little of it that I can't help thinking -- I must be missing
something monumental.

V.

Rob Stewart

unread,
Feb 20, 2014, 9:06:05 PM2/20/14
to bo...@lists.boost.org
On February 20, 2014 6:27:42 PM EST, Vladimir Batov <Vladimi...@constrainttec.com> wrote:
>On 02/21/2014 08:31 AM, Edward Diener wrote:
>> On 2/16/2014 3:56 PM, Vladimir Batov wrote:
>
>> I think you have successfully focused on what is needed for a more
>> flexible lexical_cast-like version this time.
>>
>
>I am especially surprised by the fact that its performance seems quite
>adequate (without any optimization):
[snip]
>tests. On the other hand the test could not be any more
>straightforward:
>
> double p4 = clock();
>
> for (int k = 0; k < local::num_cycles; ++k)
> sscanf("12345", "%d", &v);
>
> double p5 = clock();
>
> for (int k = 0; k < local::num_cycles; ++k)
> boost::lexical_cast<int>("12345");
>
> double p6 = clock();
>
> for (int k = 0; k < local::num_cycles; ++k)
> boost::convert<int>::from("12345", ccnv).value();
>
> double p7 = clock();
>
> printf("scanf/lcast/convert=%.2f/%.2f/%.2f seconds.\n",
> (p5 - p4) / CLOCKS_PER_SEC,
> (p6 - p5) / CLOCKS_PER_SEC,
> (p7 - p6) / CLOCKS_PER_SEC);

I assume you're timing an optimized build, on which case you are being misled by the optimizer because you're not using the results of the conversions.

___
Rob

(Sent from my portable computation engine)

Rob Stewart

unread,
Feb 20, 2014, 9:11:02 PM2/20/14
to bo...@lists.boost.org
On February 20, 2014 5:48:18 PM EST, Vladimir Batov <Vladimi...@constrainttec.com> wrote:
>
>On 02/21/2014 02:47 AM, Andrzej Krzemienski wrote:
>>
>> Regarding "Type Requirements" section in documentation:
>> TypeOut needs to be
>>
>> *Copy Constructible.*
>> Is it not enough to require that TypeOut be *MoveConstructible*?
>>
>
>I readily admit I am shaky about new C++11 stuff... even though it's
>2014 outside... I've not had (cannot afford) any exposure to C++11 as I
>
>am still stuck with C++03... well, at least for 2 more months... which
>in the airline industry is more likely 6-12 months. :-(
>
>Still I suspect some interfaces require to be Copy Constrictible. For
>example,
>
>template<typename TypeOut>
>struct boost::convert<TypeOut>::result
>{
> ...
> template<typename FallbackType>
> out_type value_or(FallbackType const& fallback) const
> {
> return good_ ? value_ : fallback;
> }
>};

This would address your concern:

if (good_)
{
return std::move(value_);
}
else
{
return fallback;
}

However, for non-movable types, the fallback is to copy anyway, so both should be moved.

___
Rob

(Sent from my portable computation engine)

Vladimir Batov

unread,
Feb 21, 2014, 3:12:08 AM2/21/14
to bo...@lists.boost.org
Rob Stewart <robertstewart <at> comcast.net> writes:
> On February 20, 2014 6:27:42 PM EST, Vladimir Batov <Vladimir.Batov <at>
Rob,

Nice hearing from you. I had that suspicion also and I do not think
optimization was at play. The reason is that I timed twice with "-g" and
without and definitely did not have optimization flags (never use them). And
it's hard not to notice when the code is optimized -- stepping through is,
well, difficult. :-) Secondly, my actual code is different (for the post I
cut it to bare minimum). All loops are actually like

for (int k = 0; k < local::num_cycles; ++k)
{
int k = boost::convert<int>::from("12345", ccnv).value();
BOOST_ASSERT(k == 12345);
}

Do you think there still might be something I missed?

Vladimir Batov

unread,
Feb 21, 2014, 3:25:23 AM2/21/14
to bo...@lists.boost.org
Rob Stewart <robertstewart <at> comcast.net> writes:
> On February 20, 2014 5:48:18 PM EST, Vladimir Batov <Vladimir.Batov <at>
constrainttec.com> wrote:
> >On 02/21/2014 02:47 AM, Andrzej Krzemienski wrote:
> >>
> >> Regarding "Type Requirements" section in documentation:
> >> TypeOut needs to be
> >>
> >> *Copy Constructible.*
> >> Is it not enough to require that TypeOut be *MoveConstructible*?
> > ...
> >Still I suspect some interfaces require to be Copy Constrictible. For
> >example,
> >
> >template<typename TypeOut>
> >struct boost::convert<TypeOut>::result
> >{
> > ...
> > template<typename FallbackType>
> > out_type value_or(FallbackType const& fallback) const
> > {
> > return good_ ? value_ : fallback;
> > }
> >};
>
> This would address your concern:
>
> if (good_)
> {
> return std::move(value_);
> }
> else
> {
> return fallback;
> }
>
> However, for non-movable types, the fallback is to copy anyway,
> so both should be moved.

The problem is, I think, with

> return std::move(value_);

because it destroys "value_" which in general terms I do not think we can
do. For example, (I'll use tr1::optional instead of convert<T>::result):

tr1::optional<std::string> res = convert<std::string>::from(12345, cnv);
std::string str = res.value_or("bummer");

... if value_or() moves/destroys the string inside "res",
... then "res" is not usable from here downwards
... it's a problem, isn't it?

Andrzej Krzemienski

unread,
Feb 21, 2014, 3:56:44 AM2/21/14
to bo...@lists.boost.org
If you added MoveConstructible requirement (I am not saying you should, I
am just exploring the possibility), you would be probably saying "It also
works with non-CopyConstructible but MoveConstructible types, but in that
case there is a limited number of ways Boost.Convert's interface can be
used." The above example would not work, but the following one should:

convert<std::string>::from(12345, cnv).value_or("bummer");

That is, you do not care that a temporary's internal value is moved from.
But this may not be implementable in C++03. I guess we do not know how to
emulate rvalue references for *this. However, the following should be easy
to implement:

convert<std::string>::from(12345, cnv).value();

For some background, move semantics obtained a nice language support in
C++11, but it is also available in C++03 for some extent. This is why we
have Boost.Move. I will try to add move semantics to Boost.Optional, so
that you can use it.

Regards,
&rzej

Vladimir Batov

unread,
Feb 21, 2014, 4:31:13 AM2/21/14
to bo...@lists.boost.org
> Andrzej Krzemienski <akrzemi1 <at> gmail.com> writes:
> ...
> If you added MoveConstructible requirement (I am not saying you should, I
> am just exploring the possibility), you would be probably saying "It also
> works with non-CopyConstructible but MoveConstructible types, but in that
> case there is a limited number of ways Boost.Convert's interface can be
> used." The above example would not work, but the following one should:
>
> convert<std::string>::from(12345, cnv).value_or("bummer");
>
> That is, you do not care that a temporary's internal value is moved from.
> But this may not be implementable in C++03. I guess we do not know how to
> emulate rvalue references for *this. However, the following should be easy
> to implement:
>
> convert<std::string>::from(12345, cnv).value();
>
> For some background, move semantics obtained a nice language support in
> C++11, but it is also available in C++03 for some extent. This is why we
> have Boost.Move. I will try to add move semantics to Boost.Optional, so
> that you can use it.

RE: convert<>::from().value();

This one is fine as it returns a const reference (unless it throws. So, no
additional requirements whatsoever... right?

RE: MoveConstructible

I do think that your clarifications should be in the docs and Rob's
std::move() deployment in the code. Still, I feel that this sort of detailed
explanations/clarifications/implementation could wait... until we decide if
we want to proceed any further with "convert"... I just do not want our
effort wasted. I hope you understand my hesitation.

V.

Andrzej Krzemienski

unread,
Feb 21, 2014, 4:38:44 AM2/21/14
to bo...@lists.boost.org
2014-02-21 10:31 GMT+01:00 Vladimir Batov <vb.ma...@gmail.com>:

> > Andrzej Krzemienski <akrzemi1 <at> gmail.com> writes:
> > ...
> > If you added MoveConstructible requirement (I am not saying you should, I
> > am just exploring the possibility), you would be probably saying "It also
> > works with non-CopyConstructible but MoveConstructible types, but in that
> > case there is a limited number of ways Boost.Convert's interface can be
> > used." The above example would not work, but the following one should:
> >
> > convert<std::string>::from(12345, cnv).value_or("bummer");
> >
> > That is, you do not care that a temporary's internal value is moved from.
> > But this may not be implementable in C++03. I guess we do not know how to
> > emulate rvalue references for *this. However, the following should be
> easy
> > to implement:
> >
> > convert<std::string>::from(12345, cnv).value();
> >
> > For some background, move semantics obtained a nice language support in
> > C++11, but it is also available in C++03 for some extent. This is why we
> > have Boost.Move. I will try to add move semantics to Boost.Optional, so
> > that you can use it.
>
> RE: convert<>::from().value();
>
> This one is fine as it returns a const reference (unless it throws. So, no
> additional requirements whatsoever... right?
>

Well, now that I think of it: implementing it may also require "rvalue
references from *this" feature, which may not be implementable in C++03.


>
> RE: MoveConstructible
>
> I do think that your clarifications should be in the docs and Rob's
> std::move() deployment in the code. Still, I feel that this sort of
> detailed
> explanations/clarifications/implementation could wait... until we decide if
> we want to proceed any further with "convert"... I just do not want our
> effort wasted. I hope you understand my hesitation.


Agreed.

Rob Stewart

unread,
Feb 21, 2014, 5:36:48 AM2/21/14
to bo...@lists.boost.org
I should have been clearer. If the argument is movable, move it. If not, don't. That means overloading based upon rvalue references vs. lvalue vs. small, by-value args. I was just showing the one case.


___
Rob

(Sent from my portable computation engine)

Rob Stewart

unread,
Feb 21, 2014, 5:31:51 AM2/21/14
to bo...@lists.boost.org
On February 21, 2014 3:12:08 AM EST, Vladimir Batov <vb.ma...@gmail.com> wrote:
>Rob Stewart <robertstewart <at> comcast.net> writes:
>> On February 20, 2014 6:27:42 PM EST, Vladimir Batov <Vladimir.Batov
><at>
>constrainttec.com> wrote:
>>
>> >I am especially surprised by the fact that its performance seems
>> > quite adequate (without any optimization):
>> [snip]
>> >tests. On the other hand the test could not be any more
>> >straightforward:

[snip]

>> I assume you're timing an optimized build, on which case you are
>being
>misled by the optimizer because
>> you're not using the results of the conversions.
>
>Rob,
>
>Nice hearing from you. I had that suspicion also and I do not think
>optimization was at play. The reason is that I timed twice with "-g"
>and
>without and definitely did not have optimization flags (never use
>them). And
>it's hard not to notice when the code is optimized -- stepping through
>is,
>well, difficult. :-)

Using -g simply includes symbols. It has nothing to do with optimizations. Not testing with optimizations is not worthwhile. sprintf() is highly tuned and C++ code relies on inlining, copy elision, etc. Users of your library will certainly use it with optimizations.

> Secondly, my actual code is different (for the
>post I
>cut it to bare minimum). All loops are actually like
>
>for (int k = 0; k < local::num_cycles; ++k)
>{
> int k = boost::convert<int>::from("12345", ccnv).value();
> BOOST_ASSERT(k == 12345);
>}
>
>Do you think there still might be something I missed?

The assertion won't help in an optimized build, of course. Save the values and print them at the end, after you've captured the elapsed times.


___
Rob

(Sent from my portable computation engine)

alex

unread,
Feb 21, 2014, 5:48:32 AM2/21/14
to bo...@lists.boost.org


>>All loops are actually like
>>
>>for (int k = 0; k < local::num_cycles; ++k)
>>{
>> int k = boost::convert<int>::from("12345", ccnv).value();
>> BOOST_ASSERT(k == 12345);
>>}
>>
>>Do you think there still might be something I missed?
>
>The assertion won't help in an optimized build, of course. Save the values
and
>print them at the end, after you've captured the elapsed times.
>

Isn't it also a problem that the inside of the loop doesn't vary? A smart
compiler may decide to evaluate boost::convert<int>::from("12345",
ccnv).value() only once and re-use the result?
Alex

Rob Stewart

unread,
Feb 21, 2014, 10:34:44 AM2/21/14
to bo...@lists.boost.org
On February 21, 2014 5:48:32 AM EST, alex <alexh...@hotmail.com> wrote:
>
>
>>>All loops are actually like
>>>
>>>for (int k = 0; k < local::num_cycles; ++k)
>>>{
>>> int k = boost::convert<int>::from("12345", ccnv).value();
>>> BOOST_ASSERT(k == 12345);
>>>}
>>>
>>>Do you think there still might be something I missed?
>>
>>The assertion won't help in an optimized build, of course. Save the
>values and
>>print them at the end, after you've captured the elapsed times.
>>
>
>Isn't it also a problem that the inside of the loop doesn't vary? A
>smart
>compiler may decide to evaluate boost::convert<int>::from("12345",
>ccnv).value() only once and re-use the result?

Of course, yes. I should have noticed that. Incrementing a value each iteration and printing that value at the end should work. Overflow isn't important. Just using the final value in an opaque way will prevent optimizing away the conversions in the loops.

___
Rob

(Sent from my portable computation engine)

alex

unread,
Feb 21, 2014, 12:21:12 PM2/21/14
to bo...@lists.boost.org
That seems easier said than done, because it is the value of a string that
needs to be unpredictable?

int sum = 0;
for (int k = 0; k < local::num_cycles; ++k)
{
char str[] = "12345";
str[4 - k % 5] = 49 + k % 9; //because the char '1' has value 49
sum += boost::convert<int>::from(str, ccnv).value();
}
std::cout << sum;

Vladimir Batov

unread,
Feb 21, 2014, 3:16:52 PM2/21/14
to bo...@lists.boost.org
> Rob Stewart <robertstewart <at> comcast.net> writes:
>>>> ...
>>> However, for non-movable types, the fallback is to copy anyway,
>>> so both should be moved.
>> ...
>>... if value_or() moves/destroys the string inside "res",
>>... then "res" is not usable from here downwards
>>... it's a problem, isn't it?
>
> I should have been clearer. If the argument is movable, move it. If not,
> don't. That means overloading based
> upon rvalue references vs. lvalue vs. small, by-value args. I was just
> showing the one case.

Yes, now that you mention moveable-based overloading I understand. Sorry for
being thick... If "convert" gets go-ahead, I'll certainly need help with
C++11 stuff... as from what I gather (in particular Meyer's ruminations
about universal references, etc) it sounds outright complicated.

Damn, gmane, forces me to trim almost all quoted text...

Vladimir Batov

unread,
Feb 21, 2014, 5:04:13 PM2/21/14
to bo...@lists.boost.org
alex <alexhighviz <at> hotmail.com> writes:

>
>
> >-----Original Message-----
> >From: Boost [mailto:boost-bounces <at> lists.boost.org] On Behalf Of Rob
Stewart
> >Sent: 21 February 2014 15:35
> >To: boost <at> lists.boost.org
> >Subject: Re: [boost] Boost.Convert. Take 2.
> >
> >On February 21, 2014 5:48:32 AM EST, alex <alexhighviz <at> hotmail.com>
Rob, Alex, thank you for the input. Humiliated by the magnitude of my own
ignorance. Learned something today. Optimized and tested as suggested.
Optimization gains seem to vary a lot depending on the compiler:

MS Visual C++ 2010 Express. Not optimized:

for user-defined type: lcast/convert=18.95/14.28 seconds.
for int type: scanf/lcast/convert=3.05/1.20/24.17 seconds.

MS Visual C++ 2010 Express. Optimized for speed:

for user-defined type: lcast/convert=6.36/2.95 seconds.
for int type: scanf/lcast/convert=0.70/0.86/5.59 seconds.

$ g++ --version
g++ (GCC) 4.8.2 Cygwin
$ g++ -O3 test_convert.cpp -I../..
for user-defined type: lcast/convert=5.42/4.14 seconds.
for int type: scanf/lcast/convert=1.08/1.16/2.38 seconds.

$ g++ -O0 test_convert.cpp -I../..
for user-defined type: lcast/convert=6.16/4.59 seconds.
for int type: scanf/lcast/convert=1.08/1.80/2.69 seconds.

Still what I am puzzled by is that lexical_cast performance table in the
docs claims to be far ahead of scanf... and I cannot reproduce their
numbers. :-(

Rob Stewart

unread,
Feb 21, 2014, 8:49:53 PM2/21/14
to bo...@lists.boost.org
On February 21, 2014 5:04:13 PM EST, Vladimir Batov <vb.ma...@gmail.com> wrote:
>
>Still what I am puzzled by is that lexical_cast performance table in
>the docs claims to be far ahead of scanf... and I cannot reproduce
>their numbers. :-(

Perhaps that's because you're using newer hardware or libraries than they used for their tests.


___
Rob

(Sent from my portable computation engine)

Rob Stewart

unread,
Feb 21, 2014, 2:29:20 PM2/21/14
to bo...@lists.boost.org
I'm not sure that's necessary. Unless there is a lot more inlining than I'm imagining, I don't think the optimizer will cause a problem with the string literal input.


___
Rob

(Sent from my portable computation engine)

Vladimir Batov

unread,
Feb 22, 2014, 2:19:40 AM2/22/14
to bo...@lists.boost.org
Rob Stewart <robertstewart <at> comcast.net> writes:
> On February 21, 2014 5:04:13 PM EST, Vladimir Batov <vb.mail.247 <at>
gmail.com> wrote:
> >
> >Still what I am puzzled by is that lexical_cast performance table in
> >the docs claims to be far ahead of scanf... and I cannot reproduce
> >their numbers.
>
> Perhaps that's because you're using newer hardware or libraries than they
> used for their tests.

Nuh, my home laptop about 10 years old. Trying the code with Microsoft
Visual C++ 2010 Express and gcc 4.8.2. Will try again on my Linux
power-house on Monday.

For fun I quickly hacked together a scanf-based converter and plugged it
into the "convert" framework. The optimized build is as efficient or close
to the raw scanf()... Only safe... And beats lexical_cast in my tests...

Alex Hagen-Zanker

unread,
Feb 22, 2014, 7:57:14 AM2/22/14
to bo...@lists.boost.org
I'm not sure either of course. But if I were a compiler, I would not
repeatedly do the same computation on two constants ("12345" and ccnv]. I am
not sure however if ccnv's mutable stream_ member will make the compiler
dismiss the const status of ccnv.

Vladimir Batov

unread,
Mar 2, 2014, 4:49:53 PM3/2/14
to bo...@lists.boost.org
On 02/21/2014 08:31 AM, Edward Diener wrote:
> On 2/16/2014 3:56 PM, Vladimir Batov wrote:
>> Addressed all three comments... I think. Checked in.
> ...
> This looks good. I think you have successfully focused on what is
> needed for a more flexible lexical_cast-like version this time.
> ...

I think I've addressed all comments/suggestions received so far and I
feel quite good about the current state of the documentation. In
particular I've extended the number of available/tested converters
(based on lexical_cast, strtol, sprintf, sstreams) and reflected that in
the Performance section.

The new location is at https://github.com/yet-another-user/boost.convert.git

What puzzles/disappoints me is this. lexical_cast has been around for
quite some time and with its potential standardization I presume quite a
bit of optimization effort went into it (I personally cannot understand
the code). On the other hand the "convert" code is dead simple with no
thoughts given to optimization. Still, "convert" easily "beats"
lexical_cast :-( on both "fronts" -- for basic conversions like
string-to-int and user-type conversions.

I applied all the tricks that Rob and Alex suggested regarding
performance tests to level out the playing field. Still, could that be
that lexical_cast is somehow disadvantaged in my tests?..

With "convert" I can plug easily different converters into the framework
when lexical_cast by comparison looks quite rigid. So, for basic
string-to-int boost::convert with the strtol converter gives me twice
the performance of lexical_cast. For complex user-type conversions I
plug sstream-based converter and again get twice the performance of
lexical_cast with the bonus of formatting and locale, etc.
What would be the domain where I might perfer lexical_cast? Not trying
to insult anyone. Only looking for answers.

Any additional input is most welcomed.

V.

Edward Diener

unread,
Mar 2, 2014, 9:54:28 PM3/2/14
to bo...@lists.boost.org
The lexical_cast functionality/syntax is a straightforward
implementation of the standard conversions between strings and basic
types without having to specify istringstream and ostringstream. Its
simplicity probably explains its popularity as well as anything. I would
guess that the vast majority of C++ programmers don't worry about speed
of execution very much. Similarly istringstream and ostringstream were
improvements over C functionality, integrating string back and forth
conversions into the C++ iostreams. If you feel that your library is an
improvement over lexical_cast I see no reason why it cannot be
considered as a Boost library pending a review. Sometimes small steps
are much better than large leaps.

As far as speed of execution a good profiler would probably tell you
where lexical_cast would be slower or faster when it executes just as
you could discover that for your own library. Perhaps then you would
discover why your library executes faster in your tests.
Reply all
Reply to author
Forward
0 new messages