Boost.Convert. Take 2.

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

On 2/9/2014 10:58 PM, Vladimir Batov wrote:
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
Subject: [Review] Boost.Convert, review manager analysis http://news.gmane.org/find-root.php?message_id=iq12fm%24k6c%241%40dough.gman... 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.
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.
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();
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.

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.

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.

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

Brian Ravnsgaard Riis
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...

-----Original Message----- From: Boost [mailto:boost-bounces@lists.boost.org] On Behalf Of Vladimir Batov Sent: Friday, February 14, 2014 9:44 AM To: boost@lists.boost.org Subject: Re: [boost] Boost.Convert. Take 2.
Brian Ravnsgaard Riis
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 pbristow@hetp.u-net.com

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.

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.

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.

Addressed all three comments... I think. Checked in. On 02/16/2014 02:58 AM, Edward Diener wrote:
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.

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.
On 02/16/2014 02:58 AM, Edward Diener wrote:
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.
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

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.

On February 20, 2014 6:27:42 PM EST, Vladimir Batov
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
On February 20, 2014 6:27:42 PM EST, Vladimir Batov
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, 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?

On February 21, 2014 3:12:08 AM EST, Vladimir Batov
Rob Stewart
writes: On February 20, 2014 6:27:42 PM EST, Vladimir Batov
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)

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

On February 21, 2014 5:48:32 AM EST, alex
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)

-----Original Message----- From: Boost [mailto:boost-bounces@lists.boost.org] On Behalf Of Rob Stewart Sent: 21 February 2014 15:35 To: boost@lists.boost.org Subject: Re: [boost] Boost.Convert. Take 2.
On February 21, 2014 5:48:32 AM EST, alex
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.
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;

On February 21, 2014 12:21:12 PM EST, alex
-----Original Message----- From: Boost [mailto:boost-bounces@lists.boost.org] On Behalf Of Rob Stewart Sent: 21 February 2014 15:35 To: boost@lists.boost.org Subject: Re: [boost] Boost.Convert. Take 2.
On February 21, 2014 5:48:32 AM EST, alex
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.
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;
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)

-----Original Message----- From: Boost [mailto:boost-bounces@lists.boost.org] On Behalf Of Rob Stewart Sent: 21 February 2014 19:29 To: boost@lists.boost.org Subject: Re: [boost] Boost.Convert. Take 2.
On February 21, 2014 12:21:12 PM EST, alex
wrote: -----Original Message----- From: Boost [mailto:boost-bounces@lists.boost.org] On Behalf Of Rob Stewart Sent: 21 February 2014 15:35 To: boost@lists.boost.org Subject: Re: [boost] Boost.Convert. Take 2.
On February 21, 2014 5:48:32 AM EST, alex
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.
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;
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.
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.

alex
-----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
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.
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;
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. :-(

On February 21, 2014 5:04:13 PM EST, Vladimir Batov
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
On February 21, 2014 5:04:13 PM EST, Vladimir Batov
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...

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.

On 3/2/2014 4:49 PM, Vladimir Batov wrote:
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.
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.

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
2014-02-14 20:16 GMT+01:00 Vladimir Batov

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

On February 16, 2014 5:13:24 PM EST, Vladimir Batov
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
When converting from T to string, you do not really need to return
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. 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.
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
-- Sent from my Android device with K-9 Mail. Please excuse my brevity.

2014-02-16 23:13 GMT+01:00 Vladimir Batov
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).
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.
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

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 = convertstd::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 = convertstd::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.

2014-02-17 22:25 GMT+01:00 Vladimir Batov
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

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.

2014-02-14 5:01 GMT+01:00 Vladimir Batov
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

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 = convertstd::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.

On February 20, 2014 5:48:18 PM EST, Vladimir Batov
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)

Rob Stewart
On February 20, 2014 5:48:18 PM EST, Vladimir Batov
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::optionalstd::string res = convertstd::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?

2014-02-21 9:25 GMT+01:00 Vladimir Batov
Rob Stewart
writes: On February 20, 2014 5:48:18 PM EST, Vladimir Batov
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::optionalstd::string res = convertstd::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?
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: convertstd::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: convertstd::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

Andrzej Krzemienski
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: convertstd::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:
convertstd::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.

2014-02-21 10:31 GMT+01:00 Vladimir Batov
Andrzej Krzemienski
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: convertstd::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:
convertstd::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.

On February 21, 2014 3:25:23 AM EST, Vladimir Batov
Rob Stewart
writes: On February 20, 2014 5:48:18 PM EST, Vladimir Batov
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::optionalstd::string res = convertstd::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?
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
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...

2014-02-10 4:58 GMT+01:00 Vladimir Batov
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
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.
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 = convertstd::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

... 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
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
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: 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. 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 = convertstd::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.

2014-02-10 22:22 GMT+01:00 Vladimir Batov
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

Andrzej Krzemienski
2014-02-10 22:22 GMT+01:00 Vladimir Batov
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.
participants (9)
-
alex
-
Alex Hagen-Zanker
-
Andrzej Krzemienski
-
Brian Ravnsgaard Riis
-
Edward Diener
-
Paul A. Bristow
-
Rob Stewart
-
Vladimir Batov
-
Vladimir Batov