
Hi Vladimir, Ed, all, It is a curiosity of Boost reviews that a vote of conditional acceptance is actually more powerful than a No vote, because if you vote No, the author might try to convince you to change your vote but otherwise has no incentive to listen to you any further. Therefore I vote to accept this library with the strong condition that a simpler syntax be adopted, doing away with the odd static ::from() function, and completely getting rid of the confusing, error-prone lazy functions. I am aware that the author thinks the present syntax is necessary, but I will argue against it in each case. (As another meta-review comment, I've noticed a few authors saying they won't consider any changes unless their library is accepted. This sets up a deadlock situation: the reviewer will not accept the library without the changes, and the author won't make the changes without acceptance. Everyone digs in their heels and nothing gets done. I think any author who really wants their library to be in Boost should be willing to submit it for a second review. And I think reviewers should tend toward conditions on acceptance rather than no votes. My two cents.)
- What is your evaluation of the design?
That's the focus of my review, specifically, the design of the interface. Supplying a default value, a don't-throw option, and applying manipulators/locale, are the major features missing from lexical_cast. So I think there is no doubt that a library like this is needed, if lexical_cast can't be upgraded. And according to its author, lexical_cast can't be upgraded because it must look like a cast and all these features require more parameters (template or actual). So, fine, let's have a replacement that's not .*_cast. It is incredibly clear what lexical_cast does because it uses the standard meaning of the operators, function calls, and return values. There is a lot of value in that. I don't think it's always a bad idea to have lazily evaluated objects or creative uses of operators, but IMHO if it is possible to do something more clearly with the standard order of evaluation and standard use of operators and function calls, then that should be preferred. The interface of the proposed Convert library is not simple enough. To start out with the most annoying thing, I don't like "::from" at all. The replacement should look as close to lexical_cast as possible. So, why the extra '::from'? Even though I disagree with the lazy evaluation (see below), even that doesn't seem to require this weird static member syntax. Is '::from' just so that '::result' will not feel lonely? For the rest of this review, I'm going to refer to convert<T>::from(s), but please keep in mind that I really think it should be something like string_convert<T>(s). So, next '::result'. It's some form of optional return. So why not just return optional? It is not much less to type convert<int>::result maybe = convert<int>::from("not an int"); versus optional<int> maybe = convert_cast<int, use_opt>("not an int"); (Where use_opt might also be spelled dont_throw, opt_out, .. I don't really care. Yes there may be problems because of the necessary deduced template argument for the source type. But isn't there a way around that with Boost.Parameter?) Why should the LHS modify the behavior of the proxy object returned by ::from? It's far too much to think about for such a simple operation. I'd rather the function clearly did or didn't throw on its own, not be influenced by its context. Lazy evaluation in expression templates is typically used to offer some kind of declarative syntax. So let's ask ourselves if that is needed here. The main innovation here is a lazily evaluated object which can then - throw or not throw based on its context convert<int>::result i = convert<int>::from("not a string"); - take iostream manipulators, which are applied before the conversion takes place. - take additional options through a second application of function call operator (!) All of these uses defy the way we ordinarily read function evaluation. What, the conversion is now feeding some data into std::hex? And then where does it go after that? It gets returned? Weird. The right shift operator was chosen in the standard because it depicts an arrow; otherwise there is nothing that says I/O about it. Thus the only way this syntax would make sense to me is if it were int i = std::hex >> convert<int>::from("deadbeef"); and then unfortunately it would be impossible to have more than one manipulator without some horrible parentheses, because of the left associativity. I don't like the look of streaming operators that lead nowhere. No, why not just int i = convert<int>::from(s, std::hex); ? Or from(s, _manip = std::hex) Or, at worst (and I don't see why this is necessary) from(s)(_manip = std__hex); If you want the operator syntax, just do int i; stringstream(s) >> std::hex >> i; If it's really got to be one line, a syntax like int i = stringstream_(s) >> std::hex >> ret_; would at least make sense and I'm sure that there's a way to do that almost as succinctly using Phoenix. On a similar nice-feature-but-weird-syntax note, let's look at the Using Boost.Convert with Standard Algorithms section, which offers a few ways that you can use convert<T>::from() to create a functor: std::transform( strings.begin(), strings.end(), std::back_inserter(integers), convert<int>::from(string(), -1) >> std::hex); std::transform( strings.begin(), strings.end(), std::back_inserter(integers), convert<int>::from<string>()(fallback_ = -1) >> std::hex); Apparently if you pass an empty string or omit the parameter to from(), it creates a function object which takes a string as a parameter. Is this correct? This seems like it would have unwanted consequences, since we now have a function object that can take either extra options or the string input. Again, I'm not against clever syntax, but this seems promiscuous and confusing. I think it would be much more appropriate to provide a Phoenix function object for the purpose, something like std::transform( strings.begin(), strings.end(), std::back_inserter(integers), convert_<int>(_1, -1, std::hex)); Sigh of relief. I see what that means immediately, and it doesn't make me worry about what convert's going to do if I forget a parameter. I agree with Jeroen's request for a default-constructed version. I don't see why no syntactic sugar could be doled out for this, a _defcon (template or real) parameter or whatever. I also want to mention type-to-type conversion, which Vicente Botet has thought out much more thoroughly in his Conversion library. This library offers type-to-type conversion seemingly as an afterthought, just because the syntax allows it. I definitely see the value of a conversion mechanism that first looks for a direct type-to-type conversion and then defaults to using type-to-string-to-type. I just don't think conversion should always do that, and I don't think such a mechanism should be called plain convert. It does make any more sense to call such a thing plain convert than it does to start optimizing lexical_cast with special cases that don't use strings (sorry!). It would be called best_conversion or something that conveys that it's doing the best it can based on what converters have been "installed" at compile time. Again my objection is not to the functionality but to an obfuscating interface where you're not sure what behavior you're going to get. I feel the same about the string-to-string conversion: it seems to be in there just because the syntax supports it, not because it actually makes sense here. I guess this is because library is called Convert instead of String Convert or Via Stream Convert, which is its main focus (and rightly so). Apologies for repeating this: I still don't get why the extra options go into a second function call operator. I found some earlier messages where you simply added those to the first function call. Simplify, simplify, simplify the interface. Please. To summarize, I don't get why we can't just have this simple clear syntax: optional<int> i = string_convert<int, dont_throw>(s); // or string_convert<int>(s, _throw=false) if that's impossible int i = string_convert<int>(s, manip_ = std::hex); It's (plain) functional. There's no question when things get evaluated or thrown. I don't care about any of the particular names used, just the syntax.
- What is your evaluation of the implementation?
I read the code enough to answer my own questions about what the library actually does. As noted, I disagree with many design choices, but the implementation seems fine. The only thing that caught my eye as a little strange is that there's the string_sfinae namespace and header which in fact contains a bunch of metafunctions which are sometimes used in sfinae and sometimes just used as metafunctions. Obviously not a big deal, just some metonymy or historical artifact there. To return to convert<>::result On Apr 27, 2011, at 7:07 PM, Vladimir Batov wrote:
They, indeed, seem to provide *similar* functionality. In fact, convert::result *uses* boost::optional. If you have a look at convert::result (it's quite small), then everything else in addition to boost::optional is what convert::result adds. In words, IMHO convert::result is better than boost::optional tailored for its domain (conversions).
So you made me look. I conclude that the only functionality it adds is the ability to delay throwing on error until the result::value() is called, am I wrong? I don't understand why you'd want this, because I thought the whole point of using ::result was when you didn't want to throw. But maybe I'm missing something. Again I think this could all be SO much simpler.
- What is your evaluation of the documentation?
It's well written and explains the subject well. It seems a bit flippant in places ("I've included this and that because why not?" kinds of statements) and IMO that's because the focus of this library is a little too fuzzy. Stick to string/stream conversion, fix the problems with lexical cast. Don't try to build a general conversion framework unless you're really willing to think that through. I looked for a Rationale section that would explain why this functionality could not be included in lexical_cast, and could not find it. Vladimir, I know you said On Apr 26, 2011, at 6:47 PM, Vladimir Batov wrote:
I again urge you to please dig the archives to see why that "extra functionality should be added to boost::lexical_cast" view is not exactly original and most importantly why it was decided *not* to proceed as you insist.
But I don't think that's a substitute for explaining to new users why they can't get what they want with modest improvements on the lexical_cast interface. Why is it that they really want some fancy lazy function object / "declarative" interface? Likewise with this non-argument: On Apr 26, 2011, at 6:47 PM, Vladimir Batov wrote:
I've come to a firm conclusion that that additional functionality/configurability is *not* implementable within the boundaries of the lexical_cast interface. In particular, the TypeIn and TypeOut types must be discriminated, i.e. one type has to be specialized first. Without going into details I'll just say that otherwise is just does not work. So, it has to be convert<TypeOut>::from<TypeIn>. You can take my word for it or you could walk that road yourself.
Sorry, but I find this response either lazy or rude. You wrote the library: you explain to us why the way it is! I'd really like to know why you think it's necessary to do convert<T>::from(s) rather than the much more familiar and easy-to-grasp string_convert<T>(s). I don't want to try to write it myself to find out why it does or doesn't need to be that way. You entered a fairly contentious arena with a majorly different interface. Write a rationale and don't just tell us to search the list.
- What is your evaluation of the potential usefulness of the library?
Very, very useful. I use lexical_cast and am frustrated by the limitations.
- Did you try to use the library? With what compiler? Did you have any problems?
No, I did not - I see how it works and trust that it does. I was not tempted to start looking for ways to make the library produce surprising results because it was immediately clear to me from the interface design that those misbehaviors would exist. I'm not always a Keep It Simple person, but in this case, please do. Otherwise you're destroying one of the best features of lexical_cast.
- How much effort did you put into your evaluation? A glance? A quick reading? In-depth study?
I spent about 5 hours reading the documentation and code, and 5 hours reading/rereading the current reviews and writing mine. I wish I'd had time to go back to the earlier discussions, although I searched a little bit; again I think everything that could be learned there should have been in the Rationale.
- Are you knowledgeable about the problem domain?
Moderately. I like to know how every construct I'm using works, and I gulp at the inefficiency of lexical_cast every time I use it, even though I never use it where efficiency matters. To further prove my point that conditional acceptance is stronger than voting no, note that I've basically made my yes vote conditional on fixing most of the things Thomas Heller complained about, notably On Apr 26, 2011, at 2:58 PM, Thomas Heller wrote:
Moreover, I don't get how the design really is superior to boost::lexical_cast, and why it couldn't been implemented as an extension to that library. (It can't take a .*_cast name, but it can use the simple syntax.)
The use of this converter function object looks like a nice concept at first sight but, as mentioned above, in the end, probably adds more confusion and problems than anything else. (It confused me and didn't look nice.)
I don't think that having everything hidden behind this static from function is wise and should be rethought. Different functions, for different purposes would be better suited. On Apr 27, 2011, at 5:14 AM, Thomas Heller wrote: Even though you tried to be minimal and simplistic, the function is overloaded with too different (in concept) behavior that its really confusing to get what the function is really doing. So my end result is that its not really minimal and minimalistic, but bloated ... sometimes, more is less. (I think more template or function parameters could also do the trick.)
I realize that my conditions mean some serious redesign, but this is an 834-line library and it would probably be even shorter without all the unnecessary lazy functions and weird out-of-place operators. I want to contrast this with my No vote in the XInt review, where I also had issues with the design. I would have voted for conditional acceptance of XInt if it were possible, but the changes there seemed like they would need additional review. String Convert is much simpler and I think that a compromise could be reached to (IMO) simplify the interface and answer most of the objections that Thomas, Jeroen, and I have made. I apologize to Ed for making a complicated vote. I'd be glad to clarify my position if needed. Cheers to Vladimir - you've identified an important problem! Now just give it a clear interface and we'll have something good. Gordon

Gordon Woodhull wrote:
I think any author who really wants their library to be in Boost should be willing to submit it for a second review. And I think reviewers should tend toward conditions on acceptance rather than no votes. My two cents.
I think the least important element of any of my reviews has been the headline yes/no "vote", since most proposals have had good and bad aspects, so I have been worried that the last couple of review results have actually called these "votes" and counted them. My recollection (which may be flawed) is that in the past we avoided ever calling reviews "votes". Regarding second reviews, the counter-argument is that we have limited reviewing resources and so authors should present their proposals in what they believe is the final state. If an additional round is needed, I would prefer that to be in the form of more "previews". Regards, Phil.

On May 2, 2011, at 12:16 PM, Phil Endecott wrote:
I think the least important element of any of my reviews has been the headline yes/no "vote", since most proposals have had good and bad aspects, so I have been worried that the last couple of review results have actually called these "votes" and counted them. My recollection (which may be flawed) is that in the past we avoided ever calling reviews "votes".
I do remember a different emphasis when I started following the list about four years ago. I think it's better to think of reviews as consensus-building. Concentrating on the vote just polarizes people.
Regarding second reviews, the counter-argument is that we have limited reviewing resources and so authors should present their proposals in what they believe is the final state. If an additional round is needed, I would prefer that to be in the form of more "previews".
Yes, it can be a lot of work to review a library or manage a review. Certainly authors should take the process seriously. I just worry about them taking it too seriously and saying "well I'll just take my ball and go home." We need strong egos to propose daring libraries, perhaps stronger egos to accept criticism about them.

Gordon Woodhull wrote:
Therefore I vote to accept this library with the strong condition that a simpler syntax be adopted, doing away with the odd static ::from() function, and completely getting rid of the confusing, error-prone lazy functions. I am aware that the author thinks the present syntax is necessary, but I will argue against it in each case.
No one would argue against a simpler interface that does all of what Boost.Convert does. Would you take the time to suggest a complete design? That would be fairest to Vladimir who spent a long time trying to devise a useful interface that encompassed the several usage styles wanted and likely won't easily be able to look past the current design to something else now. Besides, it would provide a concrete idea others can see and discuss. _____ Rob Stewart robert.stewart@sig.com Software Engineer using std::disclaimer; Dev Tools & Components Susquehanna International Group, LLP http://www.sig.com IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

On 05/02/2011 08:26 AM, Gordon Woodhull wrote:
[stuff involving various tag_ names]
One mistake made in a lot of Boost documentation and discussion of Boost interfaces is the omission of namespace qualification, under the assumption that appropriate using declarations have been made. However, in practice someone is unlikely to make those using declarations, and in any case it turns a one line call to convert into a potentially 3 or more using declarations followed by a call to convert. Thus, I propose that any time a code sample is provided, it be in a form that legitimately might occur in an actual program, so that it is apparent whether the namespace qualification is going to render an apparently nice interface into an excessively verbose one. You propose that a bunch of tag_ names be used, but in practice they would likely have to be something like boost::convert::tag_, which would substantially reduce the benefits of any such change.

Hi,
Message du 02/05/11 17:28 De : "Gordon Woodhull" A : boost@lists.boost.org Copie à : Objet : [boost] [review] string convert
Hi Vladimir, Ed, all,
- What is your evaluation of the design?
That's the focus of my review, specifically, the design of the interface.
Supplying a default value, a don't-throw option, and applying manipulators/locale, are the major features missing from lexical_cast. So I think there is no doubt that a library like this is needed, if lexical_cast can't be upgraded. And according to its author, lexical_cast can't be upgraded because it must look like a cast and all these features require more parameters (template or actual). So, fine, let's have a replacement that's not .*_cast.
It is incredibly clear what lexical_cast does because it uses the standard meaning of the operators, function calls, and return values. There is a lot of value in that.
I don't think it's always a bad idea to have lazily evaluated objects or creative uses of operators, but IMHO if it is possible to do something more clearly with the standard order of evaluation and standard use of operators and function calls, then that should be preferred.
The interface of the proposed Convert library is not simple enough. To start out with the most annoying thing, I don't like "::from" at all. The replacement should look as close to lexical_cast as possible.
So, why the extra '::from'? Even though I disagree with the lazy evaluation (see below), even that doesn't seem to require this weird static member syntax. Is '::from' just so that '::result' will not feel lonely?
For the rest of this review, I'm going to refer to convert::from(s), but please keep in mind that I really think it should be something like string_convert(s).
I wanted to add more on the this direction. Vladimir, note that maybe the syntax proposed below could be not correct in C++, but I hope you will get the idea. What do you think of using * Instead of int i1 = convert::from(str); // Throws if the conversion fails the following int i1 = string_convert(str); // Throws if the conversion fails * Instead of int i2 = convert::from(str, -1); // Returns -1 if the conversion fails the following int i2 = string_convert(defaults_to(str, -1)); // Returns -1 if the conversion fails defaultts_to(T,U) is a function that generates a wrapper to T and default value U. * Instead of convert::result res = convert::from(str, up_dir); the following optional res = string_convert >(str); * Instead of int i = convert::from(hex_str, -1) >> std::hex; the following int i = string_convert(defaults_to(as_istream(hex_str) >> std::hex, -1); as_istream is a function that convert a type to an input stream (provided the type is output streamable). * Instead of string si = string_convert::from(i)
std::showbase >> std::uppercase >> std::hex;
the following string si = string_convert(as_istream(i) >> std::showbase >> std::uppercase >> std::hex); * Instead of string d1 = string_convert::from(d)(locale_ = rus_locale)
std::setprecision(4) std::scientific;
the following string d1 = string_convert(as_istream(d) >> set_locale(rus_locale) >> std::setprecision(4)
std::scientific;
here set_locale can be a manipulator. Of course the string_convert function must be overloaded when the source parameter is a defaults_to or an input stream and when the target parameter is optional and the function is extensible. In this way the interface follows always the same schema. One source parameter convertible to one target. Just my 2cts, Vicente

Vicente BOTET wrote:
De : "Gordon Woodhull"
The interface of the proposed Convert library is not simple enough. To start out with the most annoying thing, I don't like "::from" at all. The replacement should look as close to lexical_cast as possible.
So, why the extra '::from'? Even though I disagree with the lazy evaluation (see below), even that doesn't seem to require this weird static member syntax. Is '::from' just so that '::result' will not feel lonely?
For the rest of this review, I'm going to refer to convert::from(s), but please keep in mind that I really think it should be something like string_convert(s).
I wanted to add more on the this direction. Vladimir, note that maybe the syntax proposed below could be not correct in C++, but I hope you will get the idea.
What do you think of using
* Instead of
int i1 = convert::from(str); // Throws if the conversion fails
That should be convert<int>::from(str).
the following
int i1 = string_convert(str); // Throws if the conversion fails
You haven't specified the target type. You'd need: int I = string_convert<int>(str); which doesn't read well.
* Instead of
int i2 = convert::from(str, -1); // Returns -1 if the conversion fails
the following
int i2 = string_convert(defaults_to(str, -1)); // Returns -1 if the conversion fails
defaultts_to(T,U) is a function that generates a wrapper to T and default value U.
You have the same problems here, of course. Adding the wrapper really looks bad. I'd prefer something more like this: int I = convert<int>::from(str, default_(-1)); if you wish to make it explicit.
* Instead of
int i = convert::from(hex_str, -1) >> std::hex;
the following
int i = string_convert(defaults_to(as_istream(hex_str) >> std::hex, -1);
as_istream is a function that convert a type to an input stream (provided the type is output streamable).
I see that you're trying to make "as_istream(hex_str) >> std::hex" look like a normal stream that plugs into your string_convert function, but it looks very ugly to me.
* Instead of
string d1 = string_convert::from(d)(locale_ = rus_locale)
std::setprecision(4) std::scientific;
the following
string d1 = string_convert(as_istream(d) >> set_locale(rus_locale) >> std::setprecision(4)
std::scientific;
here set_locale can be a manipulator.
With that locale, you could also have: string s = convert<string>::from(d)
set_local(rus_locale) >> std::setprecision(4) std::scientific;
I understand that there are things hiding behind a façade: - convert<string>::from(d) returns a convert<string>::result - convert<string>::from(d) acts like a std::istream WRT manipulators - convert<string>::from() returns a convert<string>::converter I also get that convert<string>::result converts implicitly to a string but can also be queried via a safe-bool and directly for the string, but adding lots of explicit wrappers into the mix just obfuscates the desired conversion operation. It makes things more obvious, but syntactically obese. _____ Rob Stewart robert.stewart@sig.com Software Engineer using std::disclaimer; Dev Tools & Components Susquehanna International Group, LLP http://www.sig.com IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

Message du 02/05/11 20:23 De : "Stewart, Robert" A : "'boost@lists.boost.org'" Copie à : Objet : Re: [boost] [review] string convert
Vicente BOTET wrote:
De : "Gordon Woodhull"
The interface of the proposed Convert library is not simple enough. To start out with the most annoying thing, I don't like "::from" at all. The replacement should look as close to lexical_cast as possible.
So, why the extra '::from'? Even though I disagree with the lazy evaluation (see below), even that doesn't seem to require this weird static member syntax. Is '::from' just so that '::result' will not feel lonely?
For the rest of this review, I'm going to refer to convert::from(s), but please keep in mind that I really think it should be something like string_convert(s).
I wanted to add more on the this direction. Vladimir, note that maybe the syntax proposed below could be not correct in C++, but I hope you will get the idea.
What do you think of using
* Instead of
int i1 = convert::from(str); // Throws if the conversion fails
That should be convert::from(str).
the following
int i1 = string_convert(str); // Throws if the conversion fails
You haven't specified the target type. You'd need:
You are rigth. I forget the template parameter.
int I = string_convert(str);
which doesn't read well.
Does int I = convert_to(str); reads better?
* Instead of
int i2 = convert::from(str, -1); // Returns -1 if the conversion fails
the following
int i2 = string_convert(defaults_to(str, -1)); // Returns -1 if the conversion fails
defaultts_to(T,U) is a function that generates a wrapper to T and default value U.
You have the same problems here, of course. Adding the wrapper really looks bad. I'd prefer something more like this:
int I = convert::from(str, default_(-1));
if you wish to make it explicit.
I would prefer int I = convert_to(str, default_(-1));
* Instead of
int i = convert::from(hex_str, -1) >> std::hex;
the following
int i = string_convert(defaults_to(as_istream(hex_str) >> std::hex, -1);
as_istream is a function that convert a type to an input stream (provided the type is output streamable).
I see that you're trying to make "as_istream(hex_str) >> std::hex" look like a normal stream that plugs into your string_convert function, but it looks very ugly to me.
Yes, it is ugly. Maybe the following is less ugly int i = -1; as_istream(hex_str) >> std::hex >> i ;
* Instead of
string d1 = string_convert::from(d)(locale_ = rus_locale)
std::setprecision(4) std::scientific;
the following
string d1 = string_convert(as_istream(d) >> set_locale(rus_locale) >> std::setprecision(4)
std::scientific;
here set_locale can be a manipulator.
With that locale, you could also have:
string s = convert::from(d)
set_local(rus_locale) >> std::setprecision(4) std::scientific;
or just as_istream(d) >> set_locale(rus_locale) >> std::setprecision(4) >> std::scientific >> s;
I understand that there are things hiding behind a façade:
- convert::from(d) returns a convert::result - convert::from(d) acts like a std::istream WRT manipulators - convert::from() returns a convert::converter
I also get that convert::result converts implicitly to a string but can also be queried via a safe-bool and directly for the string, but adding lots of explicit wrappers into the mix just obfuscates the desired conversion operation. It makes things more obvious, but syntactically obese.
You are right trying to force all around a single function gives non natural results. This is the main issue with this library. I'm sure we can find some more explicit, readable and acceptable syntax. Best, Vicente

De : "Stewart, Robert" I understand that there are things hiding behind a façade:
- convert::from(d) returns a convert::result - convert::from(d) acts like a std::istream WRT manipulators - convert::from() returns a convert::converter
I also get that convert::result converts implicitly to a string but can also be queried via a safe-bool and
Vicente BOTET <vicente.botet <at> wanadoo.fr> writes: directly for the string, but adding lots of explicit wrappers into the mix just obfuscates the desired conversion operation. It makes things more obvious, but syntactically obese.
You are right trying to force all around a single function gives non natural results. This is the main issue with this library. I'm sure we can find some more explicit, readable and acceptable syntax.
Correction. If you have a look at the convert::from() signature(s), you'll see that all overloads of the function *always* return the same -- namely the configured converter. Simple, uniform, unambiguous, non-confusing. Now it's up to the respective converter to implement (or not) certain functionality. Say, a fast converter will sacrifice locales/formatting for speed, etc. I'd personally like to see different converters available which the user would choose from via #include <convert/string-to-int-spitit.hpp> or something of that sort. V.

Vicente BOTET wrote:
De : "Stewart, Robert" Vicente BOTET wrote:
De : "Gordon Woodhull"
You haven't specified the target type. You'd need:
You are rigth. I forget the template parameter.
int I = string_convert(str);
which doesn't read well.
Does int I = convert_to(str); reads better?
You forgot the target type again. Let's compare: convert<int>::from(str); convert_to<int>(str); Your version reads pretty nicely and is shorter. For that particular use case, I'd call it better. Indeed, you might as well make it a cast: convert_cast<int>(str); That would make it completely self-documenting as it follows the new-style cast interface. However, "convert" isn't quite as useful as a cast descriptor and then there's the question of the other behaviors.
* Instead of
int i2 = convert::from(str, -1); // Returns -1 if the conversion fails
the following
int i2 = string_convert(defaults_to(str, -1));
defaultts_to(T,U) is a function that generates a wrapper to T and default value U.
You have the same problems here, of course. Adding the wrapper really looks bad. I'd prefer something more like this:
int i = convert::from(str, default_(-1));
convert<int>::from(str, default_(-1));
if you wish to make it explicit.
I would prefer
int I = convert_to(str, default_(-1));
convert_to<int>(str, default_(-1));
* Instead of
int i = convert::from(hex_str, -1) >> std::hex;
the following
int i = string_convert(defaults_to(as_istream(hex_str)
std::hex, -1);
as_istream is a function that convert a type to an input stream (provided the type is output streamable).
I see that you're trying to make "as_istream(hex_str) >> std::hex" look like a normal stream that plugs into your string_convert function, but it looks very ugly to me.
Yes, it is ugly. Maybe the following is less ugly
int i = -1; as_istream(hex_str) >> std::hex >> i ;
That's pretty reasonable. Streams are known to set an error state, possibly throwing an exception when doing so, and so the default -1 would be intact should hex_str not be parsable as a number. There is one thing missing in that approach, however: how does one determine if the conversion fails for a type without a useful default value? convert<T>::result provided a safe-bool interface to determine whether the conversion succeeded. That permits initializing the value to hold the conversion without having to use that initial value as the sentinel for failure. This is why I asked Gordon for a complete interface. Only when all of the use cases are addressed can one make a reasonable comparison with the interface submitted for review.
* Instead of
string d1 = string_convert::from(d)(locale_ = rus_locale)
std::setprecision(4) std::scientific;
the following
string d1 = string_convert(as_istream(d)
set_locale(rus_locale) >> std::setprecision(4) std::scientific;
here set_locale can be a manipulator.
With that locale, you could also have:
string s = convert::from(d)
set_local(rus_locale) >> std::setprecision(4) std::scientific;
or just
as_istream(d) >> set_locale(rus_locale) >> std::setprecision(4)
std::scientific >> s;
OK
I understand that there are things hiding behind a façade:
- convert::from(d) returns a convert::result - convert::from(d) acts like a std::istream WRT manipulators - convert::from() returns a convert::converter
I also get that convert::result converts implicitly to a string but can also be queried via a safe-bool and directly for the string, but adding lots of explicit wrappers into the mix just obfuscates the desired conversion operation. It makes things more obvious, but syntactically obese.
You are right trying to force all around a single function gives non natural results. This is the main issue with this library. I'm sure we can find some more explicit, readable and acceptable syntax.
Many things were discussed some years ago. You have some interesting ideas, but you're missing the function object part of the interface (or did we lose it through snipping context?) and the safe-bool/value part. The ability to reuse the converters and pass them to algorithms is a nice feature. The ability to use types without default constructors or usable sentinel values is necessary. Valdimir's attempt to resolve all of the forces is what is being reviewed. However, he seems open to making something better, so keep at it. _____ Rob Stewart robert.stewart@sig.com Software Engineer using std::disclaimer; Dev Tools & Components Susquehanna International Group, LLP http://www.sig.com IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

On 05/02/2011 01:36 PM, Vicente BOTET wrote:
[snip text missing angle brackets]
Hello Vicente, For some reason your e-mail client does not send less than/greater than brackets properly. As a result, at least when the messages are retrieved through gmane (the newsgroup mirror of the boost mailing list), they are just completely missing from the source of your message. It may be related to the use of HTML e-mail at some point, but by the time the message is retrieved from gmane, the message content is in plain text and there is no trace of the brackets. (This is confirmed from looking at the message source manually.) Note that it appears that, depending on the context, the brackets sometimes get through, and sometimes do not, and often some text between the brackets is also missing in addition to the brackets themselves. I suspect that configuring your e-mail client to send messages as plain text, assuming it is currently sending as HTML, may resolve this problem. Otherwise, some other solution may be necessary. Note that I tried to send a message on this topic to your personally a little while ago, but it seems you may not have received it.

Gordon Woodhull <gordon <at> woodhull.com> writes: Hi Vladimir, Ed, all, ... I am aware that the author thinks the present syntax is necessary, but I will argue against it in each case.
If the library is accepted (which seem unlikely so far and that's truly OK with me), then I am prepared to discuss and try out alternative approaches (if they are in some concrete "try-able" form). The reason I've "come up" with the API as it is now is because during discussions some 2-3 years ago I in fact tried various proposed interfaces as we were discussing them. The results of those trial-n-errors is this current API.
It is incredibly clear what lexical_cast does because it uses the standard meaning of the operators, function calls, and return values.
int i = lexical_cast<int>(str); int i = convert<int>::from(str); When I look at the above where lexical_cast and 'convert' provide the same functionality, I am not sure if from the user perspectives I immediately see how 'convert' differs from lexical_cast complexity-wise. I am certainly biased.
There is a lot of value in that.
Yes and no. From the design perspectives I agree -- simple and easy to grasp.
From the user perspectives I have to disagree as that simple design results in an implementation which has very little value for my deployment.
I don't think it's always a bad idea to have lazily evaluated objects or creative uses of operators, but IMHO if it is possible to do something more clearly with the standard order of evaluation and standard use of operators and function calls, then that should be preferred.
I hear your dissatisfaction but I am not sure I have enough information to act on addressing it. We can discuss it further if/when the time comes.
The interface of the proposed Convert library is not simple enough. To start out with the most annoying thing, I don't like "::from" at all. The replacement should look as close to lexical_cast as possible.
I'd try distinguishing "not simple enough" and "annoying". The *user* API is one (!) function -- convert::from -- on one side and *optionally* convert::result on the receiving side. I really do not know how to simplify it any further. I personally do not consider changing that "annoying" from() to something else to be a simplification. So, by "simplification" you probably had something else in mind and I am all prepared to listen. As for "I don't like "::from" at all", then it's a personal choice and we'll never get a consensus here. The practical reason for "::from" is two-fold -- 1) it clearly shows the direction of the conversion (which's been deemed important during original discussions); 2) the syntax has to be convert<TypeOut>::from<TypeIn> (replace 'convert' and 'from' with the identifiers of your choice), i.e. two types need to be evaluated separately. Otherwise, various specializations (if there are such) conflict. That's why the lexical_cast-like API (where both TypeIn and TypeOut evaluated at the same time) does not work (at least I did not manage it to work). For example, one might have a string-to-type specialization. Then if one wants to write string-to-bool, it won't compile as both specializations will match.
So, why the extra '::from'?
See the (brief) explanation above.
... seem to require this weird static member syntax.
I am not sure I am following. What's exactly weird about it (apart from your personal syntax preferences)? It's the standard C++ syntax, right?
Is '::from' just so that '::result' will not feel lonely?
:-)
I really think it should be something like string_convert<T>(s).
We had these discussions when I was writing 'convert'. string_convert<T>(s) is not good (as far as *I* tried) because it does not indicate direction of the conversion; it is limited to strings (which is again open for (mis)interpretation); it has TypeIn and TypeOut evaluated at the same time which did not work when I tried.
So, next '::result'. It's some form of optional return. So why not just return optional? It is not much less to type convert<int>::result maybe = convert<int>::from("not an int"); versus optional<int> maybe = convert_cast<int, use_opt>("not an int");
Yes, convert::result does not offer *much* beyond boost::optional. However, it *does* offer more. That's the reason it exists. Namely, convert::result provides two pieces of information -- if the conversion failed/succeeded *and* the fallback value (if provided). boost::optional provides one *or* the other. The difference might be considered subtle but it's there.
Why should the LHS modify the behavior of the proxy object returned by ::from?
Because usages of 'convert' differ -- namely, throwing and non-throwing behavior. I personally do not use throwing behavior at all but I feel it was needed to provide/support the behavior (and resulting mess) for lexical_cast backward compatibility and to cater for usages different from mine.
It's far too much to think about for such a simple operation.
Not really. Well, I do not see it that way anyway. convert<int>::result res = convert<int>::from("blah", -1); if (!res) message("conversion failed"); int value = res.value(); // proceed with the fallback anyway. The locality of 'convert' application (IMHO of course) does not leave much to think how 'convert' is to behave.
I'd rather the function clearly did or didn't throw on its own, not be influenced by its context.
Understood. I feel the behavior complexity (as perceived by you anyway) was driven by the complexity/variability of usages/applications. If it can be done easier, I'd like to hear that (if we proceed accepting 'convert' -- for *me* it already does all I want it to do).
convert<int>::result i = convert<int>::from("not a string"); - take iostream manipulators, which are applied before the conversion takes place. - take additional options through a second application of function call operator (!)
All of these uses defy the way we ordinarily read function evaluation. What, the conversion is now feeding some data into std::hex? And then where does it go after that? It gets returned? Weird.
"Weird" is a subjective thing. If you look under the hood (of this library, other libraries or, say, as a washing machine), then it might indeed look "weird". From the user perspectives though I feel it's quite straightforward. That's my view, of course and we can discuss it later if needs be.
The right shift operator was chosen in the standard because it depicts an arrow; otherwise there is nothing that says I/O about it. Thus the only way this syntax would make sense to me is if it were int i = std::hex >> convert<int>::from("deadbeef"); and then unfortunately it would be impossible to have more than one manipulator without some horrible parentheses, because of the left associativity.
Well, again it's an endless debate of one's API look-n-feel preferences over somebody else's. Beauty is in the eyes of beholder. I do not see parentheses as horrible. In fact, I love them (even though I am not a Lisp guy) :-).
I don't like the look of streaming operators that lead nowhere.
No, why not just int i = convert<int>::from(s, std::hex); ? Or from(s, _manip = std::hex) Or, at worst (and I don't see why this is necessary) from(s)(_manip = std__hex);
The huge *practical* advantage of operator() is chaining. I personally do not see one or the other of the below simpler or harder and both are of equal length: from(s, _manip = std::hex); from(s)(_manip = std::hex); However, the first one has the limit on the number of args. Second does not. Second is much easier/cleaner to implement (therefore, to maintain which is even more important) as #1 requires from() overloading or def. parameters. Thirdly, second looks more natural as a progression of increasing complexity as the user gains additional knowledge: int i = from(s); // simplest int i = from(s)(_manip = std::hex); int i = from(s)(_manip = std::hex)(locale_ = ...); Implementation for the above is considerably cleaner as from() does its own thing. op(_manip) does its own and again op(locale_) does its own. Each piece of respective functionality is well encapsulated in the relevant function. Beautiful. :-) IMHO of course. I wrote it that way. You'd probably write it differently and, then, I'd probably criticize your API. :-) So far, personal preferences aside the op()-based API seems to be the winner due its practicality and extensibility.
On a similar nice-feature-but-weird-syntax note, let's look at the Using Boost.Convert with Standard Algorithms section, which offers a few ways that you can use convert<T>::from() to create a functor:
std::transform( strings.begin(), strings.end(), std::back_inserter(integers), convert<int>::from(string(), -1) >> std::hex); ... I think it would be much more appropriate to provide a Phoenix function object for the purpose, something like std::transform( strings.begin(), strings.end(), std::back_inserter(integers), convert_<int>(_1, -1, std::hex)); Sigh of relief. I see what that means immediately, ...
Apologies for sounding as a broken record but again that preference seems quite personal. As a user not familiar with Phoenix your variant would not look familiar to me. In order to use it I'd need to learn/remember/know additional API -- convert_, _1, args order. My deployment seems simpler (not surprisingly some bias here :-) ) as the user uses the same API and does not need to learn more to use 'convert' with algorithms. Mentioning that not for the sake of arguing tooth and nail for *my* API. I am happy to consider alternatives. I just do not see your suggested API as a clear winner (not yet anyway).
I also want to mention type-to-type conversion, which Vicente Botet has thought out much more thoroughly in his Conversion library. This library offers type-to-type conversion seemingly as an afterthought, just because the syntax allows it.
I have to disagree. Again I consider 'convert' to be more of a conversion framework rather than a library. Indeed, 'convert' does not provide much more beyond string-to-type conversions because I personally did/do not need more. However, it does not mean I/we should deny that possibility to others as their needs may differ. Therefore, 'convert' was designed with that in mind. If Vicente needs type-to-type conversion functionality, I think he might consider implementing that functionality within the 'convert' framework by extending the 'convert' library for everyone's benefit. I do not think just one person can provide every possible conversion. I cannot anyway. And with 'convert' I am not saying I do.
I feel the same about the string-to-string conversion: it seems to be in there just because the syntax supports it, not because it actually makes sense here.
No, I mention string-to-string conversion because other people might find it interesting even though I personally do not have use for such a deployment. That said, I wish I had that available to me quite some time back when I was dealing with password encryption/decryption.
I guess this is because library is called Convert instead of String Convert or Via Stream Convert, which is its main focus (and rightly so).
Apologies for repeating this: I still don't get why the extra options go into a second function call operator. I found some earlier messages where you simply added those to the first function call. Simplify, simplify, simplify the interface. Please.
Again, I personally do not see one or the other of the below simpler or harder. from(s, _manip = std::hex); from(s)(_manip = std::hex); So I suspect that "simplify, simplify, simplify" cry might have a personal-preference under-tones. ;-) Due to some advantages described before I currently prefer #2 for purely practical/utilitarian reasons.
To summarize, I don't get why we can't just have this simple clear syntax: ... for brevity snipped alternative syntax suggestions.
The reason as I see it is fairly simple. The API grew as different uses/needs needed to be addressed. What you consider to be a simpler syntax might have been considered but rejected as it was not fitting some other usages the library needed to address. Or it might be that I did not manage it to work and together we might get it to work... or I simply missed that possibility. What I am trying to say, is that I personally do not consider 'convert' to be perfect in every respect and even less so complete. I believe I did implement the functionality that I immediately needed. More so, I tried to address concerns/suggestions brought up while the library was being written. So, I feel it might be a good basis from which we could move *forward* improving it -- adding conversions others needed, optimizing existing conversions. The alternative of rejecting it is truly fine by me (less hassle for me) but it will take us where we've been for close to 10 years. Then, someone else will try it all *again*... with I suspect very similar results. Namely, he'll try extending lexical_cast interface just to realize it's too restrictive. Then, he'll propose some other-than-lexical_cast interface just to cop all why-or-why-you-do-not-use-lexical_cast flames. Then, he'll try extending all other-than-his usages/deployments suggested during discussions that would lead to (I suspect) a similar design/implementation which would result in subsequent (much later) criticism that the design/implementation was unnecessarily complex. I am not complaining but I'd really like us to at long last start moving forward. Maybe an alternative might be to delay reviewing this library for a year (?) and let guys try extending lexical_cast as there were numerous suggestions. Then, we'll re-evaluate the situation. Although my suspicion is that in 1-2 years that lexical_cast-extension push will fizzle out once again, new people will come in asking the same questions and we won't move an inch.
... some sensible comments snipped for brevity only Again I think this could all be SO much simpler.
I hear you. I really do. When I just started replacing lexical_cast in *my* work it was as straightforward as aux::string::from() and aux::string::to() (to/from and 'string' names-related arguments aside). That was what I initially suggested after on the list we decided we could not proceed with lexical_cast. The 'convert' is the result. I can only say that every "complication" was added for a reason after discussions on this list. It was not just my fancy. You and/or I might consider a particular reason not legitimate enough. Some might disagree. Difficult choices. Still no effort can fix everything -- no design/implementation can satisfy everyone.
On Apr 26, 2011, at 6:47 PM, Vladimir Batov wrote:
I've come to a firm conclusion that that additional functionality/configurability is *not* implementable within the boundaries of the lexical_cast interface. In particular, the TypeIn and TypeOut types must be discriminated, i.e. one type has to be specialized first. Without going into details I'll just say that otherwise is just does not work. So, it has to be convert<TypeOut>::from<TypeIn>. You can take my word for it or you could walk that road yourself.
Sorry, but I find this response either lazy or rude. You wrote the library: you explain to us why the way it is!
Well, being rude has certainly never been my intention. If I came across as such, then it is quite unfortunate and please accept my humble apologies. The problem in such situations is that it's hard/impossible to figure out how much information the person *actually* wants to know; if the respective question was indeed a genuine interest or a rhetoric question given that the person had already rejected the library as a whole.
I'd really like to know why you think it's necessary to do convert<T>::from(s) rather than the much more familiar and easy-to-grasp string_convert<T>(s).
I am not sure if I managed to explain that above. The reason why TypeIn and TypeOut need to be separated is that if we have specialization for, say, string-to-type enabled with template<class TypeOut, class StringIn> typename boost::enable_if<convert_detail::is_any_string<StringIn>, TypeOut>::type convert_to<TypeOut>(StringIn const&) {...} then, say, string-to-bool further specialization/optimization won't be possible template<class StringIn> bool convert_to<bool>(StringIn const&) {...} It won't compile as both "specializations" will match. It did not work for me. If you can get it to work by some other means, I'll be only happy.
... I realize that my conditions mean some serious redesign, ...
Again, it is not to say I am against a re-design and re-doing the library altogether. However, as I indicated the existing functionality/API are in the lib. for a reason. Taking that functionality out or changing API will affect those use-cases that functionality/API were for. So, I'd approach that re-design with caution. I understand that might change your vote and I am OK with that.
Cheers to Vladimir - you've identified an important problem! Now just give it a clear interface and we'll have something good.
Thank you. Although I suspect I cannot take the credit for identifying the problem -- that's been with us for long-long time. What I was seemingly naive about was my hope that, if we had something, then we at long last could get it moving beyond just talk. I am more than happy to scrap my implementation in favor of some other alternative. For a user (and primarily I am a user) ending up with nothing is not a good outcome. Apologies if I have not addressed all questions/notes/suggestions. I am happy to work with anyone willing to stick with this library (or suggest his own alternative library) long enough to know and to take into consideration different deployment patterns to produce something useful. I am happy to step aside knowing there is someone prepared to take this (or his own) library to fruition. No irony or sarcasm here. I think users need that functionality and I'd really like to see that functionality available in one form or another. Best, V.

Hi Vladimir, all, First off, I'll respond to Rob and Jeremy. Here's again the alternate "simpler" syntax which I put in my original review, which I think must have gotten buried from typing too much. :-/ optional<int> i = string_convert<int, dont_throw>(s); // or string_convert<int>(s, _throw=false) if that's impossible int i = string_convert<int>(s, manip_ = std::hex); Well, Jeremy pointed out
You propose that a bunch of tag_ names be used, but in practice they would likely have to be something like boost::convert::tag_, which would substantially reduce the benefits of any such change.
So, yes, I guess dont_throw or any tag type put into the template parameters, is basically disqualified by its verbosity. That's too bad, because it would be nice to have a single function that returned either optional or a value. I suppose that Vladimir's nice named parameters exhibit the same problem. Yuck. Nonetheless, my objections and simplifications are quite orthogonal. On May 2, 2011, at 10:35 PM, Vladimir Batov wrote:
If the library is accepted (which seem unlikely so far and that's truly OK with me), then I am prepared to discuss and try out alternative approaches (if they are in some concrete "try-able" form).
Yes this is the review deadlock/stalemate problem I referred to earlier. Anyway, I remain in favor of the library with simplified semantics. It only boils down to three things I find distressing. 1. A special context which causes an apparent function call to either throw or not throw. 2. A streaming operation outside an apparent function call which applies IO manipulators within it. 3. "::from" I'll drop #4, the multiple operator(), because that's common practice and it's quite clear what that's doing.
The reason I've "come up" with the API as it is now is because during discussions some 2-3 years ago I in fact tried various proposed interfaces as we were discussing them. The results of those trial-n-errors is this current API.
I apologize that I still haven't reread those. From what I remember, these issues were raised but I don't remember whether there was consensus how to fix them. Despite that this review might return a negative result, I think it's been a valuable discussion.
It is incredibly clear what lexical_cast does because it uses the standard meaning of the operators, function calls, and return values.
int i = lexical_cast<int>(str); int i = convert<int>::from(str);
When I look at the above where lexical_cast and 'convert' provide the same functionality, I am not sure if from the user perspectives I immediately see how 'convert' differs from lexical_cast complexity-wise. I am certainly biased.
It's two more tokens, and I don't see the value to the user of separating the two parameters. I admit it's partly a matter of taste, but I think following the lexical_cast syntax as far as possible has objective value. Since you say it's there for technical reasons, let's take a look at how Vicente fixed it in his Conversion library. I think this is the relevant section of his doc: " How to specialize the conversion functions You can add an overload of convert_to and assign_to functions as you will do for the swap function for example, but we have to use a trick to allow ADL and avoid infinite recursion and ambiguity. This trick consists in adding a unused parameter representing the target type. E.g. if you want to add an explicit conversion from a type A to a type B do the following: namespace my_own_namespace { B convert_to(const A& from, boost::dummy::type_tag<B> const&); } "
I'd try distinguishing "not simple enough" and "annoying". The *user* API is one (!) function -- convert::from -- on one side and *optionally* convert::result on the receiving side. I really do not know how to simplify it any further. I personally do not consider changing that "annoying" from() to something else to be a simplification. So, by "simplification" you probably had something else in mind and I am all prepared to listen.
The complexity I and others are referring to is obviously not the number of functions! It's the fact that the converter<> object returned by from() has at least three different behaviors, when I think most people from looking at it would think conversion has already happened and a simple value is being returned. - It can throw if assigned to a regular value - It doesn't throw if assigned to ::result - It can be a functor which takes a string and returns a value, if the string was omitted in the constructor (?) or if the string was empty (?)
As for "I don't like "::from" at all", then it's a personal choice and we'll never get a consensus here.
True.
The practical reason for "::from" is two-fold -- 1) it clearly shows the direction of the conversion (which's been deemed important during original discussions);
Again, I haven't reread those discussions. I am surprised that people would find a function call confusing. Usually it takes its input as an argument and returns its result. :-p
2) the syntax has to be convert<TypeOut>::from<TypeIn> (replace 'convert' and 'from' with the identifiers of your choice), i.e. two types need to be evaluated separately. Otherwise, various specializations (if there are such) conflict. That's why the lexical_cast-like API (where both TypeIn and TypeOut evaluated at the same time) does not work (at least I did not manage it to work).
Again, see Vicente's library for the solution.
... seem to require this weird static member syntax.
I am not sure I am following. What's exactly weird about it (apart from your personal syntax preferences)? It's the standard C++ syntax, right?
I guess I'd expect something "more" to happen, like a side effect of instantiating the convert<> template, or having some value to instantiating a convert<> object or something. Instead it's really just a function call where some of the template parameters are pushed into a class. At least now I understand why you're doing this, although it appears to be unnecessary.
So, next '::result'. It's some form of optional return. So why not just return optional? It is not much less to type convert<int>::result maybe = convert<int>::from("not an int"); versus optional<int> maybe = convert_cast<int, use_opt>("not an int");
Yes, convert::result does not offer *much* beyond boost::optional. However, it *does* offer more. That's the reason it exists. Namely, convert::result provides two pieces of information -- if the conversion failed/succeeded *and* the fallback value (if provided). boost::optional provides one *or* the other. The difference might be considered subtle but it's there.
So the intention is something like this? convert<int>::result i = convert<int>::from(s, 17); if(!i) return i.value(); // returns 17 (!) That looks pretty weird to me. I don't see why someone would want to specify the default value if they are then going to check if conversion succeeded. If that's really wanted, I think simply pair<bool,T> would be more clear.
Why should the LHS modify the behavior of the proxy object returned by ::from?
Because usages of 'convert' differ -- namely, throwing and non-throwing behavior. I personally do not use throwing behavior at all but I feel it was needed to provide/support the behavior (and resulting mess) for lexical_cast backward compatibility and to cater for usages different from mine.
Personally I would want both behaviors. It's just the context-sensitive value that I object to. Is the following syntax possible? I am not up on Boost.Parameter yet. int i = convert<int>::from(s)(_dothrow); optional<int> j = convert<int>::from(s)(_dontthrow);
It's far too much to think about for such a simple operation.
Not really. Well, I do not see it that way anyway.
convert<int>::result res = convert<int>::from("blah", -1); if (!res) message("conversion failed"); int value = res.value(); // proceed with the fallback anyway.
The locality of 'convert' application (IMHO of course) does not leave much to think how 'convert' is to behave.
Clear enough in itself (although I'm still reeling from the idea of an object which converts to false having a value). It's just that the next line might read int res2 = convert<int>::from("blah"); and throw, and there's no indication why one version throws and the other does not.
The huge *practical* advantage of operator() is chaining. I personally do not see one or the other of the below simpler or harder and both are of equal length:
from(s, _manip = std::hex); from(s)(_manip = std::hex);
However, the first one has the limit on the number of args. Second does not. Second is much easier/cleaner to implement (therefore, to maintain which is even more important) as #1 requires from() overloading or def. parameters. Thirdly, second looks more natural as a progression of increasing complexity as the user gains additional knowledge:
int i = from(s); // simplest int i = from(s)(_manip = std::hex); int i = from(s)(_manip = std::hex)(locale_ = ...);
Yeah, I get this now. Thanks!
On a similar nice-feature-but-weird-syntax note, let's look at the Using Boost.Convert with Standard Algorithms section, which offers a few ways that you can use convert<T>::from() to create a functor:
std::transform( strings.begin(), strings.end(), std::back_inserter(integers), convert<int>::from(string(), -1) >> std::hex); ... I think it would be much more appropriate to provide a Phoenix function object for the purpose, something like std::transform( strings.begin(), strings.end(), std::back_inserter(integers), convert_<int>(_1, -1, std::hex)); Sigh of relief. I see what that means immediately, ...
Apologies for sounding as a broken record but again that preference seems quite personal. As a user not familiar with Phoenix your variant would not look familiar to me. In order to use it I'd need to learn/remember/know additional API -- convert_, _1, args order. My deployment seems simpler (not surprisingly some bias here :-) ) as the user uses the same API and does not need to learn more to use 'convert' with algorithms.
Mentioning that not for the sake of arguing tooth and nail for *my* API. I am happy to consider alternatives. I just do not see your suggested API as a clear winner (not yet anyway).
I think Boost.Lambda/Boost.Phoenix is pretty well-known syntax by now, but perhaps I've been indoctrinated. I guess your syntax is a bit like currying - drop an argument and it becomes a function object which takes that argument. Don't think I've seen that in C++ before.
I also want to mention type-to-type conversion, which Vicente Botet has thought out much more thoroughly in his Conversion library. This library offers type-to-type conversion seemingly as an afterthought, just because the syntax allows it.
I have to disagree. Again I consider 'convert' to be more of a conversion framework rather than a library. Indeed, 'convert' does not provide much more beyond string-to-type conversions because I personally did/do not need more. However, it does not mean I/we should deny that possibility to others as their needs may differ. Therefore, 'convert' was designed with that in mind. If Vicente needs type-to-type conversion functionality, I think he might consider implementing that functionality within the 'convert' framework by extending the 'convert' library for everyone's benefit.
Leaving aside which is a more general framework... I believe yours falls back to type-to-string-to-type whereas his does not. Both are valid behaviors.
I do not think just one person can provide every possible conversion. I cannot anyway. And with 'convert' I am not saying I do.
Extensibility is essential.
I believe I did implement the functionality that I immediately needed. More so, I tried to address concerns/suggestions brought up while the library was being written. So, I feel it might be a good basis from which we could move *forward* improving it -- adding conversions others needed, optimizing existing conversions.
I do think it's a good start.
Maybe an alternative might be to delay reviewing this library for a year (?) and let guys try extending lexical_cast as there were numerous suggestions. Then, we'll re-evaluate the situation. Although my suspicion is that in 1-2 years that lexical_cast-extension push will fizzle out once again, new people will come in asking the same questions and we won't move an inch.
I think I made suggestions which answer almost all of the concerns raised in this review, except for the insistence on lexical_cast. The functionality is clearly there. It's just this odd use of Expression Templates where IMO it's not needed, that irks me. For what? To specify a context where a function won't throw. To apply io manipulators using a familiar operator. As a shortcut for lambdas. Thanks for explaining all of this - I feel a lot better knowing why the library is how it is, especially that peculiar ::from. I still disagree and hope you'll consider the "simplifications" I've suggested. (I doubt they are original in any way!) If not, there'll probably be another lexical_cast debate soon enough. Cheers, Gordon

Gordon Woodhull wrote:
Here's again the alternate "simpler" syntax which I put in my original review, which I think must have gotten buried from typing too much. :-/
optional<int> i = string_convert<int, dont_throw>(s); // or string_convert<int>(s, _throw=false) if that's impossible int i = string_convert<int>(s, manip_ = std::hex);
That's only a portion of the functionality of the library under review.
It is incredibly clear what lexical_cast does because it uses the standard meaning of the operators, function calls, and return values.
int i = lexical_cast<int>(str); int i = convert<int>::from(str);
When I look at the above where lexical_cast and 'convert' provide the same functionality, I am not sure if from the user perspectives I immediately see how 'convert' differs from lexical_cast complexity-wise. I am certainly biased.
It's two more tokens, and I don't see the value to the user of separating the two parameters. I admit it's partly a matter of taste, but I think following the lexical_cast syntax as far as possible has objective value.
Using the new-style cast interface is certainly good. some_cast<int>(str) can actually call convert<int>::from(str), so the other aspects of the library can be retained (in whatever form may settle out of this review) without harm. Finding the right prefix for "_cast" will be a challenge.
Since you say it's there for technical reasons, let's take a look at how Vicente fixed it in his Conversion library. I think this is the relevant section of his doc:
" How to specialize the conversion functions You can add an overload of convert_to and assign_to functions as you will do for the swap function for example, but we have to use a trick to allow ADL and avoid infinite recursion and ambiguity. This trick consists in adding a unused parameter representing the target type. E.g. if you want to add an explicit conversion from a type A to a type B do the following: namespace my_own_namespace { B convert_to(const A& from, boost::dummy::type_tag<B> const&); } "
Don't forget the other two use cases the library supports: no exception on failure (for types with and without sentinel values) and function objects.
The practical reason for "::from" is two-fold -- 1) it clearly shows the direction of the conversion (which's been deemed important during original discussions);
Again, I haven't reread those discussions. I am surprised that people would find a function call confusing. Usually it takes its input as an argument and returns its result. :-p
One case that occurs to me is because of the need to specify both target and source types in some cases: int const i(convert<int,Foo>("example")); Obviously, that can be written like this instead: int const i(convert<int>(Foo("example"))); However, int a generic context, that may not be appropriate. What if Foo is noncopyable? Is convert<>()'s argument passed by value or reference? Maybe I'm just trying to create a problem, but there was certainly some context that led to that concern.
So the intention is something like this?
convert<int>::result i = convert<int>::from(s, 17); if(!i) return i.value(); // returns 17 (!)
That looks pretty weird to me. I don't see why someone would want to specify the default value if they are then going to check if conversion succeeded.
Did you read the documentation? There's an example for this use case. The issue arises when a type has no appropriate sentinel value to indicate the failure and is noncopyable. In that case, one must construct an instance with a real initial state. That instance can be passed as the fallback value (17, above). Getting the fallback value from the result object isn't proof that the conversion failed in that case. Instead, one must query the result object and, if the conversion succeeded, retrieve the value.
If that's really wanted, I think simply pair<bool,T> would be more clear.
That imposes Copyable on T.
Is the following syntax possible? I am not up on Boost.Parameter yet.
int i = convert<int>::from(s)(_dothrow); optional<int> j = convert<int>::from(s)(_dontthrow);
Aren't you asking for the very thing you've been against? convert<int>::from() returns two different things depending upon context, right? I'd also argue against requiring _dothrow and _dontthrow. One should be the default.
convert<int>::result res = convert<int>::from("blah", -1); if (!res) message("conversion failed"); int value = res.value(); // proceed with the fallback anyway.
The locality of 'convert' application (IMHO of course) does not leave much to think how 'convert' is to behave.
Clear enough in itself (although I'm still reeling from the idea of an object which converts to false having a value).
For non-DefaultConstructible types.
It's just that the next line might read
int res2 = convert<int>::from("blah");
and throw, and there's no indication why one version throws and the other does not.
Of course there is. One was given a fallback value and the other wasn't. _____ Rob Stewart robert.stewart@sig.com Software Engineer using std::disclaimer; Dev Tools & Components Susquehanna International Group, LLP http://www.sig.com IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

Stewart, Robert wrote:
Gordon Woodhull wrote:
Here's again the alternate "simpler" syntax which I put in my original review, which I think must have gotten buried from typing too much. :-/
optional<int> i = string_convert<int, dont_throw>(s); // or string_convert<int>(s, _throw=false) if that's impossible int i = string_convert<int>(s, manip_ = std::hex);
That's only a portion of the functionality of the library under review.
It is incredibly clear what lexical_cast does because it uses the standard meaning of the operators, function calls, and return values. int i = lexical_cast<int>(str); int i = convert<int>::from(str);
When I look at the above where lexical_cast and 'convert' provide the same functionality, I am not sure if from the user perspectives I immediately see how 'convert' differs from lexical_cast complexity-wise. I am certainly biased. It's two more tokens, and I don't see the value to the user of separating the two parameters. I admit it's partly a matter of taste, but I think following the lexical_cast syntax as far as possible has objective value.
Using the new-style cast interface is certainly good. some_cast<int>(str) can actually call convert<int>::from(str), so the other aspects of the library can be retained (in whatever form may settle out of this review) without harm. Finding the right prefix for "_cast" will be a challenge.
Since you say it's there for technical reasons, let's take a look at how Vicente fixed it in his Conversion library. I think this is the relevant section of his doc:
" How to specialize the conversion functions You can add an overload of convert_to and assign_to functions as you will do for the swap function for example, but we have to use a trick to allow ADL and avoid infinite recursion and ambiguity. This trick consists in adding a unused parameter representing the target type. E.g. if you want to add an explicit conversion from a type A to a type B do the following: namespace my_own_namespace { B convert_to(const A& from, boost::dummy::type_tag<B> const&); } "
Don't forget the other two use cases the library supports: no exception on failure (for types with and without sentinel values) and function objects.
The practical reason for "::from" is two-fold -- 1) it clearly shows the direction of the conversion (which's been deemed important during original discussions); Again, I haven't reread those discussions. I am surprised that people would find a function call confusing. Usually it takes its input as an argument and returns its result. :-p
One case that occurs to me is because of the need to specify both target and source types in some cases:
int const i(convert<int,Foo>("example"));
Obviously, that can be written like this instead:
int const i(convert<int>(Foo("example")));
However, int a generic context, that may not be appropriate. What if Foo is noncopyable? Is convert<>()'s argument passed by value or reference?
Maybe I'm just trying to create a problem, but there was certainly some context that led to that concern.
So the intention is something like this?
convert<int>::result i = convert<int>::from(s, 17); if(!i) return i.value(); // returns 17 (!)
That looks pretty weird to me. I don't see why someone would want to specify the default value if they are then going to check if conversion succeeded.
Did you read the documentation? There's an example for this use case. The issue arises when a type has no appropriate sentinel value to indicate the failure and is noncopyable. In that case, one must construct an instance with a real initial state. That instance can be passed as the fallback value (17, above). Getting the fallback value from the result object isn't proof that the conversion failed in that case. Instead, one must query the result object and, if the conversion succeeded, retrieve the value.
If that's really wanted, I think simply pair<bool,T> would be more clear.
That imposes Copyable on T.
Is the following syntax possible? I am not up on Boost.Parameter yet.
int i = convert<int>::from(s)(_dothrow); optional<int> j = convert<int>::from(s)(_dontthrow);
Aren't you asking for the very thing you've been against? convert<int>::from() returns two different things depending upon context, right? I'd also argue against requiring _dothrow and _dontthrow. One should be the default.
convert<int>::result res = convert<int>::from("blah", -1); if (!res) message("conversion failed"); int value = res.value(); // proceed with the fallback anyway.
The locality of 'convert' application (IMHO of course) does not leave much to think how 'convert' is to behave. Clear enough in itself (although I'm still reeling from the idea of an object which converts to false having a value).
For non-DefaultConstructible types.
It's just that the next line might read
int res2 = convert<int>::from("blah");
and throw, and there's no indication why one version throws and the other does not.
Of course there is. One was given a fallback value and the other wasn't.
dynamic_cast sets a precedent for how to handle throwing/non-throw variations: dynamic_cast<X&>(y); // throws if cast fails X* x = dynamic_cast<X*>(y); // returns 0 So if boost::optional is the analog of pointer, and we go to some xxx_cast naming convention we could have: // throws if fails int i = xxx_cast<int>(s); // returns empty optional if cast fails boost::optional<int> oi = xxx_cast<boost::optional<int>>(s); // never fails int i = xxx_cast<int>(s, 123); This just reuses boost::optional rather than the (superfluous to me) convert<T>::result type. I for one see no reason that _cast need be limited to a single argument. If others find multiple arguments too disgusting, perhaps boost::make<int>(s, 123) is more palatable. Jeff

Jeff Flinn wrote:
Stewart, Robert wrote:
Gordon Woodhull wrote:
[snip massively overquoted context]
convert<int>::result res = convert<int>::from("blah", -1); if (!res) message("conversion failed"); int value = res.value(); // proceed with the fallback anyway.
It's just that the next line might read
int res2 = convert<int>::from("blah");
and throw, and there's no indication why one version throws and the other does not.
Of course there is. One was given a fallback value and the other wasn't.
dynamic_cast sets a precedent for how to handle throwing/non- throw variations:
dynamic_cast<X&>(y); // throws if cast fails
X* x = dynamic_cast<X*>(y); // returns 0
So if boost::optional is the analog of pointer, and we go to some xxx_cast naming convention we could have:
// throws if fails int i = xxx_cast<int>(s);
// returns empty optional if cast fails boost::optional<int> oi = xxx_cast<boost::optional<int>>(s);
// never fails int i = xxx_cast<int>(s, 123);
This just reuses boost::optional rather than the (superfluous to me) convert<T>::result type. I for one see no reason that _cast need be limited to a single argument.
I really dislike the repetition of "boost::optional<int>" in that. What about going a little farther in the dynamic_cast direction (actually, following the boost::get precedent): int const i(xxx_cast<int>(s)); // throws on failure boost::optional<int> const oi(xxx_cast<int>(&s)); Whereas dynamic_cast requires casting from a pointer type to a pointer type, here the suggestion is that casting from a pointer type implies casting to an optional. Note that the cast is "xxx_cast<int>" in both cases; it’s the source argument's being a pointer or not that dictates the overload and, thus, the return type.
If others find multiple arguments too disgusting, perhaps boost::make<int>(s, 123) is more palatable.
That name could work, but does it allow for formatting? IOW, "convert," like "translate," leaves room to consider formatting, including locales. "Make" suggests something more fixed to me. _____ Rob Stewart robert.stewart@sig.com Software Engineer using std::disclaimer; Dev Tools & Components Susquehanna International Group, LLP http://www.sig.com IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

Stewart, Robert wrote:
Jeff Flinn wrote:
Stewart, Robert wrote:
Gordon Woodhull wrote:
[snip massively overquoted context]
sorry thought I snipped that stuff.
convert<int>::result res = convert<int>::from("blah", -1); if (!res) message("conversion failed"); int value = res.value(); // proceed with the fallback anyway. It's just that the next line might read
int res2 = convert<int>::from("blah");
and throw, and there's no indication why one version throws and the other does not. Of course there is. One was given a fallback value and the other wasn't. dynamic_cast sets a precedent for how to handle throwing/non- throw variations:
dynamic_cast<X&>(y); // throws if cast fails
X* x = dynamic_cast<X*>(y); // returns 0
So if boost::optional is the analog of pointer, and we go to some xxx_cast naming convention we could have:
// throws if fails int i = xxx_cast<int>(s);
// returns empty optional if cast fails boost::optional<int> oi = xxx_cast<boost::optional<int>>(s);
// never fails int i = xxx_cast<int>(s, 123);
This just reuses boost::optional rather than the (superfluous to me) convert<T>::result type. I for one see no reason that _cast need be limited to a single argument.
I really dislike the repetition of "boost::optional<int>" in that. What about going a little farther in the dynamic_cast direction (actually, following the boost::get precedent):
int const i(xxx_cast<int>(s)); // throws on failure boost::optional<int> const oi(xxx_cast<int>(&s));
My gut reaction is that it lacks symmetry. As mentioned else-thread, doesn't C++ 0x's auto solve the repetition entirely, while being explicit as to what's going on? auto oi(xxx_cast<boost::optional<int>>(s));
Whereas dynamic_cast requires casting from a pointer type to a pointer type, here the suggestion is that casting from a pointer type implies casting to an optional. Note that the cast is "xxx_cast<int>" in both cases; it’s the source argument's being a pointer or not that dictates the overload and, thus, the return type.
If others find multiple arguments too disgusting, perhaps boost::make<int>(s, 123) is more palatable.
That name could work, but does it allow for formatting? IOW, "convert," like "translate," leaves room to consider formatting, including locales. "Make" suggests something more fixed to me.
I personally don't have those reservations and the applicability of Make. Even more concise with perhaps less preconceptions might be: as<int>(s, 123); Jeff

Jeff Flinn wrote:
Stewart, Robert wrote:
Jeff Flinn wrote:
Stewart, Robert wrote:
Gordon Woodhull wrote:
// returns empty optional if cast fails boost::optional<int> oi = xxx_cast<boost::optional<int>>(s);
I really dislike the repetition of "boost::optional<int>" in that. What about going a little farther in the dynamic_cast direction (actually, following the boost::get precedent):
int const i(xxx_cast<int>(s)); // throws on failure boost::optional<int> const oi(xxx_cast<int>(&s));
My gut reaction is that it lacks symmetry.
OK, but it does follow a precedent.
As mentioned else-thread, doesn't C++ 0x's auto solve the repetition entirely, while being explicit as to what's going on?
auto oi(xxx_cast<boost::optional<int>>(s));
Yes, though that doesn't help with C++03 code.
That name could work, but does it allow for formatting? IOW, "convert," like "translate," leaves room to consider formatting, including locales. "Make" suggests something more fixed to me.
I personally don't have those reservations and the applicability of Make. Even more concise with perhaps less preconceptions might be:
as<int>(s, 123);
Not bad. _____ Rob Stewart robert.stewart@sig.com Software Engineer using std::disclaimer; Dev Tools & Components Susquehanna International Group, LLP http://www.sig.com IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

On 05/03/2011 11:41 AM, Jeff Flinn wrote:
I personally don't have those reservations and the applicability of Make. Even more concise with perhaps less preconceptions might be:
as<int>(s, 123);
Just a data point: I've been using 'obj.as<U>' as my own convention for a few years now and I like it. It was a little easier on a "lenient" compiler where I started using it: struct mytype { // Unimplemented general template. template <class U> U as() const { BOOST_MPL_ASSERT_MSG(false, NO_CONVERSION_TO, (U)); } // specializations define the types we support "conversions" to: template <> std::string as() const {return this->to_string_imp(); } template <> std::wstring as() const {return this->to_wstring_imp();} ...possibly more... ... }; template <class T> T f() { mytype m(...); return m.as<T>(); } Just tried it again the other day with a class template on a more conforming compiler (which required member specializations at namespace scope). It was a lot more overhead code to make it work. If this library could help me define explicit conversions "as" member templates with a sane amount of boilerplate again, I'd be a fan. - Marsh

Message du 03/05/11 20:53 De : "Marsh Ray" A : boost@lists.boost.org Copie à : "Jeff Flinn" Objet : Re: [boost] [review] string convert
On 05/03/2011 11:41 AM, Jeff Flinn wrote:
I personally don't have those reservations and the applicability of Make. Even more concise with perhaps less preconceptions might be:
as(s, 123);
Just a data point: I've been using 'obj.as' as my own convention for a few years now and I like it.
It was a little easier on a "lenient" compiler where I started using it:
struct mytype { // Unimplemented general template. template U as() const { BOOST_MPL_ASSERT_MSG(false, NO_CONVERSION_TO, (U)); }
// specializations define the types we support "conversions" to: template <> std::string as() const {return this->to_string_imp(); } template <> std::wstring as() const {return this->to_wstring_imp();} ...possibly more... ... };
template T f() { mytype m(...); return m.as(); }
Just tried it again the other day with a class template on a more conforming compiler (which required member specializations at namespace scope). It was a lot more overhead code to make it work.
If this library could help me define explicit conversions "as" member templates with a sane amount of boilerplate again, I'd be a fan.
explicit conversion are supported by C++11. Boost.Conversion let you already define explicit conversion but not as member templates. You will need to overload the convert_to function for the Source and target types as follows: Target convert_to(const Source& from, boost::dummy::type_tag const&) { // your specific code } Once this is done you could write your function f() as ***** Sorry I have replaced angle brackets by (* and *) ********** template (*class T*) T f() { mytype m(...); return boost::convert_to(*T*)(m); } Best, Vicente

Hi Robert, Ok, let me try again with what I've learned in the past 31 hours. Thanks for challenging me to make specific proposals! I think I was emptily complaining a bit there. Here's another revision of my idea of a good interface: Vladimir's interface squished. On May 3, 2011, at 8:11 AM, Stewart, Robert wrote:
Gordon Woodhull wrote:
Here's again the alternate "simpler" syntax which I put in my original review, which I think must have gotten buried from typing too much. :-/
optional<int> i = string_convert<int, dont_throw>(s); // or string_convert<int>(s, _throw=false) if that's impossible int i = string_convert<int>(s, manip_ = std::hex);
That's only a portion of the functionality of the library under review.
So, here's something complete (?) for people to rip apart: int i = convert_cast<int>(s); // default behavior: might throw optional<int> i = convert_cast<int>(s, use_optional_instead_of_throwing_); int i = convert_cast<int>(s, 17); // won't throw; can't tell if conversion successful int i = convert_cast<int>(s, 17, throw_even_though_i_specified_a_failback_); optional<int> i = convert_cast<int>(s, 17, use_optional_instead_of_throwing_); pair<bool,int> i = convert_cast<int>(s, 17, fallback_and_report_success_); I am not concerned with names, just with syntax. So I'm using "convert_cast" although I actually don't care what it's called, and I have some very ugly very descriptive tag values with distinct types to trigger different overloads, which might be abbreviated dont_throw_, do_throw_, and, um, tell_fallback_ or something. All would also accept extra manip_/format_= and locale_ arguments as in Vladimir's proposal, except they have to be appended to the first parameter list because of the "no proxies" requirement. Doesn't this now cover all the functionality we've been talking about? The 5th and 6th forms cover nondefaultable types / types with no sentinel value, and Vladimir's "print a warning if failing back" use-case. You mention noncopyable types but I'm not able to find any reference to that in the documentation or in previous discussion. I don't see how the value can be non-copyable since it's being returned by the function. I'd actually prefer the behavior flags were put in the template parameters, because they affect the return type, but that would be tricky because of the defaulted Destination type. (I don't think it's impossible, but it might be messy.) Maybe just having more functions would be simpler; I'm just taking an idea to its conclusion. Since there are now a bunch of overloads with different behavior, and no converter<> object, I figure these would dispatch to a customization point function that looks a lot like Vicente's, but probably with a default an extra bool parameter indicating whether to throw.
Finding the right prefix for "_cast" will be a challenge.
Sigh, another naming debate. (Naming is important but so difficult!) It does seem like the direction that *_cast<>() is clearer to people than convert<>() I also like as<>() :-) but somehow I don't think that'll generate consensus.
So the intention is something like this?
convert<int>::result i = convert<int>::from(s, 17); if(!i) return i.value(); // returns 17 (!)
That looks pretty weird to me. I don't see why someone would want to specify the default value if they are then going to check if conversion succeeded.
Did you read the documentation? There's an example for this use case. The issue arises when a type has no appropriate sentinel value to indicate the failure and is noncopyable. In that case, one must construct an instance with a real initial state. That instance can be passed as the fallback value (17, above). Getting the fallback value from the result object isn't proof that the conversion failed in that case. Instead, one must query the result object and, if the conversion succeeded, retrieve the value.
Right, I should have reread. I got it now, thanks. I think I've addressed this use-case now. I'm still not sure what you mean by noncopyable and hope that you mean nondefaultable.
If that's really wanted, I think simply pair<bool,T> would be more clear.
That imposes Copyable on T.
Again, I'm confused by this requirement for support of noncopyable types.
Is the following syntax possible? I am not up on Boost.Parameter yet.
int i = convert<int>::from(s)(_dothrow); optional<int> j = convert<int>::from(s)(_dontthrow);
Aren't you asking for the very thing you've been against? convert<int>::from() returns two different things depending upon context, right? I'd also argue against requiring _dothrow and _dontthrow. One should be the default.
No, it's not about the context, it depends just on the parameters. Different overloads. But as people have convincingly argued, the whole proxy object has to go. So those argument lists get combined, and there would have to be some tricky overloading. I think Boost.Parameter can handle this, if I understand it correctly. It might still be pretty messy, but the customization point would not have to be.
It's just that the next line might read
int res2 = convert<int>::from("blah");
and throw, and there's no indication why one version throws and the other does not.
Of course there is. One was given a fallback value and the other wasn't.
Okay my example should have read convert<int>::result i = convert<int>::from(s); // doesn't throw int i = convert<int>::from(s); // could throw Changing behavior based on arguments is okay; depending on context is too EDSL and not compatible with the many, many places this function will get used. Note: I don't think I'm volunteering to write convert_cast, just putting something out for people to tear apart. In fact, I should really be working on my BoostCon talk. :-D Cheers, Gordon

Hi Gordon,
So, here's something complete (?) for people to rip apart:
int i = convert_cast<int>(s); // default behavior: might throw optional<int> i = convert_cast<int>(s, use_optional_instead_of_throwing_); int i = convert_cast<int>(s, 17); // won't throw; can't tell if conversion successful int i = convert_cast<int>(s, 17, throw_even_though_i_specified_a_failback_); optional<int> i = convert_cast<int>(s, 17, use_optional_instead_of_throwing_); pair<bool,int> i = convert_cast<int>(s, 17, fallback_and_report_success_);
I am not concerned with names, just with syntax. So I'm using "convert_cast" although I actually don't care what it's called, and I have some very ugly very descriptive tag values with distinct types to trigger different overloads, which might be abbreviated dont_throw_, do_throw_, and, um, tell_fallback_ or something.
All would also accept extra manip_/format_= and locale_ arguments as in Vladimir's proposal, except they have to be appended to the first parameter list because of the "no proxies" requirement.
Doesn't this now cover all the functionality we've been talking about?
Since there are now a bunch of overloads with different behavior, and no converter<> object, I figure these would dispatch to a customization point function that looks a lot like Vicente's, but probably with a default an extra bool parameter indicating whether to throw.
Sounds good. What about: int i = convert_cast<int>(s); // agreed optional<int> i = convert_cast<optional<int> >(s); // so optional indicated in template parameter as return type - as always int i = convert_cast<int>(s, 17); // agreed int i = convert_cast<int, throw_even_though_i_specified_a_failback_>(s, 17); // template parameter as optional one there optional<int> i ... (similar as other one with optional) pair<bool, int> i = convert_cast<pair<bool, int> >(s, 17); // pair indicated in template parameter This would never need any runtime parameter -> dispatching possible on compile-time -> no if's or cases necessary in implementation. And it always returns the type is in the (first) template parameter of convert. Regards, Barend

Hi Barend, On May 4, 2011, at 9:46 AM, Barend Gehrels wrote:
What about:
int i = convert_cast<int>(s); // agreed optional<int> i = convert_cast<optional<int> >(s); // so optional indicated in template parameter as return type - as always int i = convert_cast<int>(s, 17); // agreed int i = convert_cast<int, throw_even_though_i_specified_a_failback_>(s, 17); // template parameter as optional one there optional<int> i ... (similar as other one with optional) pair<bool, int> i = convert_cast<pair<bool, int> >(s, 17); // pair indicated in template parameter
This would never need any runtime parameter -> dispatching possible on compile-time -> no if's or cases necessary in implementation. And it always returns the type is in the (first) template parameter of convert.
Yeah, I like that better too. I just wasn't sure exactly how to implement that, since there's a template parameter OutType which is deduced from the argument. But I bet it's possible. The last one points out another big difference between string-to-type and type-to-type: lexical_cast explicitly does not deal with complex types; otherwise that could be ambiguous. (Likewise someone could conceivably want to come up with a string representation of optional<T>.) Either way, it's a job for someone with decent knowledge of Boost.Parameter (not me yet). Cheers! Gordon

Barend Gehrels wrote: [reformatted for readability, especially due to line wrapping]
int i = convert_cast<int>(s);
// optional indicated in template parameter as return // type - as always optional<int> i = convert_cast<optional<int> >(s);
int i = convert_cast<int>(s, 17);
// template parameter as optional one there int i = convert_cast < int , throw_even_though_i_specified_a_failback_
(s, 17);
optional<int> i ... (similar as other one with optional)
I presume you mean the following, but why? optional<int> i = convert_cast < optional<int> , throw_even_though_i_specified_a_failback_
(s, 17);
// pair indicated in template parameter pair<bool, int> i = convert_cast<pair<bool, int> >(s, 17);
convert_cast<int>(s) and convert_cast<int>(s, 17) are fine. convert_cast<optional<int>>(s) is too verbose. As has been noted, a "try" variant would be much nicer: try_convert_cast<int>(s) would return an optional<int>. Not only does that play nicely in C++03, but it's even nicer with auto. I realize this loses the nice symmetry you were trying to achieve by using a single name for everything. convert_cast<int, throw_>(s, 17) works pretty well (maybe "throw_on_failure"). The only other one I haven't commented on is the pair<bool,int> version. It shouldn't require a second argument. All of the variations that take a single argument should have a version that takes a second in case there's no default construction or zero-initialization possible. That aside, the pair version leads to much less readable code, not least because the semantics of pair<int,bool> are less obvious than those of optional<int>. Compare this: auto r(convert_cast<optional<int>>(s)); if (r) { i = r.get(); } versus this: auto r(convert_cast<pair<int,bool>>(s)); if (r.second) { i = r.first; } Even better: auto r(try_convert_cast<int>(s)); if (r) { i = r.get(); } Notice also that the precedent, std::map::insert()'s return type, puts bool second, not first. I never remember the order and you used the reverse. That leaves too much room for confusion. The optional version is much better. _____ Rob Stewart robert.stewart@sig.com Software Engineer using std::disclaimer; Dev Tools & Components Susquehanna International Group, LLP http://www.sig.com IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

On May 4, 2011, at 10:33 AM, Stewart, Robert wrote:
The only other one I haven't commented on is the pair<bool,int> version. It shouldn't require a second argument. All of the variations that take a single argument should have a version that takes a second in case there's no default construction or zero-initialization possible. That aside, the pair version leads to much less readable code, not least because the semantics of pair<int,bool> are less obvious than those of optional<int>.
We're going in circles because it's hard to keep all the use-cases in one's head at one time. Something is needed here, when you want to use the fallback but you also want to know if conversion succeeded. See that snippet from Vladimir that I just quoted again in my reply to Matt.
Compare this:
auto r(convert_cast<optional<int>>(s)); if (r) { i = r.get(); }
versus this:
auto r(convert_cast<pair<int,bool>>(s)); if (r.second) { i = r.first; }
Even better:
auto r(try_convert_cast<int>(s)); if (r) { i = r.get() ; }
Agree.
Notice also that the precedent, std::map::insert()'s return type, puts bool second, not first. I never remember the order and you used the reverse. That leaves too much room for confusion.
Yep.
The optional version is much better.
But not sufficient. I think I'm done. I hope Ed or someone feels like making some sense out of all this. :-D Cheers, Gordon

Gordon Woodhull wrote:
On May 4, 2011, at 10:33 AM, Stewart, Robert wrote:
The only other one I haven't commented on is the pair<bool,int> version. It shouldn't require a second argument. All of the variations that take a single argument should have a version that takes a second in case there's no default construction or zero-initialization possible. That aside, the pair version leads to much less readable code, not least because the semantics of pair<int,bool> are less obvious than those of optional<int>.
We're going in circles because it's hard to keep all the use-cases in one's head at one time.
Something is needed here, when you want to use the fallback but you also want to know if conversion succeeded. See that snippet from Vladimir that I just quoted again in my reply to Matt.
I'm having a hard time envisioning why you would ever care if conversion failed if you are specifying a default. Is that in the snippet you refer to? Can you show it in this context to avoid confusion? In any case you would then use the version returning optional and the client code could then do what it needs to do. Jeff

Hi Jeff! On May 4, 2011, at 11:00 AM, Jeff Flinn wrote:
Gordon Woodhull wrote:
On May 4, 2011, at 10:33 AM, Stewart, Robert wrote:
The only other one I haven't commented on is the pair<bool,int> version. It shouldn't require a second argument. All of the variations that take a single argument should have a version that takes a second in case there's no default construction or zero-initialization possible. That aside, the pair version leads to much less readable code, not least because the semantics of pair<int,bool> are less obvious than those of optional<int>. We're going in circles because it's hard to keep all the use-cases in one's head at one time. Something is needed here, when you want to use the fallback but you also want to know if conversion succeeded. See that snippet from Vladimir that I just quoted again in my reply to Matt.
I'm having a hard time envisioning why you would ever care if conversion failed if you are specifying a default. Is that in the snippet you refer to? Can you show it in this context to avoid confusion?
Two use cases: 1. nondefaultable, no sentinel. So you're supplying a default value which is not a fallback. Return should be optional<T>. 2. On May 3, 2011, at 8:36 AM, Vladimir Batov wrote:
For better or worse my usual processing flow of configuration parameters:
// Try reading a configuration parameter. convert<int>::result i = convert<int>::from(s, 17); // Log error if parameter was bad. if(!i) message ("parameter ignored. default used"); // Proceed with the default. return i.value(); // returns 17 (!)
I argue this is a pair. Or call it something else which is still a pair. ;-)
In any case you would then use the version returning optional and the client code could then do what it needs to do.
Yes, that's what I said, but Rob shot that down with the nondefaultable stuff. (And apparently forgot, understandably.) And I can see the value of Vladimir's case, where he wants to supply the fallback along with the conversion, and then see if the fallback got used. Getting loopy, Gordon

On May 4, 2011, at 11:39 AM, Gordon Woodhull wrote:
In any case you would then use the version returning optional and the client code could then do what it needs to do.
Yes, that's what I said, but Rob shot that down with the nondefaultable stuff. (And apparently forgot, understandably.)
Correction. Nondefaultable always leads to optional. Rob did not forget his own argument. I think it's just Vladimir's case of "I want to supply a fallback but I also want to know if conversion succeeded" that requires something akin to a pair.

Jeff Flinn wrote:
Gordon Woodhull wrote:
Something is needed here, when you want to use the fallback but you also want to know if conversion succeeded. See that snippet from Vladimir that I just quoted again in my reply to Matt.
I'm having a hard time envisioning why you would ever care if conversion failed if you are specifying a default. Is that in the snippet you refer to? Can you show it in this context to avoid confusion?
Just think about using non-DefaultConstructible UDTs as the source type. How would the convert_cast create a variable to hold the conversion result it's to return if the conversion succeeds? That's why Vicente suggested a default_value customization point previously. _____ Rob Stewart robert.stewart@sig.com Software Engineer using std::disclaimer; Dev Tools & Components Susquehanna International Group, LLP http://www.sig.com IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

Hi Robert, On 4-5-2011 16:33, Stewart, Robert wrote:
Barend Gehrels wrote:
[reformatted for readability, especially due to line wrapping]
int i = convert_cast<int>(s);
// optional indicated in template parameter as return // type - as always optional<int> i = convert_cast<optional<int> >(s);
int i = convert_cast<int>(s, 17);
// template parameter as optional one there int i = convert_cast < int , throw_even_though_i_specified_a_failback_ >(s, 17);
optional<int> i ... (similar as other one with optional) I presume you mean the following, but why?
optional<int> i = convert_cast < optional<int> , throw_even_though_i_specified_a_failback_ >(s, 17);
// pair indicated in template parameter pair<bool, int> i = convert_cast<pair<bool, int> >(s, 17); convert_cast<int>(s) and convert_cast<int>(s, 17) are fine.
convert_cast<optional<int>>(s) is too verbose. As has been noted, a "try" variant would be much nicer: try_convert_cast<int>(s) would return an optional<int>. Not only does that play nicely in C++03, but it's even nicer with auto. I realize this loses the nice symmetry you were trying to achieve by using a single name for everything. OK for me. I don't understand the advantage for auto here (as they return the same thing). But a TryParse equivalent sounds good to me. It
Good question - I included it because it was in the original list of Gordon, but I actually also wonder what is the specific usage of this function. Gordon? tries and returns a null (optional) if failing.
convert_cast<int, throw_>(s, 17) works pretty well (maybe "throw_on_failure").
The only other one I haven't commented on is the pair<bool,int> version. It shouldn't require a second argument.
You mean the pair<bool,...> is redudant because there is also the version with optional. I agree.
All of the variations that take a single argument should have a version that takes a second in case there's no default construction or zero-initialization possible. That aside, the pair version leads to much less readable code, not least because the semantics of pair<int,bool> are less obvious than those of optional<int>. Compare this:
auto r(convert_cast<optional<int>>(s)); if (r) { i = r.get(); }
versus this:
auto r(convert_cast<pair<int,bool>>(s)); if (r.second) { i = r.first; }
Even better:
auto r(try_convert_cast<int>(s)); if (r) { i = r.get(); }
Notice also that the precedent, std::map::insert()'s return type, puts bool second, not first. I never remember the order and you used the reverse. That leaves too much room for confusion. The optional version is much better.
I agree completely. And indeed this usage with auto is convenient. One thing, users using it like this will not notice anymore that "auto" is (behind the screens now) an "optional", and might be surprised by the .get() call. Regards, Barend

Message du 04/05/11 17:05 De : "Barend Gehrels" A : boost@lists.boost.org Copie à : Objet : Re: [boost] [review] string convert
Hi Robert,
On 4-5-2011 16:33, Stewart, Robert wrote:
Barend Gehrels wrote:
int i = convert_cast(s, 17);
// template parameter as optional one there int i = convert_cast < int , throw_even_though_i_specified_a_failback_
(s, 17);
optional i ... (similar as other one with optional) I presume you mean the following, but why?
optional i = convert_cast < optional , throw_even_though_i_specified_a_failback_
(s, 17);
Good question - I included it because it was in the original list of Gordon, but I actually also wonder what is the specific usage of this function.
Gordon?
convert_cast(s, 17) works pretty well (maybe "throw_on_failure").
I think we need to clarify one thing. Vladimir library uses values for two purposes: * as a default value when the type is not default constructible * as a fail-back in case of the conversion fails And I think we should mix them. To cover the first case we can use as I said in another post a default_value metafunction that can be specialized for the non default constructible type. I agree that when a fail-back is given the user is not interested in knowing if the conversion succeeded or not, so in this case the return value should be T and not optional T. The question now is what function should be used, convert_cast or try_convert_cast. As the function doesn't throw I will use try_convert_cast, but as the function returns type T I will use convert_cast.
Even better:
auto r(try_convert_cast(s)); if (r) { i = r.get(); }
Notice also that the precedent, std::map::insert()'s return type, puts bool second, not first. I never remember the order and you used the reverse. That leaves too much room for confusion. The optional version is much better.
I agree completely. And indeed this usage with auto is convenient. One thing, users using it like this will not notice anymore that "auto" is (behind the screens now) an "optional", and might be surprised by the .get() call.
Let me comment a little more on the function try_convert_cast returning optional T. The function can not be used directly where the target type T was expected so we can not consider it to follow the cast pattern. Other try_ functions return just bool. If we follow this pattern the preceding code could be written as int i; if (try_convert(s,i)) { // do whatever you want with i; } If you want to preserve the convert_cast that returns a optional T, I will prefer to name it optional_convert_cast, so the user that will read it will be advertised that the result is an optional T. auto r(optional_convert_cast(s)); if (r) { i = r.get(); } Best, Vicente

I think we need to clarify one thing. Vladimir library uses values for two purposes: * as a default value when the type is not default constructible * as a fail-back in case of the conversion fails
And I think we should mix them. To cover the first case we can use as I said in another post a default_value metafunction that can be specialized for the non default constructible type.
I agree that when a fail-back is given the user is not interested in knowing if the conversion succeeded or not, so in this case the return value should be T and not optional T. The question now is what function should be used, convert_cast or try_convert_cast. As the function doesn't throw I will use try_convert_cast, but as the function returns type T I will use convert_cast. I disagree. I think the fallback with conversion success is a reasonable use case. Vladimir's case of notifying the user when a fallback value is being used is reasonable. It's difficult to leave
On 5/4/2011 11:05 AM, Vicente BOTET wrote: that logic outside the conversion because only the conversion knows if and why the input is invalid.
Let me comment a little more on the function try_convert_cast returning optional T. The function can not be used directly where the target type T was expected so we can not consider it to follow the cast pattern. Other try_ functions return just bool. If we follow this pattern the preceding code could be written as
int i; if (try_convert(s,i)) { // do whatever you want with i; }
If you want to preserve the convert_cast that returns a optional T, I will prefer to name it optional_convert_cast, so the user that will read it will be advertised that the result is an optional T.
auto r(optional_convert_cast(s)); if (r) { i = r.get(); }
OK, I agree that the optional return values do not conform to the cast semantics, i.e. it should only "cast" to what you tell it to. So either we should drop the _cast suffix for these variants or we specialize for optional<T>. The latter is probably disagreeable since some people might want to make their own specialization (someone mentioned optional<T> <-> string conversion earlier). If others agree, I could also go with Vincente's version of try_convert. It doesn't bother me that passing in the variable by reference makes it a two-liner, since the expression itself can go inside the if statement and it implicitly supports non-default-constructable types. And we can also provide optional_convert, which CAN be one-lined (if the user doesn't care about conversion success, and if they DO, then they should use the try_ version!). string s = "4-2"; // Matt likes to throw: int i = convert_cast<int>(s); // Vladimir cares about success: int i = 17; if (!try_convert(s,i)) { /* log that fallback value is being used */ } // except when he doesn't (but he never throws) int i = convert_cast(s, 17); // Vincente thinks success is optional: optional<int> i = optional_convert<int>(s); Note that in several of these, target typename is no longer needed, right? For non-defaultable types, I am now seeing the elegance of Vincente's default_value template parameter. It wouldn't be needed for the try_convert variant of course. But it would eliminate this distasteful overload: optional<non_defaultable_type> i = optional_convert<non_defaultable_type>(s, 17); With this setup, is there any reason that convert_cast and optional_convert couldn't just be thin wrappers around try_convert? -Matt

Matthew Chambers wrote:
On 5/4/2011 11:05 AM, Vicente BOTET wrote:
I think we need to clarify one thing. Vladimir library uses values for two purposes: * as a default value when the type is not default constructible * as a fail-back in case of the conversion fails
And I think we should mix them. To cover the first case we can use as I said in another post a default_value metafunction that can be specialized for the non default constructible type.
Presumably you meant we should *not* mix the two uses.
I agree that when a fail-back is given the user is not interested in knowing if the conversion succeeded or not, so in this case the return value should be T and not optional T. The question now is what function should be used, convert_cast or try_convert_cast. As the function doesn't throw I will use try_convert_cast, but as the function returns type T I will use convert_cast.
I disagree. I think the fallback with conversion success is a reasonable use case. Vladimir's case of notifying the user when a fallback value is being used is reasonable. It's difficult to leave that logic outside the conversion because only the conversion knows if and why the input is invalid.
I agree with Matt.
Let me comment a little more on the function try_convert_cast returning optional T. The function can not be used directly where the target type T was expected so we can not consider it to follow the cast pattern.
You're right.
Other try_ functions return just bool. If we follow this pattern the preceding code could be written as
int i; if (try_convert(s,i)) { // do whatever you want with i; }
Yeah, I think that's right, too: a "try_" function should return bool. However, the name should be "try_convert_to" if you go that route.
If you want to preserve the convert_cast that returns a optional T, I will prefer to name it optional_convert_cast, so the user that will read it will be advertised that the result is an optional T.
auto r(optional_convert_cast(s)); if (r) { i = r.get(); }
I'd much rather see convert_cast<optional<T>>(S) than optional_convert_cast<T>(). It indicates what is happening better.
If others agree, I could also go with Vincente's version of try_convert. It doesn't bother me that passing in the variable by reference makes it a two-liner, since the expression itself can go inside the if statement and it implicitly supports non-default-constructable types.
It is certainly non-surprising. Using Vicente's default_value customization point still makes convert_cast<optional<T>> a viable candidate: int i; if (try_convert_to<int>(s)) { // use i } auto const c(convert_cast<optional<int>>(s)); if (c) { // use c.get() } Note that try_convert_to() makes for simpler, shorter, and more direct code and that both are two-liners.
And we can also provide optional_convert, which CAN be one-lined (if the user doesn't care about conversion success, and if they DO, then they should use the try_ version!).
Offering both is possible, and depends upon the other functions in the set, though generally, offering two ways to do something can be a source of confusion.
string s = "4-2";
// Matt likes to throw: int i = convert_cast<int>(s);
// Vladimir cares about success: int i = 17; if (!try_convert(s,i)) { /* log that fallback value is being used */ }
// except when he doesn't (but he never throws) int i = convert_cast(s, 17);
// Vincente thinks success is optional: optional<int> i = optional_convert<int>(s);
Note that in several of these, target typename is no longer needed, right?
try_convert(s, i) doesn't tell me whether s is being converted to i's type or i to s's type. convert_cast(s, 17) has the same problem and breaks with the new-style cast pattern. The target type should be required in all cases, I suspect, to make things clearer: try_convert_to<int>(s, i) clearly indicates that int is the target type. convert_cast<int>(s, 17) is likewise better.
With this setup, is there any reason that convert_cast and optional_convert couldn't just be thin wrappers around try_convert?
Perhaps, but there's still too much churn to think about that yet. _____ Rob Stewart robert.stewart@sig.com Software Engineer using std::disclaimer; Dev Tools & Components Susquehanna International Group, LLP http://www.sig.com IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

On 5/4/2011 1:18 PM, Stewart, Robert wrote:
try_convert(s, i) doesn't tell me whether s is being converted to i's type or i to s's type. I don't think this is a problem in practice. C#'s TryParse has the same semantics, albeit you're forced to use the out keyword on the second argument, so perhaps that's not a fair comparison.
convert_cast(s, 17) has the same problem and breaks with the new-style cast pattern. What is the breaking aspect?
The target type should be required in all cases, I suspect, to make things clearer:
try_convert_to<int>(s, i) clearly indicates that int is the target type.
convert_cast<int>(s, 17) is likewise better. Yes, but thanks to type inference users are free to write try_convert_to(s, i) instead and that's even worse than try_convert(s, i). :)
Did we just rediscover Vladimir's reasoning for the ::from interface? If so, I think the cure is worse than the disease. -Matt

Matthew Chambers wrote:
On 5/4/2011 1:18 PM, Stewart, Robert wrote:
try_convert(s, i) doesn't tell me whether s is being converted to i's type or i to s's type.
I don't think this is a problem in practice. C#'s TryParse has the same semantics, albeit you're forced to use the out keyword on the second argument, so perhaps that's not a fair comparison.
I don't think it's a fair comparison. I realize that folks can become accustomed to "try_convert" and learn to read it correctly, but why make it harder than it needs to be?
convert_cast(s, 17) has the same problem and breaks with the new-style cast pattern.
What is the breaking aspect?
New-style casts are of the form xxx_cast<T>(S). Thus, that has to be convert_cast<int>(s, 17) to fit.
The target type should be required in all cases, I suspect, to make things clearer:
try_convert_to<int>(s, i) clearly indicates that int is the target type.
convert_cast<int>(s, 17) is likewise better.
Yes, but thanks to type inference users are free to write try_convert_to(s, i) instead and that's even worse than try_convert(s, i). :)
You can make the second argument non-deducible thus requiring the target type. _____ Rob Stewart robert.stewart@sig.com Software Engineer using std::disclaimer; Dev Tools & Components Susquehanna International Group, LLP http://www.sig.com IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

On 5/4/2011 2:01 PM, Stewart, Robert wrote:
convert_cast(s, 17) has the same problem and breaks with the new-style cast pattern.
What is the breaking aspect?
New-style casts are of the form xxx_cast<T>(S). Thus, that has to be convert_cast<int>(s, 17) to fit. This is a special case of the discussion below.
The target type should be required in all cases, I suspect, to make things clearer:
try_convert_to<int>(s, i) clearly indicates that int is the target type.
Yes, but thanks to type inference users are free to write try_convert_to(s, i) instead and that's even worse than try_convert(s, i). :)
You can make the second argument non-deducible thus requiring the target type. I didn't know that was possible. How? If the target is non-deducible then I agree the _to suffix for optional_convert and try_convert is good.
Back to something you said earlier:
If others agree, I could also go with Vincente's version of try_convert. It doesn't bother me that passing in the variable by reference makes it a two-liner, since the expression itself can go inside the if statement and it implicitly supports non-default-constructable types. It is certainly non-surprising.
Using Vicente's default_value customization point still makes convert_cast<optional<T>> a viable candidate:
auto const c(convert_cast<optional<int>>(s)); if (c) { // use c.get() }
What about UDT specializations for casting to and from optional<T> (i.e. string<->optional<int> where 'n/a' could be the unset string)? -Matt

Matthew Chambers wrote:
On 5/4/2011 2:01 PM, Stewart, Robert wrote:
The target type should be required in all cases, I suspect, to make things clearer:
try_convert_to<int>(s, i) clearly indicates that int is the target type.
Yes, but thanks to type inference users are free to write try_convert_to(s, i) instead and that's even worse than try_convert(s, i). :)
You can make the second argument non-deducible thus requiring the target type.
I didn't know that was possible. How? If the target is non- deducible then I agree the _to suffix for optional_convert and try_convert is good.
template <class T> struct identity { typedef T type; }; template <class T, class S> T try_convert_to(S, identity<T>::type); identity<T>::type is T, but the compiler cannot deduce T from identity<T>::type. Therefore, T must be given specifically.
Back to something you said earlier:
If others agree, I could also go with Vincente's version of try_convert. It doesn't bother me that passing in the variable by reference makes it a two-liner, since the expression itself can go inside the if statement and it implicitly supports non-default-constructable types.
It is certainly non-surprising.
Using Vicente's default_value customization point still makes convert_cast<optional<T>> a viable candidate:
auto const c(convert_cast<optional<int>>(s)); if (c) { // use c.get() }
What about UDT specializations for casting to and from optional<T> (i.e. string<->optional<int> where 'n/a' could be the unset string)?
I don't understand your question, I'm afraid. _____ Rob Stewart robert.stewart@sig.com Software Engineer using std::disclaimer; Dev Tools & Components Susquehanna International Group, LLP http://www.sig.com IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

Message du 04/05/11 21:34 De : "Stewart, Robert" A : "'boost@lists.boost.org'" Copie à : Objet : Re: [boost] [review] string convert
Matthew Chambers wrote:
What about UDT specializations for casting to and from optional (i.e. string<->optional where 'n/a' could be the unset string)?
I don't understand your question, I'm afraid.
I guess he is requesting if "" converts to an unset optional and if an unset optional converts to ""? Vicente

On Wed, May 4, 2011 at 11:37, Matthew Chambers <matt.chambers42@gmail.com> wrote:
Did we just rediscover Vladimir's reasoning for the ::from interface?
Sounds like :)
If so, I think the cure is worse than the disease.
I really like the split into two parts, to allow a direction word. If you don't like the ::from, what about something like this? foo(convert(x).to<int>()); optional<int> i = convert(x).to<int>(std::nothrow); // or maybe .try_to<int>() It's arguably the same as the .value() in Vladimir's lib, except that by delaying providing the type, it makes sense that it's needed. The scary implicit conversions might even make some sense when there's no destination type: int i = convert(x); ~ Scott

On May 4, 2011, at 3:03 PM, Scott McMurray wrote:
The scary implicit conversions might even make some sense when there's no destination type: int i = convert(x);
Please read the posts (esp Thomas Heller's and Christopher Jefferson's) about how implicit conversions break template functions and auto. They're more than just scary.

On Wed, May 4, 2011 at 16:03, Gordon Woodhull <gordon@woodhull.com> wrote:
On May 4, 2011, at 3:03 PM, Scott McMurray wrote:
The scary implicit conversions might even make some sense when there's no destination type: int i = convert(x);
Please read the posts (esp Thomas Heller's and Christopher Jefferson's) about how implicit conversions break template functions and auto. They're more than just scary.
I completely agree they're not appropriate in the normal path. But there's a difference between having explicit conversions from convert<int>::from(x) and from convert(x) Because in the former there's enough information to determine the result type. In the latter there isn't, so expecting it to return anything other than a proxy is illogical. The idea was simply to have a way to prevent the need for repeating the type in both 03 and 0x: int i = convert(x); auto i = convert(x).to<int>(); "i" being a proxy in auto i = convert<int>::from(x); is weird, I agree. "i" being a proxy in auto i = convert(x); is perfectly reasonably, since convert(x) visibly can't possibly have figured out the right type. But sure, the problem with template functions is perhaps a deal-breaker. ~ Scott

Scott McMurray <me22.ca+boost <at> gmail.com> writes:
Gordon Woodhull <gordon <at> woodhull.com> wrote:
Please read the posts (esp Thomas Heller's and Christopher Jefferson's) about how implicit conversions break template functions and auto. They're more than just scary.
I completely agree they're not appropriate in the normal path.
I do not want to re-start that sub-thread. Still, I feel that that line of 'convert' releasing the Satan with implicit conversions to the Target type and to convert<Target>::result has been blown out of all proportions. The implicit conversion to the Target type was essentially a convenience (which we in our project deploy all the time). We could remove it as it could be retrieved with value(). Does the only remaining implicit conversion to such specific type convert<>::result still look scary? In reality, all that was needed to avoid the "break template functions and auto" scenario was template<class T> foo(T const&); int v = convert<int>from(str); auto v1 = convert<int>from(str).value(); foo(v); // correct template resolution or template<class T> foo(T const&); foo(convert<int>from(str).value); // correct template resolution Was it too much to ask? How many libraries can claim to support an attitude like -- I do not want to read the documentation, I want to call those functions as I see fit and I expect them to work correctly? V.

----- "Vladimir Batov" <vbatov@people.net.au> a écrit :
Scott McMurray <me22.ca+boost <at> gmail.com> writes:
Gordon Woodhull <gordon <at> woodhull.com> wrote:
Please read the posts (esp Thomas Heller's and Christopher Jefferson's) about how implicit conversions break template functions and auto. They're more than just scary.
I completely agree they're not appropriate in the normal path.
I do not want to re-start that sub-thread. Still, I feel that that line of 'convert' releasing the Satan with implicit conversions to the Target type and to convert<Target>::result has been blown out of all proportions.
The implicit conversion to the Target type was essentially a convenience (which we in our project deploy all the time). We could remove it as it could be retrieved with value(). Does the only remaining implicit conversion to such specific type convert<>::result still look scary?
In reality, all that was needed to avoid the "break template functions and auto" scenario was
template<class T> foo(T const&); int v = convert<int>from(str); auto v1 = convert<int>from(str).value(); foo(v); // correct template resolution
IMHO, if following statement: auto v = convert<int>from(str); does not give an int, then I'd say that you should consider changing the "convert" name. Call it "converter", "make_converter", etc.
Was it too much to ask? How many libraries can claim to support an attitude like -- I do not want to read the documentation, I want to call those functions as I see fit and I expect them to work correctly?
I dislike the idea that documentation should fix misleading names. Regards, Ivan

Ivan Le Lann <ivan.lelann <at> free.fr> writes:
"Vladimir Batov" <vbatov <at> people.net.au> a écrit : Was it too much to ask? How many libraries can claim to support an attitude like -- I do not want to read the documentation, I want to call those functions as I see fit and I expect them to work correctly?
I dislike the idea that documentation should fix misleading names.
I would not say that the purpose of the documentation is to fix misleading names. I'd say it's to let the user get better understanding if his expectations match the author's vision, design and actual implementation. I understand that one might have certain expectations about, say, convert<int>::from() returning some particular type. However, it needs to be kept in mind that other people might have different expectations or no expectations at all. I quite honestly fail to understand why so much emphasis is put onto "expectations". To write a modestly decent code one must have documentation by his side. Even for something as seemingly basic as lexical_cast one has to *read* and learn that it throws and needs op>>, etc. In fact, "lexical" is about strings. So, lexical_cast<int> must be converting 'int' to a 'string'. Or is it an unreasonable expectation? :-) And out of curiosity what expectations do people actually have from spirit::karma and spirit::qi? Uh, that was merely a rant. Ignore it. V.

Vladimir Batov wrote:
Scott McMurray <me22.ca+boost <at> gmail.com> writes:
Gordon Woodhull <gordon <at> woodhull.com> wrote:
Please read the posts (esp Thomas Heller's and Christopher Jefferson's) about how implicit conversions break template functions and auto. They're more than just scary.
I completely agree they're not appropriate in the normal path.
I do not want to re-start that sub-thread. Still, I feel that that line of 'convert' releasing the Satan with implicit conversions to the Target type and to convert<Target>::result has been blown out of all proportions.
Implicit conversions can cause many unfortunate behaviors when used in generic code, which Boost fosters, of course. It is also wise to prevent unintentional misuse when possible. That is, a good interface will help the user avoid mistakes and implicit conversions can interfere with that.
The implicit conversion to the Target type was essentially a convenience (which we in our project deploy all the time). We could remove it as it could be retrieved with value().
That's always the justification for implicit conversions: convenience. That doesn't mean they don't cause trouble.
Does the only remaining implicit conversion to such specific type convert<>::result still look scary?
It isn't scary. It is troublesome by introducing ambiguities or mistaken template instantiations.
In reality, all that was needed to avoid the "break template functions and auto" scenario was
template<class T> foo(T const&); int v = convert<int>from(str); auto v1 = convert<int>from(str).value(); foo(v); // correct template resolution or template<class T> foo(T const&); foo(convert<int>from(str).value);
Was it too much to ask?
Yes, I think so, though I hadn't noticed it before it was raised during the review. The problem is that "convert<int>::from(str)" looks like it should provide a number. Indeed, "convert_cast<int>(str)" must to follow the pattern. Thus, having to remember to call value() on what looks like shouldn't need it, is frustrating. Worse, what template error backtraces will result? Furthermore, the author of foo() might want to help and so might provide an overload for convert<int>::result in order to call value() for its callers ("to improve interoperability"). Repeating that for every foo() would be irksome.
How many libraries can claim to support an attitude like -- I do not want to read the documentation, I want to call those functions as I see fit and I expect them to work correctly?
That's not how I view it. Instead, the concern is that the implicit conversions are not required, so avoid them to preclude their troublesome aspects. _____ Rob Stewart robert.stewart@sig.com Software Engineer using std::disclaimer; Dev Tools & Components Susquehanna International Group, LLP http://www.sig.com IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

Scott McMurray <me22.ca+boost <at> gmail.com> writes: ... I really like the split into two parts, to allow a direction word.
If you don't like the ::from, what about something like this? foo(convert(x).to<int>()); optional<int> i = convert(x).to<int>(std::nothrow); // or maybe .try_to<int>()
It's arguably the same as the .value() in Vladimir's lib, except that by delaying providing the type, it makes sense that it's needed.
int foo = convert("123").to<int>(); looks *very* attractive as it allows to have the converter proxy which from my experience (which obviously can be wrong) is essential for any functionality beyond lexical_cast. Still it removes that "excessive" converter exposure that some people have strong objections to. The fallback value and additional configuration (locale, formatting, etc.) could probably be provided with (just thinking out loud): int foo = convert("not int").to<int>(-1); int foo = convert("not int")(format_ = std::hex).to<int>(-1); So, 'convert' returns a Target-type-less converter, right? The only wrinkle I can see so far is that 'convert' now does not know the Target type and, therefore, has to accept/support all configuration parameters (format, locale, etc.) for all Target types even if those configurations do not make sense for that ultimate Target type. It could be quite confusing. The advantage of the original proposal was that the Target type was known much earlier (at the time of converter construction), so non-applicable configurations would not compile/be accepted. Hmm, I am afraid the above might not be implementable. Say, the user wants to supply his own manipulator (or locale). That manipulator obviously has to be attached to something (std::istream). However, we do not have that stream in the converter as the converter cannot possibly know if that stream will be needed. V. V.

Vladimir Batov <vbatov <at> people.net.au> writes:
Scott McMurray <me22.ca+boost <at> gmail.com> writes: ... I really like the split into two parts, to allow a direction word.
If you don't like the ::from, what about something like this? foo(convert(x).to<int>()); optional<int> i = convert(x).to<int>(std::nothrow); // or maybe .try_to<int>()
It's arguably the same as the .value() in Vladimir's lib, except that by delaying providing the type, it makes sense that it's needed.
int foo = convert("123").to<int>();
looks *very* attractive as it allows to have the converter proxy which from my experience (which obviously can be wrong) is essential for any functionality beyond lexical_cast. Still it removes that "excessive" converter exposure that some people have strong objections to.
Thought about it some more while sitting through my daughter's soiree. I am afraid that idea technically will be difficult (and I actually mean impossible) to implement as I feel Target-type-less converter loses all its configure-ability power, value and purpose. That way we are one step from getting away without a converter altogether. That way all configuration parameters need to be provided at the time of the call: T convert<T>::from(Source const&, boost::parameter-list) or T convert_to(Source const&, (boost::parameter-list), Rob's-inference-killer-sentinel) Then, still the question of the return type remains -- returning simply T does not cut it for me. I need two things returned -- the value (conversion result or fallback) and success/failure. Unless we are prepared to discard my major use-case altogether (which would not be nice) we seem to have to have convert<T>::result convert<T>::from(...) or std::pair<optional<T>, bool> convert_to<T>(...) which some people reject considering outright and won't settle on anything like: template<class T> foo(T const&) int v = convert_to<int>(...) foo(v); // call with proper template resolution or template<class T> foo(T const&) foo(convert_to<int>(...).value()); // call with proper template resolution Then we seem back to square one. :-( V.

Vladimir Batov wrote: [This post is a little long; I summarize my suggestion at the end]
The question of the return type remains -- returning simply T does not cut it for me. I need two things returned -- the value (conversion result or fallback) and success/failure. Unless we are prepared to discard my major use-case altogether (which would not be nice) we seem to have to have
convert<T>::result convert<T>::from(...) or std::pair<optional<T>, bool> convert_to<T>(...)
I forgot your use case at first, but I think I recall it now. I'll discuss it below. In another post, I offered the following interface:
1. T convert_cast<T,S>(S) 2. T convert_cast<T,S>(S, T) 3. T convert_cast<T,S>(S, T, nothrow_t) 4. optional<T> try_convert_cast<T,S>(S) 5. optional<T> try_convert_cast<T,S>(S, T)
In 1, the result valid unless there's an exception. It doesn't work for non-DefaultConstructible UDTs.
In 2, the result is always valid since it never throws. It works for non-DefaultConstructible UDTs, but not for types without a suitable fallback value.
In 3, the result is valid unless there's an exception. 2 and 3 could be merged, but keeping them distinct is probably more efficient and should be easier to document.
In 4 and 5, the optional is set iff the conversion succeeds.
4 is useful for built-in types and DefaultConstructible UDTs.
5 is useful for all types.
Arguably, "try_convert_cast" wants a better name.
Your use case is the following, right? optional<int> o(try_convert_cast<int>(str)); int i; if (!o) { std::cout << "using fallback\n"; i = fallback; } else { i = o.get(); } That's clearly inconvenient and 5 doesn't apply because the optional won't be set if the conversion fails. To address such inconvenience, you offered convert<T>::result and pair<optional<T>,bool> has been suggested. Isn't optional redundant in the latter? Consider this: pair<int,bool> p(something<int>(str, fallback)); if (!p.second) { std::cout << "using fallback\n"; } int const i(p.first); That's a straightforward use of pair and, I think, addresses everything. If the conversion fails, p.second is false, but p.first == fallback. If the conversion succeeds, p.second is true and p.first holds the conversion result. If the second argument to something() was not truly a fallback, doesn't p.second still provide the necessary information? I noted one missing use case:
What's missing, then, is getting an exception when a conversion to a non-DefaultConstructible UDT or a type with no fallback value fails. That is, a variant of 2 that throws on conversion failure.
Possibly, that would just mean: 2a. T convert_cast<T, S>(S, T, throw_t) However, it's also possible to state that 2 always throws on failure because 3 can be used to force the T argument to be a fallback. That would be more consistent with the difference between 4 and 5, though less convenient for the fallback-without-exception use case which is, arguably, more common than the non-DefaultConstructible-type-with-exception use case (2, as presented above). If we use Vicente's default_value customization point, however, things could be interpreted a little differently: a) T convert_cast<T,S>(S) b) T convert_cast<T,S>(S, T) c) optional<T> try_convert_cast<T,S>(S) d) optional<T> try_convert_cast<T,S>(S, T) a) Uses default_value<T>, if needed, and throws on conversion failure. b) Conversion failure implies returning the second argument's value. c) Uses default_value<T>, if needed, and the return value is not set on conversion failure. This still needs a better name. d) Conversion failure implies returning the second argument's value. This still needs a better name. Given the relative rarity of types that need a special "default" value, and the fact that a compilation error on a line in the primary specialization of default_value can lead the library user to a comment that explains the need to specialize it for T, the regularity of a-d is compelling. Three things are missing from a-d. One is formatting control. However, that can be done through extra arguments as suggested in various other posts. It may be that there would even be overloads of a-d that take the additional arguments in order to streamline the simpler cases. Another thing missing from a-d is the function object you had in convert<T>::converter<S>, IIRC. Is there any reason that cannot just be captured by converter<T,S>? For simplicity of customizing the library, I'd even expect that a-d would use a converter<T,S>. Finally, the function to address your use case is missing. It needs a suitable name, too. e) pair<T,int> something<T,S>(S, T) Summarizing, then: - default_value<T> customization point - converter<T,S> customization point; main logic - T convert_cast<T,S>(S, formatting = none); can throw - T convert_cast<T,S>(S, T, formatting = none) - optional<T> name_me_1<T,S>(S, formatting = none) - optional<T> name_me_1<T,S>(S, T, formatting = none) - pair<T,int> name_me_2<T,S>(S, T, formatting = none) Note that the T argument for convert_cast<T,S>(S, T, formatting) is non-deducible in order to require specifying T in each call. Since the name_me_1 and name_me_2 names are not expected to end with "_cast," the T arguments may be deducible. _____ Rob Stewart robert.stewart@sig.com Software Engineer using std::disclaimer; Dev Tools & Components Susquehanna International Group, LLP http://www.sig.com IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

On 5/5/2011 6:48 AM, Stewart, Robert wrote:
Your use case is the following, right?
optional<int> o(try_convert_cast<int>(str)); int i; if (!o) { std::cout<< "using fallback\n"; i = fallback; } else { i = o.get(); }
That's clearly inconvenient and 5 doesn't apply because the optional won't be set if the conversion fails. To address such inconvenience, you offered convert<T>::result and pair<optional<T>,bool> has been suggested. Isn't optional redundant in the latter?
What happened to: int i = fallback; if (!try_convert_to<int>(str, i)) { cout << "using fallback"; } A good postcondition is that try_convert_to does not modify i if the conversion fails. This achieves the same thing and is honestly more convenient and transparent than the convert<T>::result approach. I suppose you could argue that the preliminary assignment is inefficient, but I can't see a reasonable objection to it. If it really must be avoided, then of course for defaultable types it can look like yours (assigning the fallback in the if), but for non-defaultable types there's no benefit (a default_value<T> invocation is as bad as the preliminary assignment). -Matt

Matt Chambers wrote:
On 5/5/2011 6:48 AM, Stewart, Robert wrote:
Your use case is the following, right?
optional<int> o(try_convert_cast<int>(str)); int i; if (!o) { std::cout<< "using fallback\n"; i = fallback; } else { i = o.get(); }
That's clearly inconvenient and 5 doesn't apply because the optional won't be set if the conversion fails. To address such inconvenience, you offered convert<T>::result and pair<optional<T>,bool> has been suggested. Isn't optional redundant in the latter?
What happened to: int i = fallback; if (!try_convert_to<int>(str, i)) { cout << "using fallback"; }
A good postcondition is that try_convert_to does not modify i if the conversion fails. This achieves the same thing and is honestly more convenient and transparent than the convert<T>::result approach. I suppose you could argue that the preliminary assignment is inefficient, but I can't see a reasonable objection to it. If it really must be avoided, then of course for defaultable types it can look like yours (assigning the fallback in the if), but for non-defaultable types there's no benefit (a default_value<T> invocation is as bad as the preliminary assignment).
How would I use this within an initializer list? Jeff

On 5/5/2011 10:17 AM, Jeff Flinn wrote:
Matt Chambers wrote:
On 5/5/2011 6:48 AM, Stewart, Robert wrote:
Your use case is the following, right?
optional<int> o(try_convert_cast<int>(str)); int i; if (!o) { std::cout<< "using fallback\n"; i = fallback; } else { i = o.get(); }
That's clearly inconvenient and 5 doesn't apply because the optional won't be set if the conversion fails. To address such inconvenience, you offered convert<T>::result and pair<optional<T>,bool> has been suggested. Isn't optional redundant in the latter?
What happened to: int i = fallback; if (!try_convert_to<int>(str, i)) { cout << "using fallback"; }
A good postcondition is that try_convert_to does not modify i if the conversion fails. This achieves the same thing and is honestly more convenient and transparent than the convert<T>::result approach. I suppose you could argue that the preliminary assignment is inefficient, but I can't see a reasonable objection to it. If it really must be avoided, then of course for defaultable types it can look like yours (assigning the fallback in the if), but for non-defaultable types there's no benefit (a default_value<T> invocation is as bad as the preliminary assignment).
How would I use this within an initializer list?
When initializing an int? Clearly in that case you can't get access to success or failure, so you would use the throwing or non-throwing convert_cast: foo(string s) : i(convert_cast<int>(s)) {}; // throws on failure foo(string s) : i(convert_cast<int>(s, 42)) {}; // uses fallback value on failure -Matt

Matt Chambers wrote:
On 5/5/2011 6:48 AM, Stewart, Robert wrote:
optional<int> o(try_convert_cast<int>(str)); int i; if (!o) { std::cout<< "using fallback\n"; i = fallback; } else { i = o.get(); }
That's clearly inconvenient and 5 doesn't apply because the optional won't be set if the conversion fails. To address such inconvenience, you offered convert<T>::result and pair<optional<T>,bool> has been suggested. Isn't optional redundant in the latter?
What happened to: int i = fallback; if (!try_convert_to<int>(str, i)) { cout << "using fallback"; }
It slipped my mind in the whirl of ideas being bandied about in this thread.
A good postcondition is that try_convert_to does not modify i if the conversion fails. This achieves the same thing and is honestly more convenient and transparent than the convert<T>::result approach.
Yes.
I suppose you could argue that the preliminary assignment is inefficient, but I can't see a reasonable objection to it.
For a type like int, it really doesn't matter. For UDTs, there might be some slight penalty, but I think it would be an edge case. OK, that means we're down to the following now: - default_value<T> customization point - converter<T,S> customization point, main logic, functor - T convert_cast<T,S>(S, formatting = none); can throw - T convert_cast<T,S>(S, T, formatting = none) - bool try_convert_to<T,S>(S, T &, formatting = none) I removed the following from my earlier list because I think try_convert_to() is a suitable replacement: - optional<T> name_me_1<T,S>(S, formatting = none) - optional<T> name_me_1<T,S>(S, T, formatting = none) - pair<T,int> name_me_2<T,S>(S, T, formatting = none) (The second is not needed because of the default_value CP.) Clearly, try_convert_to() can be the foundation of the other function templates. Phil's suggestion was to forego the others and just provide the foundation. I'm not sure that's necessary, but it remains an option. It's worth enumerating the use cases: 1. lexical_cast-style usage: new-style cast with exception 2. new-style cast with fallback and no exceptions 3. indicate whether the conversion succeeded; no value if failed 4. indicate whether the conversion succeeded; use fallback Is that a complete list? I suspect I'm missing something. 1 is satisfied by T convert_cast<T,S>(S, formatting = none). 2 is satisfied by T convert_cast<T,S>(S, T, formatting = none). 3 is satisfied by try_convert_to(), though by virtue of having first created a T to pass as the second argument. name_me_1 would be a more direct way to satisfy this use case. 4 is satisfied by try_convert_to(). _____ Rob Stewart robert.stewart@sig.com Software Engineer using std::disclaimer; Dev Tools & Components Susquehanna International Group, LLP http://www.sig.com IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

On 5/5/2011 12:35 PM, Stewart, Robert wrote:
OK, that means we're down to the following now:
- default_value<T> customization point - converter<T,S> customization point, main logic, functor - T convert_cast<T,S>(S, formatting = none); can throw - T convert_cast<T,S>(S, T, formatting = none) - bool try_convert_to<T,S>(S, T&, formatting = none)
I removed the following from my earlier list because I think try_convert_to() is a suitable replacement:
- optional<T> name_me_1<T,S>(S, formatting = none) - optional<T> name_me_1<T,S>(S, T, formatting = none) - pair<T,int> name_me_2<T,S>(S, T, formatting = none)
(The second is not needed because of the default_value CP.)
Clearly, try_convert_to() can be the foundation of the other function templates. Phil's suggestion was to forego the others and just provide the foundation. I'm not sure that's necessary, but it remains an option.
It's worth enumerating the use cases:
1. lexical_cast-style usage: new-style cast with exception 2. new-style cast with fallback and no exceptions 3. indicate whether the conversion succeeded; no value if failed 4. indicate whether the conversion succeeded; use fallback
Is that a complete list? I suspect I'm missing something.
1 is satisfied by T convert_cast<T,S>(S, formatting = none).
2 is satisfied by T convert_cast<T,S>(S, T, formatting = none).
3 is satisfied by try_convert_to(), though by virtue of having first created a T to pass as the second argument. name_me_1 would be a more direct way to satisfy this use case.
4 is satisfied by try_convert_to().
What would a use case look like for converter<T,S>? Some lambda syntax was discussed. Say I want to convert a vector<string> to a vector<int>: vector<string> source; source.push_back("A1"); source.push_back("foo"); vector<int> target; transform(source.begin(), source.end(), back_inserter(target), converter<int>(formatting = hex)); Would something like that work? Perhaps another (cooler) use of this could be as an iterator and/or range adapter. vector<int> target; boost::copy(source | boost::adaptors::converter<int>(formatting = hex), back_inserter(target)); -Matt

Matthew Chambers wrote:
On 5/5/2011 12:35 PM, Stewart, Robert wrote:
OK, that means we're down to the following now:
- default_value<T> customization point - converter<T,S> customization point, main logic, functor - T convert_cast<T,S>(S, formatting = none); can throw - T convert_cast<T,S>(S, T, formatting = none) - bool try_convert_to<T,S>(S, T&, formatting = none)
It's worth enumerating the use cases:
1. lexical_cast-style usage: new-style cast with exception 2. new-style cast with fallback and no exceptions 3. indicate whether the conversion succeeded; no value if failed 4. indicate whether the conversion succeeded; use fallback
Is that a complete list? I suspect I'm missing something.
What would a use case look like for converter<T,S>?
Oh, right. I didn't list the use cases for a function object interface to use with algorithms or to reuse a particular conversion configuration (some set of manipulators, etc.).
Some lambda syntax was discussed. Say I want to convert a vector<string> to a vector<int>:
vector<string> source; source.push_back("A1"); source.push_back("foo"); vector<int> target; transform(source.begin(), source.end(), back_inserter(target), converter<int>(formatting = hex));
Would something like that work?
You need the source and target types, so I think it would be more like this: transform(source.begin(), source.end(), back_inserter(target), converter<string,int>(format = hex));
Perhaps another (cooler) use of this could be as an iterator and/or range adapter.
vector<int> target; boost::copy(source | boost::adaptors::converter<int>( formatting = hex), back_inserter(target));
Interesting. Again, you'd need to specify the source and target types. It occurs to me that the name "converter" is a bit ambiguous WRT its two template parameters. Which should be first, source or target? Arguably, source then target makes sense, since that's the direction of the conversion, but the cast function templates list them in reverse order because the source type can be deduced while the target type must be specified. That could be confusing. converter_from_to<Source,Target> would help, though it's uglier. _____ Rob Stewart robert.stewart@sig.com Software Engineer using std::disclaimer; Dev Tools & Components Susquehanna International Group, LLP http://www.sig.com IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

On 05/05/2011 10:35 AM, Stewart, Robert wrote: [snip]
OK, that means we're down to the following now:
- default_value<T> customization point - converter<T,S> customization point, main logic, functor
It seems that technically the default_value customization is subsumed by converter customization.

Jeremy Maitin-Shepard wrote:
On 05/05/2011 10:35 AM, Stewart, Robert wrote:
OK, that means we're down to the following now:
- default_value<T> customization point - converter<T,S> customization point, main logic, functor
It seems that technically the default_value customization is subsumed by converter customization.
No, default_value<T> applies to all uses of T in converter<T,S>. That is, S can be an element of a large set of types for any given T. _____ Rob Stewart robert.stewart@sig.com Software Engineer using std::disclaimer; Dev Tools & Components Susquehanna International Group, LLP http://www.sig.com IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

Stewart, Robert <Robert.Stewart <at> sig.com> writes: ... OK, that means we're down to the following now:
1 - default_value<T> customization point 2 - converter<T,S> customization point, main logic, functor 3 - T convert_cast<T,S>(S, formatting = none); can throw 4 - T convert_cast<T,S>(S, T, formatting = none) 5 - bool try_convert_to<T,S>(S, T &, formatting = none) ...
#3, #4. I do not think these can cause any controversy (although people who refuse reading documentation might be surprised by #2 throwing or #3 not throwing ;-) ). #5 IMO can. It deploys the Pascal-style parameter passing and modifications. I remember reading Stroustrup (I think) long time ago advising against passing non-const references and I personally agree. That's due to potential confusion and wrong expectations. I am not aware of any function in std and boost doing that. Introducing such a precedent might be a hard-sell. Especially confusing might be the fact that #4 does not modify the second parameter when #5 does. IMO std::pair<T, bool> try_convert_to<T,S>(S, T, formatting = none) is far less-controversial and even familiar (even though I personally dislike std::pair due to unreadability of the resulting code sprinkled with faceless first and second). Could I suggest convert_result as a more palatable alternative? :-) #1. I am personally not thrilled with default_value because it looks to me like additional/avoidable/arbitrary piece of machinery that I have to implement to incorporate my class into the framework. I've been managing without it. I'd expect a new design not to force me to work harder. Additional note. While you are on the interface, it probably needs to be kept in mind that the current (as I understand it anyway) Boost policy is to avoid introducing content directly in the boost namespace. Therefore, I presume fully qualified it'll be something like int i = boost::conversion::convert_cast<int>(str); So, maybe you might consider shortening it to something like int i = boost::convert::cast<int>(str); or int i = boost::convert::to<int>(str); or int i = boost::convert<int>::from(str); // Just kidding. because otherwise the user will be implicitly forced to namespace cvt = boost::conversion; or worse using namespace boost::conversion; Best, V. P.S. Posting this message via Gmane and it wants me to type 'grouchiness' to confirm. How appropriate. LOL

Message du 06/05/11 00:49 De : "Vladimir Batov" A : boost@lists.boost.org Copie à : Objet : Re: [boost] [review] string convert Additional note. While you are on the interface, it probably needs to be kept in mind that the current (as I understand it anyway) Boost policy is to avoid introducing content directly in the boost namespace.
Would you apply this to your Boost libraries? Best, Vicente

Vicente BOTET <vicente.botet <at> wanadoo.fr> writes:
Additional note. While you are on the interface, it probably needs to be kept in mind that the current (as I understand it anyway) Boost policy is to avoid introducing content directly in the boost namespace.
Would you apply this to your Boost libraries?
You are probably referring to my old 'convert' proposal. Yes, all was wrapped inside 'convert' which among other things worked as a namespace. Indeed it was not a namespace. However, it was implemented *as* a namespace. Exactly how namespaces were implemented via struct long time ago when compilers did not support namespaces. Namely, namespace convert { void from(); } can be achieved with 'struct': struct convert { static void from(); }; In fact, for various reasons I still often prefer the latter. Your taste might differ. V.

Message du 06/05/11 01:31 De : "Vladimir Batov" A : boost@lists.boost.org Copie à : Objet : Re: [boost] [review] string convert
Vicente BOTET wanadoo.fr> writes:
Additional note. While you are on the interface, it probably needs to be kept in mind that the current (as I understand it anyway) Boost policy is to avoid introducing content directly in the boost namespace.
Would you apply this to your Boost libraries?
You are probably referring to my old 'convert' proposal. Yes, all was wrapped inside 'convert' which among other things worked as a namespace. Indeed it was not a namespace. However, it was implemented *as* a namespace. Exactly how namespaces were implemented via struct long time ago when compilers did not support namespaces. Namely,
namespace convert { void from(); }
can be achieved with 'struct':
struct convert { static void from(); };
In fact, for various reasons I still often prefer the latter. Your taste might differ.
V.
Well I can see that you use boost::conversion namespace and boost::convert class. So you are using two 'namaspaces'. I don't know if this follows the Boost.rule. In any case it is not quite orthodox. I see that your PImpl proposal defines a class piml which is not even in the boost namespace. Best, Vicente

Vicente BOTET <vicente.botet <at> wanadoo.fr> writes:
Well I can see that you use boost::conversion namespace and boost::convert class. So you are using two 'namaspaces'. I don't know if this follows the Boost.rule. In any case it is not quite orthodox.
I see that your PImpl proposal defines a class piml which is not even in the boost namespace.
I am not sure what point you are trying to make. Are you trying to justify particular behavior using some wacky/bad failed proposal as an example? So, for 'convert' I created 2 'namespaces' instead of 1. So, what now? As for Pimpl, then it's over 4 years old now. If the time comes, I'll re-adjust with the spirit of the day. Apologies but I find your line of reasoning like "you do this" somewhat immature. What have you got all jumpy about anyway? I was not fighting anyone. I was not criticizing anything. I was only reminding what (I think) Dave Abrahams voiced in one of his emails. V.

Message du 06/05/11 03:01 De : "Vladimir Batov" A : boost@lists.boost.org Copie à : Objet : Re: [boost] [review] string convert
Vicente BOTET wanadoo.fr> writes:
Well I can see that you use boost::conversion namespace and boost::convert class. So you are using two 'namaspaces'. I don't know if this follows the Boost.rule. In any case it is not quite orthodox.
I see that your PImpl proposal defines a class piml which is not even in the boost namespace.
I am not sure what point you are trying to make. Are you trying to justify particular behavior using some wacky/bad failed proposal as an example? So, for 'convert' I created 2 'namespaces' instead of 1. So, what now? As for Pimpl, then it's over 4 years old now. If the time comes, I'll re-adjust with the spirit of the day.
Apologies but I find your line of reasoning like "you do this" somewhat immature.
You are right. I should discuss the advantage/liabilities of your proposal independently of how you used namespaces in the past.
What have you got all jumpy about anyway? I was not fighting anyone. I was not criticizing anything. I was only reminding what (I think) Dave Abrahams voiced in one of his emails.
Apologies by misunderstanding your purpose. Best, Vicente

Vladimir Batov wrote:
Stewart, Robert <Robert.Stewart <at> sig.com> writes: ... OK, that means we're down to the following now:
1 - default_value<T> customization point 2 - converter<T,S> customization point, main logic, functor 3 - T convert_cast<T,S>(S, formatting = none); can throw 4 - T convert_cast<T,S>(S, T, formatting = none) 5 - bool try_convert_to<T,S>(S, T &, formatting = none) ...
#3, #4. I do not think these can cause any controversy (although people who refuse reading documentation might be surprised by #2 throwing or #3 not throwing ;-) ).
I don't care too much if they can't read the documentation far enough to learn that much.
#5 IMO can. It deploys the Pascal-style parameter passing and modifications. I remember reading Stroustrup (I think) long time ago advising against passing non-const references and I personally agree. That's due to potential confusion and wrong expectations. I am not aware of any function in std and boost doing that. Introducing such a precedent might be a hard-sell. Especially confusing might be the fact that #4 does not modify the second parameter when #5 does.
I know some folks don't care for output parameters but they are useful. As for #4 not modifying and #5 modifying the second parameter, the names are distinct, so distinct behavior should not be surprising.
std::pair<T, bool> try_convert_to<T,S>(S, T, formatting = none)
is far less-controversial and even familiar
That cannot be used as straightforwardly: int i(3); if (!try_convert_to<int>("1", i)) { // report failure } // i == 3 versus: std::pair<int,bool> maybe(attempt_to_convert_to<int>("1", i)); if (!maybe.second) { // report failure } else { // what's i's value? } Note that I changed the name. The "try" prefix, as noted by Vicente, implies returning a bool, not a pair.
(even though I personally dislike std::pair due to unreadability of the resulting code sprinkled with faceless first and second).
I agree that a dedicated type, with names for the two fields, would be better. Furthermore, that avoids the chance for reversing the parameterizing types: std::pair<bool,int> bad(attempt_to_convert_to...); // Mismatch!
Could I suggest convert_result as a more palatable alternative? :-)
That would have to be convert_result<int>.
#1. I am personally not thrilled with default_value because it looks to me like additional/avoidable/arbitrary piece of machinery that I have to implement to incorporate my class into the framework. I've been managing without it. I'd expect a new design not to force me to work harder.
That's based upon the idea that default constructors and zero-initialization are available for most types one would use this library to convert. However, I note that you replied elsewhere that relatively few classes in your environment have default constructors. That very clearly is a sore spot in this design. Vicente's desire for uniformity, through a generic interface, is laudable. Using a CP like default_value permits that. Having to specialize default_value for most of your classes would be annoying, I agree, but consider that the alternative is to explicitly create an initial value for every conversion call you make.
Additional note. While you are on the interface, it probably needs to be kept in mind that the current (as I understand it anyway) Boost policy is to avoid introducing content directly in the boost namespace.
That's an excellent point.
Therefore, I presume fully qualified it'll be something like
int i = boost::conversion::convert_cast<int>(str);
A namespace alias or using declaration or directive may be used to overcome such a verbose name, of course.
So, maybe you might consider shortening it to something like
int i = boost::convert::cast<int>(str);
With a using directive, that would end up just being cast<int>(str), which is unhelpful.
int i = boost::convert::to<int>(str);
That's good.
int i = boost::convert<int>::from(str); // Just kidding.
I've seen that somewhere before. Now, where was that?
because otherwise the user will be implicitly forced to
namespace cvt = boost::conversion;
or worse
using namespace boost::conversion;
A user may prefer to avoid the verboseness of "boost::conversion::convert_cast," but there's no implicit force. Given that "convert_cast" is a nice, descriptive name, I'd be likely to use a using declaration. _____ Rob Stewart robert.stewart@sig.com Software Engineer using std::disclaimer; Dev Tools & Components Susquehanna International Group, LLP http://www.sig.com IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

From: "Stewart, Robert" <Robert.Stewart@sig.com> ...
int i = boost::convert::to<int>(str);
That's good.
How about we simplify the API down to just: T convert::to<T>(S, boost::parameter-list); Then we specialize it for convert::result type so that convert::result<T> convert::to<result<T>>(S, boost::parameter-list); In fact, if there is a lot of resistance to incorporate that into the lib., I can simply implement that locally. The "bool convert::try_to<>(S, T&)" API is for those who prefer that style. Then, a most-functionality-covering example might look like // The predictable. Throws on failure int i = convert::to<int>("FF", (format_ = std::hex)) // The predictable. Returns fallback on failure int i = convert::to<int>("FF", (fallback_ = 255, locale_= ..., format_ = std::hex, throw_ = true)) // The predictable. Has fallback but is forced to throw on failure int i = convert::to<int>("FF", (fallback_ = 255, locale_= ..., format_ = std::hex, throw_ = true)) // The 'try' version int v = 255; bool success = convert::try_to<int>("FF", v, (locale_= ..., format_ = std::hex)) // My personal favorite (locally implemented or incorporated into the lib.) convert::result<int> res = convert::to<result<int>>("FF", (fallback_ = 255, locale_= ..., format_ = std::hex)); There is no converter exposure. The expected Target type is returned. No implicit conversions whatsoever. When I need convert::result, I'll ask for it explicitly. Seems like that might turn nay-sayers (or no-voters) to happy customers. 'convert' would be fairly easy to re-shape like that. Or Vicente might beef-up his library and get all the fame (oooh, jealous). V.

Message du 07/05/11 07:49 De : "Vladimir Batov" A : boost@lists.boost.org Copie à : Objet : Re: [boost] [review] string convert
From: "Stewart, Robert" ...
int i = boost::convert::to(str);
That's good.
How about we simplify the API down to just:
T convert::to(S, boost::parameter-list);
As Robert said, the name of a free function must be readable when introducing a using namespace. Or is convert a class?
Then we specialize it for convert::result type so that
convert::result convert::to>(S, boost::parameter-list);
I don't see any inconvenient in introducing a special overload. I guess a user needs to call it as follows convert::result r = convert::to>(S, boost::parameter-list);
In fact, if there is a lot of resistance to incorporate that into the lib., I can simply implement that locally. The "bool convert::try_to<>(S, T&)" API is for those who prefer that style. Then, a most-functionality-covering example might look like
// The predictable. Throws on failure int i = convert::to("FF", (format_ = std::hex))
Would this overload exist only when the source parameter is string? Sorry, but I don't see how it could be used for type-to-type conversions int i = convert::to(aValue, (format_ = std::hex)) Would the std::hex be applied to the output or the input stream? ios << aValue; ios >> std::hex >> i; or ios << std::hex << aValue; ios >> i; Are these equivalent? I think the interface must state clearly when these manipulators are applied.
There is no converter exposure. The expected Target type is returned. No implicit conversions whatsoever. When I need convert::result, I'll ask for it explicitly. Seems like that might turn nay-sayers (or no-voters) to happy customers. 'convert' would be fairly easy to re-shape like that. Or Vicente might beef-up his library and get all the fame (oooh, jealous).
Vladimir, as I said you my library doesn't pretend to take care of string conversions or conversions via a stream. My library will specialize the convert_to function when one of the parameters is a string and call the best library providing it. The current code is calling your library. Other candidates are possible depending on which libraries will be there, lexical_cast, yours, construe (using Spirit parser). I'm just suggesting some alternative design to your interface, that would cover the same functionality in a less bloated way. Up to you to take whatever is better for you. While the fallback feature can be associated to a conversion constructor with a fallback parameter, it can not be associated to a conversion operator, as it doesn't allows additional parameters. The same applies to the no throw feature. While the language allows to state at compile time that a constructor or a conversion operator will not throw, there is no constructor or conversion operator that can signal at run-time (without a throw exception) if the conversion succeeded or not. I don't know yet if I will introduce the fallback and the no throw features in my library as the default behavior intends to use the throw conversions and protect it with a try-catch clause. If I or someone else find a default behavior for these features that doesn't use a try-catch block then I will surely introduce them. Anyway, if Boost.Conversion introduce other functions, they should also be customizable as convert_to and assign_to are now. The string specific conversion library (as the yours was at the beginning) could have a more different design and define the throwing functions using the no throwing ones. Resuming. My Boost.Conversion needs a string conversion that more efficient than lexical_cast, but I could use lexical_cast if there is no better choice. If your library reach to manage type-to-type conversion in general, then Boost.Conversion will be a no sense and I will withdraw it with no problem. Good luck with your library, I need it (at least for the string part :) Best, Vicente

Vicente BOTET wrote:
De : "Vladimir Batov"
From: "Stewart, Robert" ...
int i = boost::convert::to(str);
That's good.
How about we simplify the API down to just:
T convert::to(S, boost::parameter-list);
As Robert said, the name of a free function must be readable when introducing a using namespace. Or is convert a class?
I think to<int>("FF") is readable.
Then we specialize it for convert::result type so that
convert::result convert::to>(S, boost::parameter-list);
I don't see any inconvenient in introducing a special overload.
I presume you mean that such an overload could be convenient for someone like Vladimir and would not cause a problem for those preferring other approaches.
I guess a user needs to call it as follows
convert::result r = convert::to>(S, boost::parameter-list);
It's hard to say as that was munged.
// The predictable. Throws on failure int i = convert::to("FF", (format_ = std::hex))
Would this overload exist only when the source parameter is string? Sorry, but I don't see how it could be used for type- to-type conversions
If it supports type-to-type conversion via an I/O stream, it would apply, wouldn't it?
int i = convert::to(aValue, (format_ = std::hex))
Would the std::hex be applied to the output or the input stream?
ios << aValue; ios >> std::hex >> i;
or
ios << std::hex << aValue; ios >> i;
Are these equivalent?
I think the interface must state clearly when these manipulators are applied.
I think it should be something like this: convert::as<int>(aValue, (in_ = std::hex, out_ = std::hex));
Vladimir, as I said you my library doesn't pretend to take care of string conversions or conversions via a stream.
I presume you mean string-to-string conversions when you write "string conversions" ...
My library will specialize the convert_to function when one of the parameters is a string and call the best library providing it.
...because of that. _____ Rob Stewart robert.stewart@sig.com Software Engineer using std::disclaimer; Dev Tools & Components Susquehanna International Group, LLP http://www.sig.com IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

Message du 09/05/11 16:22 De : "Stewart, Robert" A : "'boost@lists.boost.org'" Copie à : Objet : Re: [boost] [review] string convert
Vicente BOTET wrote:
De : "Vladimir Batov"
From: "Stewart, Robert" ...
int i = boost::convert::to(str);
That's good.
How about we simplify the API down to just:
T convert::to(S, boost::parameter-list);
As Robert said, the name of a free function must be readable when introducing a using namespace. Or is convert a class?
I think to("FF") is readable.
Maybe, but the name doesn't scale. So I will prefer to preserve the convert_to prefix.
Then we specialize it for convert::result type so that
convert::result convert::to>(S, boost::parameter-list);
I don't see any inconvenient in introducing a special overload.
I presume you mean that such an overload could be convenient for someone like Vladimir and would not cause a problem for those preferring other approaches.
Right.
I guess a user needs to call it as follows
convert::result r = convert::to>(S, boost::parameter-list);
It's hard to say as that was munged.
convert::result r = convert::to(*convert::result*)(S, boost::parameter-list);
// The predictable. Throws on failure int i = convert::to("FF", (format_ = std::hex))
Would this overload exist only when the source parameter is string? Sorry, but I don't see how it could be used for type- to-type conversions
If it supports type-to-type conversion via an I/O stream, it would apply, wouldn't it?
It would, but I will prefer to use the stream syntax for that kind of conversion instead of using additional parameters.
int i = convert::to(aValue, (format_ = std::hex))
Would the std::hex be applied to the output or the input stream?
ios << aValue; ios >> std::hex >> i;
or
ios << std::hex << aValue; ios >> i;
Are these equivalent?
I think the interface must state clearly when these manipulators are applied.
I think it should be something like this:
convert::as(aValue, (in_ = std::hex, out_ = std::hex));
Hmmm, I don't tisk this is enough readable.
Vladimir, as I said you my library doesn't pretend to take care of string conversions or conversions via a stream.
I presume you mean string-to-string conversions when you write "string conversions" ...
No, I meant conversion from string to a type and from a type to a string. As I tried to explain in this and other posts, to take care of a type-to-type conversion via a streams you needs to state which manipulators are applied to the ostream and which one to the istream. Your in_, out_ is first trial but must be generalized to several manipulators.
My library will specialize the convert_to function when one of the parameters is a string and call the best library providing it.
...because of that.
The intent of Boost.Conversion is to support generic conversion that can be specialized for specific types. One of the major differences between Boost.Conversion and Boost.Convert, a part from the interface is the customization point. In the case of Boost.Convert it is the class convert and defaults conversion using a iostream. Boost.Conversion is customized by overloading the convert_to function, and by default uses the target conversion operator from a source. Best, Vicente

Vicente BOTET wrote:
De : "Stewart, Robert"
Vicente BOTET wrote:
De : "Vladimir Batov"
From: "Stewart, Robert" ...
int i = boost::convert::to(str);
That's good.
How about we simplify the API down to just:
T convert::to(S, boost::parameter-list);
As Robert said, the name of a free function must be readable when introducing a using namespace. Or is convert a class?
I think to<int>("FF") is readable.
Maybe, but the name doesn't scale. So I will prefer to preserve the convert_to prefix.
What do you mean it "doesn't scale?"
// The predictable. Throws on failure int i = convert::to("FF", (format_ = std::hex))
Would this overload exist only when the source parameter is string? Sorry, but I don't see how it could be used for type-to-type conversions
If it supports type-to-type conversion via an I/O stream, it would apply, wouldn't it?
It would, but I will prefer to use the stream syntax for that kind of conversion instead of using additional parameters.
I see.
I think the interface must state clearly when these manipulators are applied.
I think it should be something like this:
convert::as(aValue, (in_ = std::hex, out_ = std::hex));
Hmmm, I don't tisk this is enough readable.
Perhaps not, especially when one might wish to supply multiple manipulators.
Vladimir, as I said you my library doesn't pretend to take care of string conversions or conversions via a stream.
I presume you mean string-to-string conversions when you write "string conversions" ...
No, I meant conversion from string to a type and from a type to a string.
OK, but just after that you stated that your "library will specialize the convert_to function when one of the parameters is a string," which means it will handle that case, even if by using another library behind the scenes.
As I tried to explain in this and other posts, to take care of a type-to-type conversion via a streams you needs to state which manipulators are applied to the ostream and which one to the istream. Your in_, out_ is first trial but must be generalized to several manipulators.
Yes, I just noticed the problem with multiple manipulators when replying above. It's also possible to punt on this issue by deciding that manipulators should only be supported for extractions, which is what Vladimir did. When one needs more control, one can use streams directly. Following that idea, then Vladimir's use of the extraction operator, which clearly indicated which part of the streaming was manipulated, was reasonable, if unusual as a function argument.
The intent of Boost.Conversion is to support generic conversion that can be specialized for specific types. One of the major differences between Boost.Conversion and Boost.Convert, a part from the interface is the customization point. In the case of Boost.Convert it is the class convert and defaults conversion using a iostream. Boost.Conversion is customized by overloading the convert_to function, and by default uses the target conversion operator from a source.
I don't think it's necessary to continue to distinguish between the (now withdrawn) Boost.Convert and your Boost.Conversion library. Instead, we should focus on the future of conversion in Boost. For that, I see both approaches being appropriate, which is why I enumerated an API that includes the stream-based and non-stream-based interfaces. I thought the combination would support what you've been trying to develop and what Valdimir was trying to promote as a unified library for future review. Whether that library is yours or a collaboration is unimportant. Please comment on my 9 point API list, particularly if I've missed something you think is important. _____ Rob Stewart robert.stewart@sig.com Software Engineer using std::disclaimer; Dev Tools & Components Susquehanna International Group, LLP http://www.sig.com IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

Message du 09/05/11 21:26 De : "Stewart, Robert" A : "'boost@lists.boost.org'" Copie à : Objet : Re: [boost] Conversion Library (Was: string convert)
Vicente BOTET wrote:
De : "Stewart, Robert"
Vicente BOTET wrote:
De : "Vladimir Batov"
From: "Stewart, Robert" ... > int i = boost::convert::to(str);
That's good.
How about we simplify the API down to just:
T convert::to(S, boost::parameter-list);
As Robert said, the name of a free function must be readable when introducing a using namespace. Or is convert a class?
I think to("FF") is readable.
Maybe, but the name doesn't scale. So I will prefer to preserve the convert_to prefix.
What do you mean it "doesn't scale?"
With a convert namespace we need to name your functions using prepositions. This limit a lot the choice of names. I will comment to your 9 interface later.
Vladimir, as I said you my library doesn't pretend to take care of string conversions or conversions via a stream.
I presume you mean string-to-string conversions when you write "string conversions" ...
No, I meant conversion from string to a type and from a type to a string.
OK, but just after that you stated that your "library will specialize the convert_to function when one of the parameters is a string," which means it will handle that case, even if by using another library behind the scenes.
Yes, Boost.Conversion will have such overload using a specific library.
As I tried to explain in this and other posts, to take care of a type-to-type conversion via a streams you needs to state which manipulators are applied to the ostream and which one to the istream. Your in_, out_ is first trial but must be generalized to several manipulators.
Yes, I just noticed the problem with multiple manipulators when replying above.
It's also possible to punt on this issue by deciding that manipulators should only be supported for extractions, which is what Vladimir did. When one needs more control, one can use streams directly. Following that idea, then Vladimir's use of the extraction operator, which clearly indicated which part of the streaming was manipulated, was reasonable, if unusual as a function argument.
Yes. This could be a good compromise and the documentation should state clearly when the manipulators are applied.
The intent of Boost.Conversion is to support generic conversion that can be specialized for specific types. One of the major differences between Boost.Conversion and Boost.Convert, a part from the interface is the customization point. In the case of Boost.Convert it is the class convert and defaults conversion using a iostream. Boost.Conversion is customized by overloading the convert_to function, and by default uses the target conversion operator from a source.
I don't think it's necessary to continue to distinguish between the (now withdrawn) Boost.Convert and your Boost.Conversion library. Instead, we should focus on the future of conversion in Boost. For that, I see both approaches being appropriate, which is why I enumerated an API that includes the stream-based and non-stream-based interfaces. I thought the combination would support what you've been trying to develop and what Valdimir was trying to promote as a unified library for future review. Whether that library is yours or a collaboration is unimportant.
I'm not against a collaborative work. I think that both default behaviors are useful, and we can not have both without having two interfaces. I will push for a generic conversion interface based on Boost.Conversion default behavior and a string/stream specific based on Boost.Convert default behavior. We can have all in one library on on two, I have no problem with that. Note that there are other conversion in Boost, NumericConversion that offers specific and rich interface adapted to the kind of conversion that are addressed. I expect something similar for stream based conversion, a stream converter class and a streamable_cast function. Unfortunately I don't know which could be a good namespace to use for this specific conversions yet. If my distinction is not enough clear and justified I could try to be more explicit. Best, Vicente

Vicente BOTET wrote:
De : "Stewart, Robert"
Vicente BOTET wrote:
De : "Stewart, Robert"
Vicente BOTET wrote:
De : "Vladimir Batov"
How about we simplify the API down to just:
T convert::to(S, boost::parameter-list);
As Robert said, the name of a free function must be readable when introducing a using namespace. Or is convert a class?
I think to("FF") is readable.
Maybe, but the name doesn't scale. So I will prefer to preserve the convert_to prefix.
What do you mean it "doesn't scale?"
With a convert namespace we need to name your functions using prepositions. This limit a lot the choice of names. I will comment to your 9 interface later.
I don't think everything must end with a preposition to use "to" or "as." So far, though, your suggestion is to use "convert_to" and "try_convert_to," which means you think "to" is a good choice. The difference is that I suggested that "convert_" be "convert::" instead. _____ Rob Stewart robert.stewart@sig.com Software Engineer using std::disclaimer; Dev Tools & Components Susquehanna International Group, LLP http://www.sig.com IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

Vladimir Batov wrote:
From: "Stewart, Robert" <Robert.Stewart@sig.com>
Vladimir Batov wrote:
There are several threads of discussion going on here and I'd like to avoid letting them fragment too much, so this reply includes thoughts that don't apply directly to what you've written.
int i = boost::convert::to<int>(str);
That's good.
How about we simplify the API down to just:
T convert::to<T>(S, boost::parameter-list);
Vicente has been discussing the issue of stream-based conversions WRT manipulators. In lexical_cast-style conversions, there's an insertion followed by an extraction. Are manipulators needed for both directions? If so, the interface must allow for them using, say, "in" and "out" keywords instead of "format_." to<T>() seems a very nice name for stream-based conversions. It can be qualified by the namespace name, convert::to<T>(), or not and it is still readable. That function can, one way or another, provide support for manipulators while convert_cast<T>() can skip them. That means that to<T>() can be used for stream-based conversions and convert_cast<T>() for non-stream-based conversions. Whether the latter must provide any l10n support is for others to determine as I simply don't know enough to say.
Then we specialize it for convert::result type so that
convert::result<T> convert::to<result<T>>(S, boost::parameter- list);
That strikes me as a very nice compromise.
// The predictable. Throws on failure int i = convert::to<int>("FF", (format_ = std::hex))
Good for stream-based formatting. convert_cast<int>("FF") would be the non-streams-based equivalent.
// The predictable. Returns fallback on failure int i = convert::to<int>("FF", (fallback_ = 255, locale_= ..., format_ = std::hex, throw_ = true))
Good for stream-based formatting. I presume you meant "throw_ = false." I think nothrow (or similar) is better than "throw_ = false," however. try_converting_to<int>("FF", 255) would be the non-streams-based equivalent.
// The predictable. Has fallback but is forced to throw on failure int i = convert::to<int>("FF", (fallback_ = 255, locale_= ..., format_ = std::hex, throw_ = true))
Good for stream-based formatting. convert_cast<int>("FF", 255) would be the non-streams-based equivalent.
// The 'try' version int v = 255; bool success = convert::try_to<int>("FF", v, (locale_= ..., format_ = std::hex))
I don't like that name. "to<int>" says the right thing. "try_to<int>" doesn't. It makes me think "int" is a verb, as in "try to jump." If "to" were "as" instead, then this could be "try_as:" success = convert::try_as<int>("FF", v, ...);
// My personal favorite (locally implemented or incorporated into the lib.) convert::result<int> res = convert::to<result<int>>("FF", (fallback_ = 255, locale_= ..., format_ = std::hex));
Good for stream-based formatting. convert_cast<convert::result<int>>("FF") would work for non-streams-based conversions. This leaves the fate of the converter function objects unclear. That functionality seems too useful to drop. Two sets are needed now? One set would be for non-streams-based conversions and the other set for streams-based conversions? The latter would support manipulators and the former would not. To summarize, here's the complete API as I see it now: 1. convert::default_value<T> customization point * Default value, if needed, when not otherwise supplied 2. convert::converter<T,S> customization point * Non-stream-based main logic * Functor interface 3. T convert::convert_cast<T,S>(S) * Throws on failure unless T is convert::result<T'> * Uses convert::converter<T,S> to do conversion 4. T convert::convert_cast<T,S>(S, T _fallback) * Returns _fallback on failure unless T is convert::result<T'> * Uses convert::converter<T,S> to do conversion 5. bool convert::try_converting_to<T,S>(S, T & _value) * Returns false and leaves _value unchanged on failure * Uses convert::converter<T,S> to do conversion 6. convert::stream_converter<T,S> customization point * Stream-based main logic * Functor interface * Supports manipulators 7. T convert::as<T,S>(S, options) * Throws on failure * Uses convert::stream_converter<T,S> to do conversion 8. T convert::as<T,S>(S, T _fallback, options) * Returns _fallback on failure * Uses convert::stream_converter<T,S> to do conversion 9. bool convert::try_as<T,S>(S, T & _value, options) * Returns false and leaves _value unchanged on failure * Uses convert::stream_converter<T,S> to do conversion Does that cover everything satisfactorily? Did I miss something? Except where I have suggested specific support for convert::result, I don't think it should be allowed. (That is, use SFINAE to ensure that convert::result isn't used with the other parts of the API.) If that's a decent API, and since it represents the combination of the functionality in both Vicente's and your library, perhaps the two of you could collaborate on it and produce a new Boost.Convert proposal. (I'd prefer Boost.Convert to Boost.Conversion because the namespace name is shorter and more useful when reading names like "convert::as.") _____ Rob Stewart robert.stewart@sig.com Software Engineer using std::disclaimer; Dev Tools & Components Susquehanna International Group, LLP http://www.sig.com IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

On 5/9/2011 9:08 AM, Stewart, Robert wrote:
This leaves the fate of the converter function objects unclear. That functionality seems too useful to drop. Two sets are needed now? One set would be for non-streams-based conversions and the other set for streams-based conversions? The latter would support manipulators and the former would not.
To summarize, here's the complete API as I see it now:
1. convert::default_value<T> customization point * Default value, if needed, when not otherwise supplied
2. convert::converter<T,S> customization point * Non-stream-based main logic * Functor interface
3. T convert::convert_cast<T,S>(S) * Throws on failure unless T is convert::result<T'> * Uses convert::converter<T,S> to do conversion
4. T convert::convert_cast<T,S>(S, T _fallback) * Returns _fallback on failure unless T is convert::result<T'> * Uses convert::converter<T,S> to do conversion
5. bool convert::try_converting_to<T,S>(S, T& _value) * Returns false and leaves _value unchanged on failure * Uses convert::converter<T,S> to do conversion
6. convert::stream_converter<T,S> customization point * Stream-based main logic * Functor interface * Supports manipulators
7. T convert::as<T,S>(S, options) * Throws on failure * Uses convert::stream_converter<T,S> to do conversion
8. T convert::as<T,S>(S, T _fallback, options) * Returns _fallback on failure * Uses convert::stream_converter<T,S> to do conversion
9. bool convert::try_as<T,S>(S, T& _value, options) * Returns false and leaves _value unchanged on failure * Uses convert::stream_converter<T,S> to do conversion
Does that cover everything satisfactorily? Did I miss something? Except where I have suggested specific support for convert::result, I don't think it should be allowed. (That is, use SFINAE to ensure that convert::result isn't used with the other parts of the API.)
If that's a decent API, and since it represents the combination of the functionality in both Vicente's and your library, perhaps the two of you could collaborate on it and produce a new Boost.Convert proposal. (I'd prefer Boost.Convert to Boost.Conversion because the namespace name is shorter and more useful when reading names like "convert::as.")
When you say "stream-based main logic" do you mean that it should appear to be using streams, but that it doesn't matter if it really does? It's reasonable to develop optimized string<->numeric converting code that respects locale and manipulators, so I don't think we should exclude the possibility of such implementations. If we're going to create two APIs, I'd rather have one for string<->numeric conversion and one for type<->type conversion. I also don't understand the shift from "convert_cast" to "as". It isn't intuitive why one uses streams and the other doesn't. The specialization for convert::result doesn't bother me, but I suspect it will garner the same criticism as Vladimir's original proposal (i.e. why not optional<T>?). I agree we need to support multiple manipulators, but yuck! The nicest way I can think of to do that is with separate parameters for each manipulator category: int i = convert::as<int>("+42", (_showpos=true, _base=ios::oct)); string s = convert::as<string>(123.456, (_scientific=true)); // or _float=ios::scientific ? Passing them in as a vector would as bad or worse and we've already decided against having to store them in a stateful converter. -Matt

Matthew Chambers wrote:
On 5/9/2011 9:08 AM, Stewart, Robert wrote:
To summarize, here's the complete API as I see it now:
1. convert::default_value<T> customization point * Default value, if needed, when not otherwise supplied
2. convert::converter<T,S> customization point * Non-stream-based main logic * Functor interface
3. T convert::convert_cast<T,S>(S) * Throws on failure unless T is convert::result<T'> * Uses convert::converter<T,S> to do conversion
4. T convert::convert_cast<T,S>(S, T _fallback) * Returns _fallback on failure unless T is convert::result<T'> * Uses convert::converter<T,S> to do conversion
5. bool convert::try_converting_to<T,S>(S, T& _value) * Returns false and leaves _value unchanged on failure * Uses convert::converter<T,S> to do conversion
6. convert::stream_converter<T,S> customization point * Stream-based main logic * Functor interface * Supports manipulators
7. T convert::as<T,S>(S, options) * Throws on failure * Uses convert::stream_converter<T,S> to do conversion
8. T convert::as<T,S>(S, T _fallback, options) * Returns _fallback on failure * Uses convert::stream_converter<T,S> to do conversion
9. bool convert::try_as<T,S>(S, T& _value, options) * Returns false and leaves _value unchanged on failure * Uses convert::stream_converter<T,S> to do conversion
Does that cover everything satisfactorily? Did I miss something? Except where I have suggested specific support for convert::result, I don't think it should be allowed. (That is, use SFINAE to ensure that convert::result isn't used with the other parts of the API.)
When you say "stream-based main logic" do you mean that it should appear to be using streams, but that it doesn't matter if it really does?
No. I meant that all of the stream-based conversions are done via the stream_converter class. It is the customization point and, therefore, the common place in which to handle any special cases for the stream-based conversions. Likewise, the converter class is the customization point and the common place in which to handle special cases for non-stream-based conversions. That approach means that all conversions are handled by one class template for each of the two conversion styles. The various function templates are merely wrappers which provide type deduction and things like exception throwing and varying return types.
It's reasonable to develop optimized string<->numeric converting code that respects locale and manipulators, so I don't think we should exclude the possibility of such implementations.
If such logic can be added to provide conversions as if a stream was used but without using a stream, then it fits as a stream_converter specialization.
If we're going to create two APIs, I'd rather have one for string<->numeric conversion and one for type<->type conversion. I also don't understand the shift from "convert_cast" to "as". It isn't intuitive why one uses streams and the other doesn't.
My idea was that the convert_cast part of the interface was not stream-based and, therefore, incorporated Vicente's ideas, and that the "as" part was stream-based and, therefore, incorporated Vladimir's ideas.
The specialization for convert::result doesn't bother me, but I suspect it will garner the same criticism as Vladimir's original proposal (i.e. why not optional<T>?).
I think the issue is merely one of documentation, showing the difference in features. IOW, here's the code with result and here's the code with optional. See how nice result makes the code? That may be moot now, given the get_value_or() epiphany. Nevertheless, Vladimir correctly noted that he can add the result interface for his own use if it isn't part of a Boost library.
I agree we need to support multiple manipulators, but yuck! The nicest way I can think of to do that is with separate parameters for each manipulator category:
int i = convert::as<int>("+42", (_showpos=true, _base=ios::oct)); string s = convert::as<string>(123.456, (_scientific=true)); // or _float=ios::scientific ?
Do those apply to the insertion into or extraction from the stream?
Passing them in as a vector would as bad or worse and we've already decided against having to store them in a stateful converter.
Quite. _____ Rob Stewart robert.stewart@sig.com Software Engineer using std::disclaimer; Dev Tools & Components Susquehanna International Group, LLP http://www.sig.com IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

On 5/9/2011 4:10 PM, Stewart, Robert wrote:
Matthew Chambers wrote:
When you say "stream-based main logic" do you mean that it should appear to be using streams, but that it doesn't matter if it really does?
No. I meant that all of the stream-based conversions are done via the stream_converter class. It is the customization point and, therefore, the common place in which to handle any special cases for the stream-based conversions.
Likewise, the converter class is the customization point and the common place in which to handle special cases for non-stream-based conversions.
That approach means that all conversions are handled by one class template for each of the two conversion styles. The various function templates are merely wrappers which provide type deduction and things like exception throwing and varying return types.
It's reasonable to develop optimized string<->numeric converting code that respects locale and manipulators, so I don't think we should exclude the possibility of such implementations.
If such logic can be added to provide conversions as if a stream was used but without using a stream, then it fits as a stream_converter specialization. OK, that sounds good.
If we're going to create two APIs, I'd rather have one for string<->numeric conversion and one for type<->type conversion. I also don't understand the shift from "convert_cast" to "as". It isn't intuitive why one uses streams and the other doesn't.
My idea was that the convert_cast part of the interface was not stream-based and, therefore, incorporated Vicente's ideas, and that the "as" part was stream-based and, therefore, incorporated Vladimir's ideas. I understand that, I just meant that the nomenclature doesn't suggest that distinction. Why should the interface be different at all (except for the manipulators)? It seems you've come to this idea in another post.
I agree we need to support multiple manipulators, but yuck! The nicest way I can think of to do that is with separate parameters for each manipulator category:
int i = convert::as<int>("+42", (_showpos=true, _base=ios::oct)); string s = convert::as<string>(123.456, (_scientific=true)); // or _float=ios::scientific ?
Do those apply to the insertion into or extraction from the stream?
Using a single stringstream for a string<->non-string conversion, I'm not sure it makes a difference. The only case I thought of where it would matter is a string<->string, i.e. "42"(dec)<->"2A"(hex) "123.456"(fixed)<->"1.23456e+2"(scientific) Maybe I'm overlooking something obvious. -Matt

Matthew Chambers wrote:
On 5/9/2011 4:10 PM, Stewart, Robert wrote:
Matthew Chambers wrote:
I agree we need to support multiple manipulators, but yuck! The nicest way I can think of to do that is with separate parameters for each manipulator category:
int i = convert::as<int>("+42", (_showpos=true, _base=ios::oct)); string s = convert::as<string>(123.456, (_scientific=true)); // or _float=ios::scientific ?
Do those apply to the insertion into or extraction from the stream?
Using a single stringstream for a string<->non-string conversion, I'm not sure it makes a difference.
I suppose it wouldn't be too hard to document that any manipulators supplied in the conversion apply to the string side of the conversion and, thus, may be insertion or extraction manipulators, depending upon the target and source types.
The only case I thought of where it would matter is a string<->string, i.e.
"42"(dec)<->"2A"(hex) "123.456"(fixed)<->"1.23456e+2"(scientific)
Those wouldn't work as they would require understanding the string's content in order to select an intermediate type for the conversion or else a way to specify the intermediate type. That is, whereas lexical_cast does a source-to-stream-to-target conversion, what you've shown would require a string-to-stream-to-numeric-to-stream-to-string conversion and that requires specifying the intermediate numeric type. IOW, those would actually require two conversions: double const d(convert_to<double>("42", format_ = std::dec)); std::string const s( convert_to<std::string>(d, format_ = std::hex)); So, std::dec would be applied for the extraction, while std::hex for the insertion, because the string type is the source in one case and the target in the other. That makes sense. Vicente, you raised the issue. Is there a use case we missed that requires manipulators for both insertion and extraction, given that string<->string conversions can use manipulators because there's no intermediate conversion? _____ Rob Stewart robert.stewart@sig.com Software Engineer using std::disclaimer; Dev Tools & Components Susquehanna International Group, LLP http://www.sig.com IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

Message du 10/05/11 14:03 De : "Stewart, Robert" A : "'boost@lists.boost.org'" Copie à : Objet : Re: [boost] [review] string convert
Matthew Chambers wrote:
On 5/9/2011 4:10 PM, Stewart, Robert wrote:
Matthew Chambers wrote:
Do those apply to the insertion into or extraction from the stream?
Using a single stringstream for a string<->non-string conversion, I'm not sure it makes a difference.
I suppose it wouldn't be too hard to document that any manipulators supplied in the conversion apply to the string side of the conversion and, thus, may be insertion or extraction manipulators, depending upon the target and source types.
The only case I thought of where it would matter is a string<->string, i.e.
"42"(dec)<->"2A"(hex) "123.456"(fixed)<->"1.23456e+2"(scientific)
Those wouldn't work as they would require understanding the string's content in order to select an intermediate type for the conversion or else a way to specify the intermediate type. That is, whereas lexical_cast does a source-to-stream-to-target conversion, what you've shown would require a string-to-stream-to-numeric-to-stream-to-string conversion and that requires specifying the intermediate numeric type. IOW, those would actually require two conversions:
double const d(convert_to("42", format_ = std::dec)); std::string const s( convert_to(d, format_ = std::hex));
So, std::dec would be applied for the extraction, while std::hex for the insertion, because the string type is the source in one case and the target in the other. That makes sense.
Vicente, you raised the issue. Is there a use case we missed that requires manipulators for both insertion and extraction, given that string<->string conversions can use manipulators because there's no intermediate conversion?
I've no example at hand, but I'm sure we can find type-to-type conversions that need either to use the manipulators for insertion or for extraction. If for string to type it is clear that the manipulators were applied to for extraction and that for type to string the manipulators are applied for insertion, it is not clear when these manipulators should be applied for type-to-type conversions. Best, Vicente

Message du 09/05/11 16:09 De : "Stewart, Robert" A : "'boost@lists.boost.org'" Copie à : Objet : Re: [boost] [review] string convert
To summarize, here's the complete API as I see it now:
1. convert::default_value customization point * Default value, if needed, when not otherwise supplied
I don't see this customization point exclusive for conversions. I will move it to the boost namespace.
2. convert::converter customization point * Non-stream-based main logic * Functor interface
Do you mean that the default behavior is a call to the conversion operator?
3. T convert::convert_cast(S) * Throws on failure unless T is convert::result * Uses convert::converter to do conversion
I prefer conversion::convert_to.
4. T convert::convert_cast(S, T _fallback) * Returns _fallback on failure unless T is convert::result * Uses convert::converter to do conversion
I prefer conversion::convert_to_or.
5. bool convert::try_converting_to(S, T & _value) * Returns false and leaves _value unchanged on failure * Uses convert::converter to do conversion
I prefer conversion::try_convert_to.
6. convert::stream_converter customization point * Stream-based main logic * Functor interface * Supports manipulators
What about streamable::converter?
7. T convert::as(S, options) * Throws on failure * Uses convert::stream_converter to do conversion
What about streamable::convert_to?
8. T convert::as(S, T _fallback, options) * Returns _fallback on failure * Uses convert::stream_converter to do conversion
What about streamable::convert_to_or?
9. bool convert::try_as(S, T & _value, options) * Returns false and leaves _value unchanged on failure * Uses convert::stream_converter to do conversion
What about streamable::try_convert_to?
Does that cover everything satisfactorily? Did I miss something? Except where I have suggested specific support for convert::result, I don't think it should be allowed. (That is, use SFINAE to ensure that convert::result isn't used with the other parts of the API.)
If try_ will return bool, I think that we need also a function that returns optional. Even if the function has the problem related to a template parameter, the name of the function don't reflect that the result will be the parameter T, and doesn't return a bloated interface, but a well know and useful class that I believe is proposed for TR2, or if it is not the case it should be.
If that's a decent API, and since it represents the combination of the functionality in both Vicente's and your library, perhaps the two of you could collaborate on it and produce a new Boost.Convert proposal. (I'd prefer Boost.Convert to Boost.Conversion because the namespace name is shorter and more useful when reading names like "convert::as.")
The names I have suggested allow to have two more or less independent libraries, that follows the same interface on the common needs, but having a different default behavior. For the specific stream conversions there are surely a lot of variants that can be provided. I recognize that the namespace streamable is not perfect. If we find a better one, Robert, Vladimir what do you think of this proposal? Best, Vicente

Vicente BOTET wrote:
De : "Stewart, Robert"
To summarize, here's the complete API as I see it now:
1. convert::default_value customization point * Default value, if needed, when not otherwise supplied
I don't see this customization point exclusive for conversions. I will move it to the boost namespace.
I don't know about that. If a type has a globally applicable default value, then it will have a default constructor (or be zero-initializable). I see default_value as specific to conversions.
2. convert::converter customization point * Non-stream-based main logic * Functor interface
Do you mean that the default behavior is a call to the conversion operator?
The default behavior is to rely on T(S) or T = S. Specializations of converter<T,S> can do things differently.
3. T convert::convert_cast(S) * Throws on failure unless T is convert::result * Uses convert::converter to do conversion
I prefer conversion::convert_to.
That looks every bit like a cast. Why not name is so? Still, if there are to be parallel namespaces (see below), then convert_to() would be good.
4. T convert::convert_cast(S, T _fallback) * Returns _fallback on failure unless T is convert::result * Uses convert::converter to do conversion
I prefer conversion::convert_to_or.
Overloading is a lot cleaner to my eyes and, of course, I don't like your name as I've noted.
5. bool convert::try_converting_to(S, T & _value) * Returns false and leaves _value unchanged on failure * Uses convert::converter to do conversion
I prefer conversion::try_convert_to.
It doesn't read properly, but I now see why you like it: you are adding "try_" to "convert_to" the name you want for #3. There is asymmetry in convert_cast/try_converting_to versus your convert_to/try_convert_to, which is good. However, convert_to_or simply has to go! ;-)
6. convert::stream_converter customization point * Stream-based main logic * Functor interface * Supports manipulators
What about streamable::converter?
In my mind, there's one main namespace for the conversion library. Therefore, that would need to be conversion::streamable::converter and then I'd expect conversion::something_else::converter for the non-stream-based converter. I'd prefer conversion::converter and conversion::stream_converter, but that does require different names for the two sets of function templates.
7. T convert::as(S, options) * Throws on failure * Uses convert::stream_converter to do conversion
What about streamable::convert_to?
conversion::streamable::convert_to() is fine, but then the non-stream-based version should be conversion::something_else::convert_to(). Still, this makes for highly parallel interfaces -- other than where the streamable API includes support for the extra formatting, etc. -- so is worth further discussion if a good pair of namespace names can be found.
8. T convert::as(S, T _fallback, options) * Returns _fallback on failure * Uses convert::stream_converter to do conversion
What about streamable::convert_to_or?
Nope; I very much dislike the "to_or" naming. Once again, I like the overloading. They differ by one argument which dictates no exception. I don't see why they need a different name.
9. bool convert::try_as(S, T & _value, options) * Returns false and leaves _value unchanged on failure * Uses convert::stream_converter to do conversion
What about streamable::try_convert_to?
If there's to be conversion::something_else::convert_to() and conversion::something_else::try_convert_to(), then conversion::streamable::try_convert_to() is fine. BTW, I think "streaming" or even "stream" is better than "streamable."
Does that cover everything satisfactorily? Did I miss something? Except where I have suggested specific support for convert::result, I don't think it should be allowed. (That is, use SFINAE to ensure that convert::result isn't used with the other parts of the API.)
If try_ will return bool, I think that we need also a function that returns optional.
OK. There are those that dislike non-const reference arguments, so such an interface would satisfy those.
Even if the function has the problem related to a template parameter, the name of the function don't reflect that the result will be the parameter T, and doesn't return a bloated interface, but a well know and useful class that I believe is proposed for TR2, or if it is not the case it should be.
I'm sorry. I couldn't parse that.
The names I have suggested allow to have two more or less independent libraries, that follows the same interface on the common needs, but having a different default behavior. For the specific stream conversions there are surely a lot of variants that can be provided.
I won't summarize the API this time because there are names to be ironed out, but there is the optional-based interface to add. I think that's best handled by making the use of optional explicit: optional<int> const o1(convert_cast<optional<int>>(s)); optional<int> const o2(as<optional<int>>(s)); When auto is added to the mix, it works very nicely. auto const o1(convert_cast<optional<int>>(s)); auto const o2(as<optional<int>>(s)); _____ Rob Stewart robert.stewart@sig.com Software Engineer using std::disclaimer; Dev Tools & Components Susquehanna International Group, LLP http://www.sig.com IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

Message du 09/05/11 23:34 De : "Stewart, Robert" A : "'boost@lists.boost.org'" Copie à : Objet : Re: [boost] [review] string convert
Vicente BOTET wrote:
De : "Stewart, Robert"
To summarize, here's the complete API as I see it now:
1. convert::default_value customization point * Default value, if needed, when not otherwise supplied
I don't see this customization point exclusive for conversions. I will move it to the boost namespace.
I don't know about that. If a type has a globally applicable default value, then it will have a default constructor (or be zero-initializable). I see default_value as specific to conversions.
You can have a 3pp class that has not defined a default constructor, but in your application you could find one. In any case, default_value is useful only when stream conversions are concerned.
2. convert::converter customization point * Non-stream-based main logic * Functor interface
Do you mean that the default behavior is a call to the conversion operator?
The default behavior is to rely on T(S) or T = S. Specializations of converter can do things differently.
OK. Boost.Conversion customization point is based on overload of the convert_to function. Are you proposing I change this? What are the advantages?
3. T convert::convert_cast(S) * Throws on failure unless T is convert::result * Uses convert::converter to do conversion
I prefer conversion::convert_to.
That looks every bit like a cast. Why not name is so? Still, if there are to be parallel namespaces (see below), then convert_to() would be good.
4. T convert::convert_cast(S, T _fallback) * Returns _fallback on failure unless T is convert::result * Uses convert::converter to do conversion
I prefer conversion::convert_to_or.
Overloading is a lot cleaner to my eyes and, of course, I don't like your name as I've noted.
OK. I could remove the _or suffix and use overloading if we don't find a better name.
5. bool convert::try_converting_to(S, T & _value) * Returns false and leaves _value unchanged on failure * Uses convert::converter to do conversion
I prefer conversion::try_convert_to.
It doesn't read properly, but I now see why you like it: you are adding "try_" to "convert_to" the name you want for #3. There is asymmetry in convert_cast/try_converting_to versus your convert_to/try_convert_to, which is good. However, convert_to_or simply has to go! ;-)
OK.
6. convert::stream_converter customization point * Stream-based main logic * Functor interface * Supports manipulators
What about streamable::converter?
In my mind, there's one main namespace for the conversion library. Therefore, that would need to be conversion::streamable::converter and then I'd expect conversion::something_else::converter for the non-stream-based converter. I'd prefer conversion::converter and conversion::stream_converter, but that does require different names for the two sets of function templates.
Well, I don't think we need absolutely a specific namespace for the generic conversion (using the conversion operator by default) other than the conversion namespace, For the stream based conversion I will not use conversion::streaming::, but just streaming::
BTW, I think "streaming" or even "stream" is better than "streamable."
yes, streaming could be more acceptable.
Does that cover everything satisfactorily? Did I miss something? Except where I have suggested specific support for convert::result, I don't think it should be allowed. (That is, use SFINAE to ensure that convert::result isn't used with the other parts of the API.)
If try_ will return bool, I think that we need also a function that returns optional.
OK. There are those that dislike non-const reference arguments, so such an interface would satisfy those.
Even if the function has the problem related to a template parameter, the name of the function don't reflect that the result will be the parameter T, and doesn't return a bloated interface, but a well know and useful class that I believe is proposed for TR2, or if it is not the case it should be.
I'm sorry. I couldn't parse that.
It is my bad. What I meant is that the problem was found in the context in of a single function behaving as an optional, the Target type itself, a bool. When we use two different names, one returning the target type (convert_to) and the other returning an optional (try_convert_to) is simple to avoid the ambiguity. It is enough to use convert_to. No need to add a .value() to qualify the requested result.
The names I have suggested allow to have two more or less independent libraries, that follows the same interface on the common needs, but having a different default behavior. For the specific stream conversions there are surely a lot of variants that can be provided.
I won't summarize the API this time because there are names to be ironed out, but there is the optional-based interface to add. I think that's best handled by making the use of optional explicit:
optional const o1(convert_cast>(s)); optional const o2(as>(s));
When auto is added to the mix, it works very nicely.
auto const o1(convert_cast>(s)); auto const o2(as>(s));
Ok. I agree with this overloading as it states clearly the returned type and avoid looking for a name. I will add the new prototypes for the part non related to streams in Boost.Conversion and request a pre-review to see what others think. If the namespace conversion is requested by others I will add it. Waiting for what Vladimir will propose. Best, Vicente

Vicente BOTET wrote:
De : "Stewart, Robert"
Vicente BOTET wrote:
De : "Stewart, Robert"
2. convert::converter customization point * Non-stream-based main logic * Functor interface
Do you mean that the default behavior is a call to the conversion operator?
The default behavior is to rely on T(S) or T = S. Specializations of converter can do things differently.
OK. Boost.Conversion customization point is based on overload of the convert_to function. Are you proposing I change this? What are the advantages?
Yes. As I have mentioned elsewhere, the functor interface is useful and provides a convenient place to do specializations since it supports partial specialization. IOW, having the function template use a class template to implement its logic gives the best of each: template argument deduction and partial specialization.
6. convert::stream_converter customization point * Stream-based main logic * Functor interface * Supports manipulators
What about streamable::converter?
In my mind, there's one main namespace for the conversion library. Therefore, that would need to be conversion::streamable::converter and then I'd expect conversion::something_else::converter for the non-stream-based converter. I'd prefer conversion::converter and conversion::stream_converter, but that does require different names for the two sets of function templates.
Well, I don't think we need absolutely a specific namespace for the generic conversion (using the conversion operator by default) other than the conversion namespace,
Is that, perhaps, because the type-to-type conversions have been your focus?
For the stream based conversion I will not use conversion::streaming::, but just streaming::
Distinct namespaces within boost conventionally implies distinct libraries, yet we're creating symmetry between the stream-based and non-stream-based conversions that cries out for a single library. If default_value is conversion specific, and if we retain Vladimir's result type, both belong to the boost::conversion (or convert!) namespace. Using them with boost::streaming functions would not be horrible, but seems odd, especially if there's just one library. Therefore, boost::conversion::streaming and boost::conversion::direct (or whatever) seems better. _____ Rob Stewart robert.stewart@sig.com Software Engineer using std::disclaimer; Dev Tools & Components Susquehanna International Group, LLP http://www.sig.com IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

Stewart, Robert <Robert.Stewart <at> sig.com> writes:
T convert::to<T>(S, boost::parameter-list);
Vicente has been discussing the issue of stream-based conversions WRT manipulators. In lexical_cast-style conversions, there's an insertion followed by an extraction. Are manipulators needed for both directions? If so, the interface must allow for them using, say, "in" and "out" keywords instead of "format_."
There has been no need for that. The original 'convert' was applying manipulators in both directions. The in/out direction is defined by the string-to-type or type-to-string specialization.
to<T>() seems a very nice name for stream-based conversions. It can be qualified by the namespace name,
The original 'convert' was a struct working as a namespace, i.e. one had to say convert::to/from.
convert::to<T>(), or not and it is still readable. That function can, one way or another, provide support for manipulators while convert_cast<T>() can skip them. That means that to<T>() can be used for stream-based conversions and convert_cast<T>() for non-stream-based conversions. Whether the latter must provide any l10n support is for others to determine as I simply don't know enough to say.
I am not sure why that distinction has to be made. Now that converters are gone and all parameters are provided in one call, the implementer knows what conversion is needed. That is, if there are no 'format_' parameters specified, then, say, spirit-based quick conversion is applied. As soon as I see 'format_', then I fall back to stream-based. So, all we seem to need is convert::to.
Then we specialize it for convert::result type so that
convert::result<T> convert::to<result<T>>(S, boost::parameter- list);
That strikes me as a very nice compromise.
Glad to hear that.
// The predictable. Throws on failure int i = convert::to<int>("FF", (format_ = std::hex))
Good for stream-based formatting.
convert_cast<int>("FF") would be the non-streams-based equivalent.
As I said, I'd expect the following to be the non-streams-based equivalent: int i = convert::to<int>("FF")
// The predictable. Returns fallback on failure int i = convert::to<int>("FF", (fallback_ = 255, locale_= ..., format_ = std::hex, throw_ = true))
Good for stream-based formatting. I presume you meant "throw_ = false."
Indeed, I meant *no* 'throw_' parameter (which is "throw_ = false.").
I think nothrow (or similar) is better than "throw_ = false," however.
try_converting_to<int>("FF", 255) would be the non-streams-based equivalent.
Again, I'd like to re-iterate (as I think it's quite important) that the choice of the best available converter is done internally based on supplied parameters. Therefore, no need to stream- and non-stream-based variants.
...
// The 'try' version int v = 255; bool success = convert::try_to<int>("FF", v, (locale_= ..., format_ = std::hex))
I don't like that name. "to<int>" says the right thing. "try_to<int>" doesn't. It makes me think "int" is a verb, as in "try to jump."
I am not a fan either (with modifiable ref-passing). In my world it does not exist. So, I'll stand aside while others discuss this func/name.
This leaves the fate of the converter function objects unclear. That functionality seems too useful to drop.
I am not sure converters are needed as we can feed convert::to() to algorithms with 'bind' or 'lambda' .
To summarize, here's the complete API as I see it now:
1. convert::default_value<T> customization point * Default value, if needed, when not otherwise supplied
I expressed my opposition to default_value. It was not needed for the original 'convert', i.e. we can get away without it. Asking the user to supply this arbitrary functionality is bothersome and will be mis-used/abused.
2. convert::converter<T,S> customization point * Non-stream-based main logic * Functor interface
I am not sure converters are needed as we can feed convert::to() to algorithms with 'bind' or 'lambda' .
3. T convert::convert_cast<T,S>(S) * Throws on failure unless T is convert::result<T'> * Uses convert::converter<T,S> to do conversion
T convert::to<T>(s) seems to work as well. Less API to remember.
4. T convert::convert_cast<T,S>(S, T _fallback) * Returns _fallback on failure unless T is convert::result<T'> * Uses convert::converter<T,S> to do conversion
T convert::to<T>(s, (fallback_ = ...)) seems to work as well. Less API to remember.
5. bool convert::try_converting_to<T,S>(S, T & _value) * Returns false and leaves _value unchanged on failure * Uses convert::converter<T,S> to do conversion
If people want it. I'll be using convert::result<T> convert::to<convert::result<T>>(s) instead. Less API to remember.
6. convert::stream_converter<T,S> customization point * Stream-based main logic * Functor interface * Supports manipulators
I am not sure converters are needed as we can feed convert::to() to algorithms with 'bind' or 'lambda' .
7. T convert::as<T,S>(S, options) * Throws on failure * Uses convert::stream_converter<T,S> to do conversion
8. T convert::as<T,S>(S, T _fallback, options) * Returns _fallback on failure * Uses convert::stream_converter<T,S> to do conversion
The fallback could be provided again via boost::parameter-list as T convert::to<T>(s, (fallback_ = ...)) seems to work as well. Less API to remember. (I user convert::to instead of convert::as. Not sure if convert::as was intentional or accidental).
9. bool convert::try_as<T,S>(S, T & _value, options) * Returns false and leaves _value unchanged on failure * Uses convert::stream_converter<T,S> to do conversion
That's a lot. I'd seem to manage with just convert::to. :-) ... even though it did not make it onto the list.
If that's a decent API, and since it represents the combination of the functionality in both Vicente's and your library, perhaps the two of you could collaborate on it and produce a new Boost.Convert proposal. (I'd prefer Boost.Convert to Boost.Conversion because the namespace name is shorter and more useful when reading names like "convert::as.")
I do not want to slow Vicente down and I am happy for Vicente to steam ahead if he feels like it... if he is willing to cover use-cases described in the original 'convert' proposal. Although so far I did not get that impression that Vicente was covering the original 'convert' cases. If that's changed and I missed it, great. V.

Message du 10/05/11 02:25 De : "Vladimir Batov" A : boost@lists.boost.org Copie à : Objet : Re: [boost] [review] string convert
Stewart, Robert sig.com> writes:
convert::to(), or not and it is still readable. That function can, one way or another, provide support for manipulators while convert_cast() can skip them. That means that to() can be used for stream-based conversions and convert_cast() for non-stream-based conversions. Whether the latter must provide any l10n support is for others to determine as I simply don't know enough to say.
I am not sure why that distinction has to be made. Now that converters are gone and all parameters are provided in one call, the implementer knows what conversion is needed. That is, if there are no 'format_' parameters specified, then, say, spirit-based quick conversion is applied. As soon as I see 'format_', then I fall back to stream-based. So, all we seem to need is convert::to.
Vladimir, what is the default behavior of your convert::to proposal with only one source argument? If it is a call to the conversion operator then the two approaches are just differing at the interface level and we should have just one. If the default behavior is the the equivalent of a lexical cast whatever technique is used, the the libraries have different semantics and I need to have the Boost.Conversion default behavior. So we will need two libraries.
If that's a decent API, and since it represents the combination of the functionality in both Vicente's and your library, perhaps the two of you could collaborate on it and produce a new Boost.Convert proposal. (I'd prefer Boost.Convert to Boost.Conversion because the namespace name is shorter and more useful when reading names like "convert::as.")
I do not want to slow Vicente down and I am happy for Vicente to steam ahead if he feels like it... if he is willing to cover use-cases described in the original 'convert' proposal. Although so far I did not get that impression that Vicente was covering the original 'convert' cases. If that's changed and I missed it, great.
As you, I'm ready to add whatever feature I think is useful to the users that preserve my library main intent. I have presented some proposals for the non default constructible class, for the no-throw semantics and for the fallback that follows the initial design. I think that stream based conversion need special treatment as it is the case for numeric conversions and I don't want to play in this game. Recall again the for me the main difference between the libraries was the default behavior. I you want to change the default behavior of Boost.Convert, then the things could be quite different. Best, Vicente

From: "Vicente BOTET" <vicente.botet@wanadoo.fr>
De : "Vladimir Batov"
I am not sure why that distinction has to be made. Now that converters are gone and all parameters are provided in one call, the implementer knows what conversion is needed. That is, if there are no 'format_' parameters specified, then, say, spirit-based quick conversion is applied. As soon as I see 'format_', then I fall back to stream-based. So, all we seem to need is convert::to.
Vladimir, what is the default behavior of your convert::to proposal with only one source argument? If it is a call to the conversion operator then the two approaches are just differing at the interface level and we should have just one. If the default behavior is the equivalent of a lexical cast whatever technique is used, the the libraries have different semantics and I need to have the Boost.Conversion default behavior. So we will need two libraries.
Err, you mean int i = convert::to<int>(str); right? Does not it work as lexical_cast (for string-to-int) as I do not think there are other choices. I am not sure what "a call to the conversion operator" means. If see this call returning int() or some default_value<int>, then I firmly believe that is not right as you cannot possibly anticipate what the user might consider an appropriate default. V.

On May 10, 2011, at 3:10 AM, Vladimir Batov wrote:
From: "Vicente BOTET" <vicente.botet@wanadoo.fr>
De : "Vladimir Batov"
I am not sure why that distinction has to be made. Now that converters are gone and all parameters are provided in one call, the implementer knows what conversion is needed. That is, if there are no 'format_' parameters specified, then, say, spirit-based quick conversion is applied. As soon as I see 'format_', then I fall back to stream-based. So, all we seem to need is convert::to.
Vladimir, what is the default behavior of your convert::to proposal with only one source argument? If it is a call to the conversion operator then the two approaches are just differing at the interface level and we should have just one. If the default behavior is the equivalent of a lexical cast whatever technique is used, the the libraries have different semantics and I need to have the Boost.Conversion default behavior. So we will need two libraries.
Err, you mean int i = convert::to<int>(str); right? Does not it work as lexical_cast (for string-to-int) as I do not think there are other choices. I am not sure what "a call to the conversion operator" means. If see this call returning int() or some default_value<int>, then I firmly believe that is not right as you cannot possibly anticipate what the user might consider an appropriate default.
I am not sure if one unified function is possible, but I wonder if one way to define a "best conversion" function, would be to drop lexical_cast's type-to-stream-to-type default, which is IMHO what really messes up thinking about type-to-type and string-to-type/type-to-string in the same sentence. And so convert_cast<T>(S) would use something like Convert if S or T is string (with any behavior determined by arguments), and something like Conversion if neither is. [again i'm not concerned with the name just the syntax/semantics.] This is also in line with Antony Polukhin's improvements for lexical_cast which are mostly not lexical IIUC. I think people and their metaprograms expect a conversion function that is not actually lexical unless S or T is string. IMO the name and default-to-streaming behavior of lexical_cast makes plenty of sense for string-to-type or type-to-string, but would rarely be useful for type-to-type. I'm not sure if I have followed the entire conversation (I've read it once), but I do not see anything that is incompatible about Vladimir's and Vicente's approaches unless this misfeature of lexical_cast is preserved. There are some extra arguments (e.g. manipulators) which might become legal depending upon S or T, which Vicente does not think he needs for any other type-to-type. There is a need to somehow return a value along with a success flag, or to return an optional. There is a fallback. Cheers, Gordon

Gordon Woodhull wrote:
I am not sure if one unified function is possible, but I wonder if one way to define a "best conversion" function, would be to drop lexical_cast's type-to-stream-to-type default, which is IMHO what really messes up thinking about type-to-type and string-to-type/type-to-string in the same sentence.
And so convert_cast<T>(S) would use something like Convert if S or T is string (with any behavior determined by arguments), and something like Conversion if neither is.
This is also in line with Antony Polukhin's improvements for lexical_cast which are mostly not lexical IIUC. I think people and their metaprograms expect a conversion function that is not actually lexical unless S or T is string.
At first blush, I agree that, without a string, there's no "lexical" in the cast. However, a UDT might contain a string and thus rely on the stream behavior and formatting. That, could, of course, be managed with a trait that indicates whether a type requires a stream for its conversion. Specializations for string types would provide the default behavior, but authors of UDTs can specialize the trait as needed. If the trait indicates non-stream-based conversion, is it a (compile time) error to call a conversion function with manipulators, thereby implying the desire to use a stream? With such a trait in place, the same syntax may involve a stream or not, depending upon the types involved. This fits Vladimir's idea that there need be no naming distinction between the stream-based and non-stream-based conversion functions since the trait would indicate whether a stream is needed. _____ Rob Stewart robert.stewart@sig.com Software Engineer using std::disclaimer; Dev Tools & Components Susquehanna International Group, LLP http://www.sig.com IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

On May 10, 2011, at 8:51 AM, "Stewart, Robert" <Robert.Stewart@sig.com> wrote:
Gordon Woodhull wrote:
I am not sure if one unified function is possible, but I wonder if one way to define a "best conversion" function, would be to drop lexical_cast's type-to-stream-to-type default, which is IMHO what really messes up thinking about type-to-type and string-to-type/type-to-string in the same sentence.
And so convert_cast<T>(S) would use something like Convert if S or T is string (with any behavior determined by arguments), and something like Conversion if neither is.
This is also in line with Antony Polukhin's improvements for lexical_cast which are mostly not lexical IIUC. I think people and their metaprograms expect a conversion function that is not actually lexical unless S or T is string.
At first blush, I agree that, without a string, there's no "lexical" in the cast. However, a UDT might contain a string and thus rely on the stream behavior and formatting. That, could, of course, be managed with a trait that indicates whether a type requires a stream for its conversion. Specializations for string types would provide the default behavior, but authors of UDTs can specialize the trait as needed.
This seems like a case of non-lexical cast recursing into a lexical cast. To take a concrete example, say you want to convert pair<string,string> to pair<int,int>. Vicente's library already automatically decomposes this into two string->int conversions. Those might need manipulators, so it seems a unified interface would be helpful here, so that eg std::hex can be passed through Vicente's library to where it's needed in Vladomir's. But is this opening up another can of worms? What about converting paid<string,int> to pair<int,string> ? Now we're back to needing both in and out manipulators, for a different reason!

Message du 10/05/11 09:11 De : "Vladimir Batov" A : boost@lists.boost.org Copie à : Objet : Re: [boost] [review] string convert
From: "Vicente BOTET"
De : "Vladimir Batov"
I am not sure why that distinction has to be made. Now that converters are gone and all parameters are provided in one call, the implementer knows what conversion is needed. That is, if there are no 'format_' parameters specified, then, say, spirit-based quick conversion is applied. As soon as I see 'format_', then I fall back to stream-based. So, all we seem to need is convert::to.
Vladimir, what is the default behavior of your convert::to proposal with only one source argument? If it is a call to the conversion operator then the two approaches are just differing at the interface level and we should have just one. If the default behavior is the equivalent of a lexical cast whatever technique is used, the the libraries have different semantics and I need to have the Boost.Conversion default behavior. So we will need two libraries.
Err, you mean int i = convert::to(str); right? Does not it work as lexical_cast (for string-to-int) as I do not think there are other choices. I am not sure what "a call to the conversion operator" means. If see this call returning int() or some default_value, then I firmly believe that is not right as you cannot possibly anticipate what the user might consider an appropriate default.
I suspect you have not understood my concern. The question is what is the default behavior of the conversion of two UDT Source and Target if the library is not aware of. I it return Target(val) or Target res; ios << val; ios >> res; return res; ? IMO this difference in behavior merits two different functions. Best, Vicente

Vicente BOTET <vicente.botet <at> wanadoo.fr> writes: ... I suspect you have not understood my concern. The question is what is the default behavior of the conversion of two UDT Source and Target if the library is not aware of.
Is it
return Target(val)
or
Target res; ios << val; ios >> res; return res;
?
IMO this difference in behavior merits two different functions.
The second approach of converting two UDTs (which could be done with lexical_cast) never made sense to me. It still looks bizarre and I doubt anyone deployed it ever. lexical_cast stream-related business is sensible as long as the string type is in the picture. In fact, I do not believe the ios-based conversion of 2 UDTs is by design. I see it more of a side-effect of lexical_cast stream-based approach to to_string from-string conversions. After all, it's not called *lexical* for nothing. The original 'convert' did not have the second behavior... well, it did not have the first -- Target(val) -- either. :-) That generalization seems sensible. V.

Message du 10/05/11 23:50 De : "Vladimir Batov" A : boost@lists.boost.org Copie à : Objet : Re: [boost] [review] string convert
Vicente BOTET wanadoo.fr> writes: ... I suspect you have not understood my concern. The question is what is the default behavior of the conversion of two UDT Source and Target if the library is not aware of.
Is it
return Target(val)
or
Target res; ios << val; ios >> res; return res;
?
IMO this difference in behavior merits two different functions.
The second approach of converting two UDTs (which could be done with lexical_cast) never made sense to me. It still looks bizarre and I doubt anyone deployed it ever. lexical_cast stream-related business is sensible as long as the string type is in the picture. In fact, I do not believe the ios-based conversion of 2 UDTs is by design. I see it more of a side-effect of lexical_cast stream-based approach to to_string from-string conversions. After all, it's not called *lexical* for nothing. The original 'convert' did not have the second behavior... well, it did not have the first -- Target(val) -- either. :-) That generalization seems sensible.
Do you mean that is without a specialization is a compile error in the original library? What are your project for the future library? Best, Vicente

Vicente BOTET <vicente.botet <at> wanadoo.fr> writes:
De : "Vladimir Batov" ...The original 'convert' did not have the second behavior... well, it did not have the first -- Target(val) -- either.
Do you mean that is without a specialization is a compile error in the original library?
Yes. My submission dealt with to-string and from-string and left the door open for type-to-type conversions *if* deemed useful. I personally never needed it so far.
What are your project for the future library?
Uhm, not sure what you mean. Is it "what are you proposing for the future library"? Or "What will be your next project?". Or something completely else? V.

Vladimir Batov wrote:
Stewart, Robert <Robert.Stewart <at> sig.com> writes:
T convert::to<T>(S, boost::parameter-list);
Vicente has been discussing the issue of stream-based conversions WRT manipulators. In lexical_cast-style conversions, there's an insertion followed by an extraction. Are manipulators needed for both directions? If so, the interface must allow for them using, say, "in" and "out" keywords instead of "format_."
There has been no need for that. The original 'convert' was applying manipulators in both directions. The in/out direction is defined by the string-to-type or type-to-string specialization.
That came up in my reply to Matt. I think Matt and I agree with you. Unless Vicente can find a use case, the manipulators-apply-to-the-string view is sufficient.
to<T>() seems a very nice name for stream-based conversions.
I am not sure why that distinction has to be made. Now that converters are gone
That isn't clear yet.
and all parameters are provided in one call, the implementer knows what conversion is needed. That is, if there are no 'format_' parameters specified, then, say, spirit-based quick conversion is applied. As soon as I see 'format_', then I fall back to stream-based. So, all we seem to need is convert::to.
Stream-based and non-stream-based conversions can easily provide different results, without using manipulators to influence things, if for no reason other than the global locale's influence. It seems useful to be able to explicitly invoke a stream-based conversion without specifying manipulators. Whether the stream-based conversion API uses a different set of names or is in a distinct namespace is a separate matter.
This leaves the fate of the converter function objects unclear. That functionality seems too useful to drop.
I am not sure converters are needed as we can feed convert::to() to algorithms with 'bind' or 'lambda' .
That's a fair point. However, I think there's still value in the converters because while the function templates take advantage of template parameter deduction, a class template (converter) can use partial specialization. Partial specialization will probably be beneficial for customizations. If you accept that, then making the converters function objects is a small step.
To summarize, here's the complete API as I see it now:
1. convert::default_value<T> customization point * Default value, if needed, when not otherwise supplied
I expressed my opposition to default_value. It was not needed for the original 'convert', i.e. we can get away without it. Asking the user to supply this arbitrary functionality is bothersome and will be mis-used/abused.
The interfaces that obviated default_value were questioned, however. Using default_value makes various interfaces simpler. One isn't required to use it, however. There are API choices that don't rely on it.
3. T convert::convert_cast<T,S>(S) * Throws on failure unless T is convert::result<T'> * Uses convert::converter<T,S> to do conversion
T convert::to<T>(s) seems to work as well. Less API to remember.
I like to<T>(S) very much, but it doesn't lend itself as well to all of the API variations. I know you don't care to have the try_convert_to(S, T &) interface, but others find value in it, so accounting for its name is necessary.
4. T convert::convert_cast<T,S>(S, T _fallback) * Returns _fallback on failure unless T is convert::result<T'> * Uses convert::converter<T,S> to do conversion
T convert::to<T>(s, (fallback_ = ...)) seems to work as well. Less API to remember.
Writing "(fallback_ = _fallback)" seems harder to remember than that there's an overload taking a second argument.
(I user convert::to instead of convert::as. Not sure if convert::as was intentional or accidental).
It was intentional because it allowed for #9. as<int>(s) reads about as well as to<int>(s).
9. bool convert::try_as<T,S>(S, T & _value, options) * Returns false and leaves _value unchanged on failure * Uses convert::stream_converter<T,S> to do conversion
That's a lot. I'd seem to manage with just convert::to. :-) ... even though it did not make it onto the list.
If we could agree on "as" rather than "to," it might be workable because "try_as" works reasonably well for the interface you dislike. IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

Stewart, Robert <Robert.Stewart <at> sig.com> writes:
Vladimir Batov wrote: ... and all parameters are provided in one call, the implementer knows what conversion is needed. That is, if there are no 'format_' parameters specified, then, say, spirit-based quick conversion is applied. As soon as I see 'format_', then I fall back to stream-based. So, all we seem to need is convert::to.
Stream-based and non-stream-based conversions can easily provide different results, without using manipulators to influence things, if for no reason other than the global locale's influence. It seems useful to be able to explicitly invoke a stream-based conversion without specifying manipulators. Whether the stream-based conversion API uses a different set of names or is in a distinct namespace is a separate matter.
Hmm, I forgot locales. I guess, quick spirit conversion does not support locales (need to check). Then, every time we see locale_, format_ we switch to stream-based. :-) I am somewhat uneasy about providing stream- and non-stream-based conversions. I feel that an important value 'conversion library is to decide *for* the user the quickest path. And different results are fine IMO -- we specified locale_, format_, etc. after all... as long as it's consistent.
I am not sure converters are needed as we can feed convert::to() to algorithms with 'bind' or 'lambda' .
That's a fair point...
More so, it'll see far less opposition. :-)
T convert::to<T>(s, (fallback_ = ...)) seems to work as well. Less API to remember.
Writing "(fallback_ = _fallback)" seems harder to remember than that there's an overload taking a second argument.
I myself actually much prefer T convert::to<T>(s, fallback)
(I user convert::to instead of convert::as. Not sure if convert::as was intentional or accidental).
It was intentional because it allowed for #9.
as<int>(s) reads about as well as to<int>(s).
I am certainly not a native speaker but "convert to" feels considerably more natural than "convert as". T convert::to<T,S>(S, T& fallback, options) bool convert::try_as<T,S>(S, T & _value, options) I personally cannot see anything wrong with the above. The name difference seems justified -- their behavior are quite different. So, the further they are apart name-wise the better.
That's a lot. I'd seem to manage with just convert::to. ... even though it did not make it onto the list.
If we could agree on "as" rather than "to," it might be workable because "try_as" works reasonably well for the interface you dislike.
So, again, T convert::to<T,S>(S, T& fallback, options) bool convert::try_as<T,S>(S, T & _value, options) --------------- Something old. I am again having doubts if specializations will work well for struct convert { template<T, S, Enable> to(S const&, identity<T>::type const* =...); }; I did look at Vicente's approach and it still (as the original 'convert') boils down to the "tmpl<> class { tmpl<> func }" construct. The problem is that some partial specializations are not such in the strict definition of the concept. Namely, string-to-type conversion of the original 'convert' had to be done (IMO) with 'enable_if' as it was not a partial specialization as such but rather a particular *implementation* for a group of string-related classes. Then, string-to-bool provided another implementation -- actual specialization -- template<> struct convert<bool>. I am under impression that such a scenario will not work with the above (where T and S are together) as I suspect it won't work with Vicente's library either. In other words, I am again having suspicions that T and S need to be separated, i.e. template<T> struct convert { template<S> from(S const&, ...); }; I guess, I need to find time to actually try it again. Vicente, if you are reading this, do you actually have a working example of semi-generic string-to-any-type "specialization" and, then string-to-bool complete specialization? V. V.

Message du 11/05/11 01:18 De : "Vladimir Batov" A : boost@lists.boost.org Copie à : Objet : Re: [boost] [review] string convert
Something old. I am again having doubts if specializations will work well for
struct convert { template to(S const&, identity::type const* =...); };
Maybe you should specialize the converter instead of the function to ;-) as c++ desn't allows to specialize the preceding schema.
I did look at Vicente's approach and it still (as the original 'convert') boils down to the "tmpl<> class { tmpl<> func }" construct.
No. Boost.Conversion doesn't partially specialize the inner function.
The problem is that some partial specializations are not such in the strict definition of the concept. Namely, string-to-type conversion of the original 'convert' had to be done (IMO) with 'enable_if' as it was not a partial specialization as such but rather a particular *implementation* for a group of string-related classes. Then, string-to-bool provided another implementation -- actual specialization -- template<> struct convert. I am under impression that such a scenario will not work with the above (where T and S are together) as I suspect it won't work with Vicente's library either. In other words, I am again having suspicions that T and S need to be separated, i.e.
template struct convert { template from(S const&, ...); };
I guess, I need to find time to actually try it again.
Vicente, if you are reading this, do you actually have a working example of semi-generic string-to-any-type "specialization" and, then string-to-bool complete specialization?
Boost.Conversion is based on two configuration points. First overloading of convert_to in a namespace reachable using ADL and second partial template specialization of a convert_to functor (with two type template parameters Target and Source) for the case where there are some issues with function template ordering or for std types (as we can not add any thing to the std namespace). The library contains a lot of examples of partial specializations as for example the optional conversions. I have added a dummy complete specialization for std::string, bool and everything is OK. Best, Vicente

Vicente BOTET <vicente.botet <at> wanadoo.fr> writes:
... and second partial template specialization of a convert_to functor (with two type template parameters Target and Source) ...
The library contains a lot of examples of partial specializations as for example the optional conversions. I have added a dummy complete specialization for std::string, bool and everything is OK.
The problem: there may be two independent specialization streams -- by the Source type and by the Target type. For example, convert_to<T, char const*> convert_to<T, wchar_t const*> convert_to<T, string const&> convert_to<T, wstring const&> convert_to<T, char []> string-to-type share the same implementation of a partial specialization for the Source type -- a string-like type. Then, one wants to specialize/optimize by the Target type. Say, specifically for bool and tries to provide convert_to<bool, int> convert_to<bool, long int> convert_to<bool, short int> convert_to<bool, string-like-type> The latter bool-to-string-like-type is a partial specialization again as it takes any string-like type (char const*, std::string, etc.). Now type-to-string and bool-to-string partial specializations seem to match/clash for bool v = convert_to<bool>("false"); I vaguely remember having that issue 2-3 years back. That's why I had to split Target and Source types by convert<T>::from(S). I did look at your examples/tests in the conversion/libs/test directory. They all seem to be complete specializations for a particular Target type. The std/string.hpp does not cover the above-mentioned use-case as it deals with type-to-std::string (again specialization by Target type) conversion. That is, I did not find any specializations by the Source type like any-string-to-type conversion tests/examples. That might be the reason you have not had the problem that I described. :-) V.

Message du 12/05/11 00:21 De : "Vladimir Batov" A : boost@lists.boost.org Copie à : Objet : Re: [boost] [review] string convert
Vicente BOTET wanadoo.fr> writes:
... and second partial template specialization of a convert_to functor (with two type template parameters Target and Source) ...
The library contains a lot of examples of partial specializations as for example the optional conversions. I have added a dummy complete specialization for std::string, bool and everything is OK.
The problem: there may be two independent specialization streams -- by the Source type and by the Target type. For example,
convert_to convert_to convert_to convert_to convert_to
string-to-type share the same implementation of a partial specialization for the Source type -- a string-like type. Then, one wants to specialize/optimize by the Target type. Say, specifically for bool and tries to provide
convert_to convert_to convert_to convert_to
The latter bool-to-string-like-type is a partial specialization again as it takes any string-like type (char const*, std::string, etc.).
Now type-to-string and bool-to-string partial specializations seem to match/clash for
bool v = convert_to("false");
I vaguely remember having that issue 2-3 years back. That's why I had to split Target and Source types by convert::from(S).
I did look at your examples/tests in the conversion/libs/test directory. They all seem to be complete specializations for a particular Target type. The std/string.hpp does not cover the above-mentioned use-case as it deals with type-to-std::string (again specialization by Target type) conversion. That is, I did not find any specializations by the Source type like any-string-to-type conversion tests/examples. That might be the reason you have not had the problem that I described. :-)
As I said
I have added a dummy complete specialization for std::string, bool and everything is OK.
I have committed the added changes so you can play with, even if it is not useful for the library as the behavior is not specialized. You can update the sandbox now to check and play with. Best, Vicente

"Stewart, Robert" <Robert.Stewart@sig.com> wrote
In another post, I offered the following interface:
1. T convert_cast<T,S>(S) 2. T convert_cast<T,S>(S, T) 3. T convert_cast<T,S>(S, T, nothrow_t) 4. optional<T> try_convert_cast<T,S>(S) 5. optional<T> try_convert_cast<T,S>(S, T)
In 1, the result valid unless there's an exception. It doesn't work for non-DefaultConstructible UDTs. In 2, the result is always valid since it never throws. It works for non-DefaultConstructible UDTs, but not for types without a suitable fallback value.
#2 seems to have to be 2. T convert_cast<T,S,U>(S, U, identity<T>::type) Where U is the type of the fallback value. It obviously can be different from the target type. For example, // fallback is char const* std:string str = convert_cast<std::string>(123, "failed"); // fallback is int double d = convert_cast<double>("123", 0);
In 3, the result is valid unless there's an exception. 2 and 3 could be merged, but keeping them distinct is probably more efficient and should be easier to document.
As written at the top up #2 and #3 seem to do the same thing. Namely, both do not throw (with nothrow_t in #3) and return fallback on failure. Above though you say "In 3, the result is valid unless there's an exception". Could that be that you meant 3. T convert_cast<T,S>(S, T, DOthrow_t) If so, it'll be something like the following, right? 3. T convert_cast<T,S,U>(S, U, DOthrow_t, identity<T>::type)
In 4 and 5, the optional is set iff the conversion succeeds. 4 is useful for built-in types and DefaultConstructible UDTs. 5 is useful for all types.
#4 seems to be a non-throwing version of #1, right? #1 and #4 seem like a nice complementary pair for DefaultConstructible types and tasks currently covered "natively" by lexical_cast but with the choice of non-throwing (already a plus). Then, as a user I instinctively try pairing up #2 and #5 and fail. #1 and #4 exhibit the same behavior but throws and not. #2 does not throw already. Then, I think #5 must complement #2 somehow differently. #2 does not indicate failure. Then, #5 probably does. Yes, it does... but #5 *never* returns the fallback value (as #2 does) that I provided (which might be seen as surprising). The fact is that #5 is not really a comlepement for #2. More so, the supplied value is not a fallback either but to work-around #4 limitations (for non-DefaultConstructible types). IMHO that situation (when the provided value is sometimes a fallback sometimes not) could be potentially quite surprising for the user. Maybe to avoid the temptation of pairing up we could simplify (at least from the user perspectives) down to: 1. T convert_cast<T,S>(S) 2. T convert_cast<T,S>(S, T, throw_t =nothrow) 5. optional<T> try_convert_cast<T,S>(S, T =T()) Still, the difference in the meaning of the second parameter in #2 and #5 makes me feel uncomfortable.
Your use case is the following, right?
optional<int> o(try_convert_cast<int>(str)); int i; if (!o) { std::cout << "using fallback\n"; i = fallback; } else { i = o.get(); }
That's clearly inconvenient and 5 doesn't apply because the optional won't be set if the conversion fails. To address such inconvenience, you offered convert<T>::result and pair<optional<T>,bool> has been suggested. Isn't optional redundant in the latter?
Yes, indeed, std::optional inside std::pair is/was redundant. ... Purely for brevity I snipped your example which was explaining redundancy of optional.
I noted one missing use case:
What's missing, then, is getting an exception when a conversion to a non-DefaultConstructible UDT or a type with no fallback value fails. That is, a variant of 2 that throws on conversion failure.
Possibly, that would just mean:
2a. T convert_cast<T, S>(S, T, throw_t)
I am under impression that the following covers all use-cases (known to me so far) 1. T convert_cast<T,S>(S) 2. T convert_cast<T,S>(S, T, throw_t =nothrow) 6. std::pair<T, bool> try_convert_cast<T,S>(S, T =T()) #1 is straightforward lexical_cast replacement. #2 takes fallback and returns it. Optionally throws on request. #6 replaces the original #5. Never throws. In fact, it is not unreasonable (as you mentioned) to say that we provide a building block library. That way we'll only need to supply #6 as #1 and #2 are easily covered: #1 std::pair<T, bool> res = try_convert_cast<T>(s); If (!res.second) throw an ex. of your choice #2 T v = try_convert_cast<T>(s).first; [...] Snipped some more. It's 1AM and I want to get to the bottom of it. :-)
If we use Vicente's default_value customization point, however, things could be interpreted a little differently:
a) T convert_cast<T,S>(S) b) T convert_cast<T,S>(S, T) c) optional<T> try_convert_cast<T,S>(S) d) optional<T> try_convert_cast<T,S>(S, T)
a) Uses default_value<T>, if needed, and throws on conversion failure. b) Conversion failure implies returning the second argument's value. c) Uses default_value<T>, if needed, and the return value is not set on conversion failure. This still needs a better name. d) Conversion failure implies returning the second argument's value. This still needs a better name.
Given the relative rarity of types that need a special "default" value, and the fact that a compilation error on a line in the primary specialization of default_value can lead the library user to a comment that explains the need to specialize it for T, the regularity of a-d is compelling.
It's probably getting late and my brain is not working but I cannot help thinking that the three choices I mentioned above (or maybe even just one) would cover all the cases. I'll revisit it again.
Three things are missing from a-d. One is formatting control. However, that can be done through extra arguments as suggested in various other posts. It may be that there would even be overloads of a-d that take the additional arguments in order to streamline the simpler cases.
Supplying formatting, locales, etc. might be done with boost::parameter. It takes some getting used to though.
Another thing missing from a-d is the function object you had in convert<T>::converter<S>, IIRC. Is there any reason that cannot just be captured by converter<T,S>? For simplicity of customizing the library, I'd even expect that a-d would use a converter<T,S>.
The main reason the converter existed was the need to gather configuration information before applying actual conversion as the configuration process (providing locale, manipulators, etc.) was gradual. With this design I feel all the information -- value-in, fallback-value, configuration -- needs to be supplied in one long "sausage". It could have a signature similar to T convert_cast<T,S>(S, (boost::parameter-list), identity<T>::type) i.e. int v = convert_cast<int>("FF", (format_ = std::hex, locale_ = locale, throw_ = yes)) I think it's quite acceptable. As for feeding all that to algorithms, then OTOH I am not sure how to achieve that as we'll need a proxy holding all configuration parameters together.
Finally, the function to address your use case is missing. It needs a suitable name, too.
Well, it's not exactly *my* case. I happen to bring it forward as some people who do not deal with, say, XML parsing tend to find it surprising when for XML it's quite standard processing (where an element/attribute are optional but one needs something (fallback) to proceed anyway).
Summarizing, then:
- default_value<T> customization point - converter<T,S> customization point; main logic - T convert_cast<T,S>(S, formatting = none); can throw - T convert_cast<T,S>(S, T, formatting = none) - optional<T> name_me_1<T,S>(S, formatting = none) - optional<T> name_me_1<T,S>(S, T, formatting = none) - pair<T,int> name_me_2<T,S>(S, T, formatting = none)
Note that the T argument for convert_cast<T,S>(S, T, formatting) is non-deducible in order to require specifying T in each call. Since the name_me_1 and name_me_2 names are not expected to end with "_cast," the T arguments may be deducible.
1) Again, as I mentioned before (which can be wrong) I feel that the list of functions can be reduced maybe even down to just one (returning std::pair) if we want to keep the lib. minimal and consider additional functionality (like throwing) to be orthogonal and outside of the scope of the lib. 2) I suspect T can not be deducible in any of the cases above as I might provide "char const*" type as a fallback and expect std::string in return. Going to bed now, V.

Message du 05/05/11 16:52 De : "Vladimir Batov" A : boost@lists.boost.org Copie à : Objet : Re: [boost] [review] string convert
"Stewart, Robert" wrote
Maybe to avoid the temptation of pairing up we could simplify (at least from the user perspectives) down to:
1. T convert_cast(S) 2. T convert_cast(S, T, throw_t =nothrow)
I don't see why (2) the user could want to throw when is giving a fallback. So the throw_t =nothrow is not needed. Best, Vicente

Vicente BOTET wrote:
Message du 05/05/11 16:52 De : "Vladimir Batov" A : boost@lists.boost.org Copie à : Objet : Re: [boost] [review] string convert
"Stewart, Robert" wrote
Maybe to avoid the temptation of pairing up we could simplify (at least from the user perspectives) down to:
1. T convert_cast(S) 2. T convert_cast(S, T, throw_t =nothrow)
I don't see why (2) the user could want to throw when is giving a fallback. So the throw_t =nothrow is not needed.
+1 Jeff

Vicente BOTET wrote:
De : "Vladimir Batov"
"Stewart, Robert" wrote
Maybe to avoid the temptation of pairing up we could simplify (at least from the user perspectives) down to:
1. T convert_cast(S) 2. T convert_cast(S, T, throw_t =nothrow)
I don't see why (2) the user could want to throw when is giving a fallback. So the throw_t =nothrow is not needed.
That's the non-DefaultConstructible, no-sentinel use case: struct example { example(int); }; To get an exception on conversion failure, one writes: example const e(convert_cast<example>("1")); // exception However, without the default_value CP, that fails because example is non-DefaultConstructible. One can avoid the exception by providing a fallback value: example const e(convert_cast<example>("1", 3)); // no exception (I'm assuming the fallback value can be of type U implicitly convertible to T.) In that case, e == example(3). But if one wants an exception to indicate conversion failure, an initial value is needed and the desire for an exception must be noted: example const e(convert_cast<example>("1", 3, throw_)); // exception Obviously, with your default_value CP, the second argument isn't needed for the last use case: template <> default_value<example> { static example apply() { return example(3); } }; example const e(convert_cast<example>("1")); // exception HTH, _____ Rob Stewart robert.stewart@sig.com Software Engineer using std::disclaimer; Dev Tools & Components Susquehanna International Group, LLP http://www.sig.com IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

Stewart, Robert wrote:
Vicente BOTET wrote:
De : "Vladimir Batov"
"Stewart, Robert" wrote Maybe to avoid the temptation of pairing up we could simplify (at least from the user perspectives) down to:
1. T convert_cast(S) 2. T convert_cast(S, T, throw_t =nothrow) I don't see why (2) the user could want to throw when is giving a fallback. So the throw_t =nothrow is not needed.
That's the non-DefaultConstructible, no-sentinel use case:
struct example { example(int); };
To get an exception on conversion failure, one writes:
example const e(convert_cast<example>("1")); // exception
However, without the default_value CP, that fails because example is non-DefaultConstructible.
One can avoid the exception by providing a fallback value:
example const e(convert_cast<example>("1", 3)); // no exception
(I'm assuming the fallback value can be of type U implicitly convertible to T.)
In that case, e == example(3).
But if one wants an exception to indicate conversion failure, an initial value is needed and the desire for an exception must be noted:
example const e(convert_cast<example>("1", 3, throw_)); // exception
Sorry if I'm just being dense, but if the above throws, the default value is never used, so why supply it?
Obviously, with your default_value CP, the second argument isn't needed for the last use case:
template <> default_value<example> { static example apply() { return example(3); } };
example const e(convert_cast<example>("1")); // exception
So it's really just the no-default function that needs to be told to throw or return an optional. Jeff

Jeff Flinn wrote:
Stewart, Robert wrote:
Vicente BOTET wrote:
De : "Vladimir Batov"
"Stewart, Robert" wrote
Maybe to avoid the temptation of pairing up we could simplify (at least from the user perspectives) down to:
1. T convert_cast(S) 2. T convert_cast(S, T, throw_t =nothrow) I don't see why (2) the user could want to throw when is giving a fallback. So the throw_t =nothrow is not needed.
That's the non-DefaultConstructible, no-sentinel use case:
struct example { example(int); };
If one wants an exception to indicate conversion failure, an initial value is needed and the desire for an exception must be noted:
example const e(convert_cast<example>("1", 3, throw_));
Sorry if I'm just being dense, but if the above throws, the default value is never used, so why supply it?
Think about how convert_cast() might be implemented: template <class T, class S, formatting-stuff> T convert_cast(S _source, formatting-stuff _formatting) { T result; std::stringstream s; s << _source; s >> _formatting >> result; if (!s) { throw something; } return result; } Since example cannot be default constructed, you'd need: template <class T, class S, class U, formatting-stuff> T convert_cast(S _source, U value, formatting-stuff _formatting, throw_t) { std::stringstream s; s << _source; s >> _formatting >> value; if (!s) { throw something; } return value; }
Obviously, with your default_value CP, the second argument isn't needed for the last use case:
template <> default_value<example> { static example apply() { return example(3); } };
example const e(convert_cast<example>("1")); // exception
So it's really just the no-default function that needs to be told to throw or return an optional.
Yes. _____ Rob Stewart robert.stewart@sig.com Software Engineer using std::disclaimer; Dev Tools & Components Susquehanna International Group, LLP http://www.sig.com IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

On 05/05/2011 10:32 AM, Stewart, Robert wrote:
Jeff Flinn wrote:
Stewart, Robert wrote:
Vicente BOTET wrote:
De : "Vladimir Batov"
"Stewart, Robert" wrote
Maybe to avoid the temptation of pairing up we could simplify (at least from the user perspectives) down to:
1. T convert_cast(S) 2. T convert_cast(S, T, throw_t =nothrow) I don't see why (2) the user could want to throw when is giving a fallback. So the throw_t =nothrow is not needed.
That's the non-DefaultConstructible, no-sentinel use case:
struct example { example(int); };
If one wants an exception to indicate conversion failure, an initial value is needed and the desire for an exception must be noted:
example const e(convert_cast<example>("1", 3, throw_));
Sorry if I'm just being dense, but if the above throws, the default value is never used, so why supply it?
Think about how convert_cast() might be implemented:
[snip] Is conflating the fallback value with the initial value used by a stringstream-based implementation really a good idea?

Jeremy Maitin-Shepard wrote:
On 05/05/2011 10:32 AM, Stewart, Robert wrote:
Jeff Flinn wrote:
Stewart, Robert wrote:
If one wants an exception to indicate conversion failure, an initial value is needed and the desire for an exception must be noted:
example const e(convert_cast<example>("1", 3, throw_));
Sorry if I'm just being dense, but if the above throws, the default value is never used, so why supply it?
Think about how convert_cast() might be implemented:
[snip]
Is conflating the fallback value with the initial value used by a stringstream-based implementation really a good idea?
In that overload, there is no fallback value. It is the initial value. In a nothrow overload, the two would be conflated. However, the default_value CP eliminates this need. _____ Rob Stewart robert.stewart@sig.com Software Engineer using std::disclaimer; Dev Tools & Components Susquehanna International Group, LLP http://www.sig.com IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

Message du 05/05/11 18:58 De : "Stewart, Robert" A : "'boost@lists.boost.org'" Copie à : Objet : Re: [boost] [review] string convert
Vicente BOTET wrote:
De : "Vladimir Batov"
"Stewart, Robert" wrote
Maybe to avoid the temptation of pairing up we could simplify (at least from the user perspectives) down to:
1. T convert_cast(S) 2. T convert_cast(S, T, throw_t =nothrow)
I don't see why (2) the user could want to throw when is giving a fallback. So the throw_t =nothrow is not needed.
That's the non-DefaultConstructible, no-sentinel use case:
Sorry. I believed we had a consensus in using the default_value CP so I was taking the additional T parameter as fallback. Could we state if the default_value CP is adopted? How think is not a good thing and why? Best, Vicente

Vicente BOTET <vicente.botet <at> wanadoo.fr> writes:
Sorry. I believed we had a consensus ...
Did you actually say 'consensus'? LOL
Could we state if the default_value CP is adopted? How think is not a good thing and why?
I am certainly looking at the idea from my original 'convert' point of view where such a thing was not needed. I find the need to write something like template <> default_value<example> { static example apply() { return example(3); } }; quite bothersome as I know I really do not have to. In the original wacky design the fallback and the default were synonyms. Now you are introducing an additional piece of machinery/code that is needed to deploy a class with conversion and I have to provide the default and the fallback. With that in mind I am not sure I am personally warming up for default_value concept yet. V.

On 5/5/2011 4:35 PM, Vladimir Batov wrote:
Vicente BOTET<vicente.botet<at> wanadoo.fr> writes:
Sorry. I believed we had a consensus ...
Did you actually say 'consensus'? LOL
Could we state if the default_value CP is adopted? How think is not a good thing and why?
I am certainly looking at the idea from my original 'convert' point of view where such a thing was not needed. I find the need to write something like
template<> default_value<example> { static example apply() { return example(3); } };
quite bothersome as I know I really do not have to. In the original wacky design the fallback and the default were synonyms. Now you are introducing an additional piece of machinery/code that is needed to deploy a class with conversion and I have to provide the default and the fallback. With that in mind I am not sure I am personally warming up for default_value concept yet.
Did you ever use boost.convert on a non-defaultable type? I've never needed that. -Matt

Matthew Chambers <matt.chambers42 <at> gmail.com> writes:
Did you ever use boost.convert on a non-defaultable type? I've never needed that.
It might come a surprise but non-defaultable types are the overwhelming majority of our classes. Easily 9:1. And I'd like to stress *our* classes and not just wacko-Vladimir's. And, yes, 'convert' is used well beyond ints and the like. The direction class in 'convert' documentation is a real class and we have plenty of similar ones not to mention considerably more complex. An enumerator as simple as enum hamlet_problem { to_be, not_to_be }; has no default. I am not sure if extending the above to enum hamlet_problem { to_be, not_to_be, i_dont_know }; will be helpful for poor Hamlet. From the design perspectives adding elements not relevant to the domain (i_dont_know) is pollution. I won't be surprised if you find a lot of non-defaultable types if you look closely around without pre-disposition that any class is supposed to have the default constructor. One needs to remember that the default constructor was introduced in C++ (if my memory serves me) so that objects would not be in an invalid state like 'int i;' can be. If one introduces the default constructor which does not, in fact, construct a valid object, then it seems to defeat the purpose. V.

Message du 06/05/11 02:00 De : "Vladimir Batov" A : boost@lists.boost.org Copie à : Objet : Re: [boost] [review] string convert
Matthew Chambers gmail.com> writes:
Did you ever use boost.convert on a non-defaultable type? I've never needed that.
It might come a surprise but non-defaultable types are the overwhelming majority of our classes. Easily 9:1. And I'd like to stress *our* classes and not just wacko-Vladimir's. And, yes, 'convert' is used well beyond ints and the like. The direction class in 'convert' documentation is a real class and we have plenty of similar ones not to mention considerably more complex. An enumerator as simple as
enum hamlet_problem { to_be, not_to_be };
has no default. I am not sure if extending the above to
enum hamlet_problem { to_be, not_to_be, i_dont_know };
will be helpful for poor Hamlet.
Think on bool. What is the value of bool()?
From the design perspectives adding elements not relevant to the domain (i_dont_know) is pollution.
I won't be surprised if you find a lot of non-defaultable types if you look closely around without pre-disposition that any class is supposed to have the default constructor. One needs to remember that the default constructor was introduced in C++ (if my memory serves me) so that objects would not be in an invalid state like 'int i;' can be. If one introduces the default constructor which does not, in fact, construct a valid object, then it seems to defeat the purpose.
I agree completely. Don't do that. Best, Vicente

Matthew Chambers<matt.chambers42<at> gmail.com> writes:
Did you ever use boost.convert on a non-defaultable type? I've never needed that. [...snip...] An enumerator as simple as
enum hamlet_problem { to_be, not_to_be };
has no default. I am not sure if extending the above to
enum hamlet_problem { to_be, not_to_be, i_dont_know };
will be helpful for poor Hamlet. From the design perspectives adding elements not relevant to the domain (i_dont_know) is pollution. I agree that in some cases there is no sensible default/fallback value. In those cases, the only concern is whether the conversion succeeded, right? So optional<T> will work fine for that case, but it would have to be renamed so it doesn't conflict with the simple, throwing convert_cast<T>(s):
On 5/5/2011 6:58 PM, Vladimir Batov wrote: optional<hamlet_problem> t = optional_convert_to<hamlet_problem>(s); This can be in addition to try_convert_to, which would still be useful when both the conversion success and the final value (fallback or not) are wanted. In an earlier message you wrote this about the try_convert_to method:
#5 IMO can. It deploys the Pascal-style parameter passing and modifications. I remember reading Stroustrup (I think) long time ago advising against passing non-const references and I personally agree. That's due to potential confusion and wrong expectations. I am not aware of any function in std and boost doing that. Introducing such a precedent might be a hard-sell. If you want a precedent for taking output as a by-reference parameter: boost::algorithm::split. Further, I'm not sure how output references could be considered more confusing than the output iterators that are ubiquitous in std algorithms.
Outside the enum case, have you considered that your use of convert on so many object types mixes lexical conversion and serialization (admittedly related concepts)? In my experience, the former uses a simple, concise interface intended for value types while the latter uses a verbose interface supporting both object types and value types. What's the rationale for a lexical conversion library to support serialization? -Matt

From: "Matt Chambers" <matt.chambers42@gmail.com> ... I agree that in some cases there is no sensible default/fallback value. In those cases, the only concern is whether the conversion succeeded, right? So optional<T> will work fine for that case, but it would have to be renamed so it doesn't conflict with the simple, throwing convert_cast<T>(s): optional<hamlet_problem> t = optional_convert_to<hamlet_problem>(s); This can be in addition to try_convert_to, which would still be useful when both the conversion success and the final value (fallback or not) are wanted.
Again, my typical concern is twofold -- result of conversion and the return value (converted or fallback). The following is my typical use-case (note the second -- to_be -- parameter that you seemingly missed in your example) optional<hamlet_problem> t = optional_convert_to<hamlet_problem>(s, to_be); if (!t) { message("Invalid input. Using fallback"); t = to_be; } Given I might have a dozen of configuration parameters read, I would very much like have it all done with as few lines as possible: pair<hamlet_problem, bool> res1 = optional_convert_to<hamlet_problem>(s, to_be); pair<my_problem, bool> res2 = optional_convert_to<hamlet_problem>(s, why_bother); if (!res1.second) message("Invalid input ... Using fallback"); if (!res2.second) message("Invalid input ... Using fallback"); ... proceed with res1.first which is to_be; ... proceed with res2.first which is why_bother;
In an earlier message you wrote this about the try_convert_to method:
#5 IMO can. It deploys the Pascal-style parameter passing and modifications. I remember reading Stroustrup (I think) long time ago advising against passing non-const references and I personally agree. That's due to potential confusion and wrong expectations. I am not aware of any function in std and boost doing that. Introducing such a precedent might be a hard-sell. If you want a precedent for taking output as a by-reference parameter: boost::algorithm::split.
Great. :-)
Further, I'm not sure how output references could be considered more confusing than the output iterators that are ubiquitous in std algorithms.
Stroustrup. "The C++" #7.2 "... functions that modify call-by-reference arguments can make programs hard to read and should most often be avoided (but see 21.2.1)". I agree whole-heartedly. The output iterators that you mention fall in to the exception category covered in 21.2.1. Our fallback reference not (IMHO). I find the below quite confusing int fallback = 1; int v1 = convert_to("123", fallback); bool converted = try_convert_to<int>("123", fallback); as convert_to does not and try_convert_to does modify 'fallback'. Again, that's my preference and, in fact, I believe it's quite a common view (just see how often modifiable call-by-reference arguments are used).
Outside the enum case, have you considered that your use of convert on so many object types mixes lexical conversion and serialization (admittedly related concepts)?
Well, we indeed use lexical conversion to implement serialization, i.e. conversion to/from textual form. Is it wrong? Indeed, I was pushing that objectified enum case as an example. Primarily due to its simplicity. However, where is that edge where a class which starts as an objectified enumerator acquires enough functionality not to be considered just an objectified enumerator anymore?
In my experience, the former uses a simple, concise interface intended for value types while the latter uses a verbose interface supporting both object types and value types. What's the rationale for a lexical conversion library to support serialization?
To begin with we use lexical conversion to read from config. files. Spot on deployment of lexical conversion, right? I am not sure what you describe as a "simple, concise interface" but we find the interface of lexical_cast to be just way to clunky and insufficient. For us 'convert' is just right. Once one has a decent lexical conversion library I am not sure why he needs to restrict its use to certain cases. After all, a lexical conversion library takes care of "string-ification" of a class. When an object is "stringified", I can easily write it to a file, send it over the network, etc. Then, that object (or rather its copy) is reconstructed from that respective string. That mentioned lexical conversion library takes care of that as well and seemingly does it well. I cannot see anything specifically wrong in deploying a lexical conversion library for the task. Note: I am glad you mentioned "lexical" and "conversion" together as that's exactly how I see lexical_cast which pretends to be a cast when it is not (and I do not like pretenders). It's a converter -- it returns a completely new instance of a different type when a cast returns the same instance but retyped (I am aware that upcast returns a different pointer instance for multiple inheritance). So, I am not thrilled by the latest attempts to continue that pretending business with convert_cast name (not to mention it is an oxymoron -- convert and cast are quite different operations). Why can't things be called as they are -- 'convert' if we are converting, 'cast' is (and when) we are casting. Those are merely my views. I am not fighting anyone or anything. I am not forcing anyone to share those views and not pushing for any particular interface based on those views. Please proceed as you feel appropriate in whatever direction you want to proceed. Best, V.

Vladimir Batov wrote:
I am glad you mentioned "lexical" and "conversion" together as that's exactly how I see lexical_cast which pretends to be a cast when it is not (and I do not like pretenders). It's a converter -- it returns a completely new instance of a different type when a cast returns the same instance but retyped (I am aware that upcast returns a different pointer instance for multiple inheritance).
You have quite a mistaken notion about casts. static_cast<int>(1.235) returns an int. It does not return the double 1.235 retyped as int. lexical_cast is not pretending to be a cast. It *is* a cast.
So, I am not thrilled by the latest attempts to continue that pretending business with convert_cast name (not to mention it is an oxymoron -- convert and cast are quite different operations).
convert_cast does a proper cast. Conversion and casting are different shades of the same color. Consider static_cast<int>(1.235), which involves a conversion: "4.9 Floating-integral conversions 1 An rvalue of a floating point type can be converted to an rvalue of an integer type." _____ Rob Stewart robert.stewart@sig.com Software Engineer using std::disclaimer; Dev Tools & Components Susquehanna International Group, LLP http://www.sig.com IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

From: "Stewart, Robert" <Robert.Stewart@sig.com>
You have quite a mistaken notion about casts. static_cast<int>(1.235) returns an int. It does not return the double 1.235 retyped as int. lexical_cast is not pretending to be a cast. It *is* a cast. ... convert_cast does a proper cast. Conversion and casting are different shades of the same color. Consider static_cast<int>(1.235), which involves a conversion:
"4.9 Floating-integral conversions
1 An rvalue of a floating point type can be converted to an rvalue of an integer type."
Yes, that's a fair point. I stand corrected. Thank you. I guess, my interpretation of cast vs. conversion was based more on my expectations (the word popping up quite a bit recently) due to my personal childhood experiences deeply rooted in C with void* voidp = ... int* intp = (int*) voidp; V.

Vladimir Batov wrote:
Matthew Chambers <matt.chambers42 <at> gmail.com> writes:
Did you ever use boost.convert on a non-defaultable type? I've never needed that.
It might come a surprise but non-defaultable types are the overwhelming majority of our classes. Easily 9:1. And I'd like to stress *our* classes and not just wacko-Vladimir's. And, yes, 'convert' is used well beyond ints and the like. The direction class in 'convert' documentation is a real class and we have plenty of similar ones not to mention considerably more complex. An enumerator as simple as
enum hamlet_problem { to_be, not_to_be };
has no default. I am not sure if extending the above to
But is an enum IStreamable? Aren't you relying on implicit conversion to/from int for streaming? An int *is* default constructable. hamlet_problem shroedinger = static_cast<hamlet_problem>(xxx_cast<int>(s,0));
enum hamlet_problem { to_be, not_to_be, i_dont_know };
will be helpful for poor Hamlet. From the design perspectives adding elements not relevant to the domain (i_dont_know) is pollution.
Hey, I just successfully compiled: int main() { enum X { y = 1, z = 2 }; X x; } on MSVC8, XCode gcc4.0.1 and Codewarrior 3.2.5. So doesn't: hamlet_problem shroedinger = xxx_cast<hamlet_problem>(s,to_be); just work? I expect this standard, given the C-language lineage.
I won't be surprised if you find a lot of non-defaultable types if you look closely around without pre-disposition that any class is supposed to have the default constructor. One needs to remember that the default constructor was introduced in C++ (if my memory serves me) so that objects would not be in an invalid state like 'int i;' can be. If one introduces the default constructor which does not, in fact, construct a valid object, then it seems to defeat the purpose.
Just as another data point, looking at *our* classes, I see many more types that are not IStreamable, but are default-constructable. In looking at my own types, those that are not default-constructable would not make sense to be IStreamable. The types I find that are non-default-constructable tend to hold references to items passed as a ctor args. I'm not opposed to supporting non-default-constructable types, I'm opposed to paying the cost of a more complicated interface to support them. Jeff

From: "Jeff Flinn" <TriumphSprint2000@hotmail.com>
has no default. I am not sure if extending the above to
But is an enum IStreamable? Aren't you relying on implicit conversion to/from int for streaming? An int *is* default constructable.
You are correct. The example I gave in a haste is probably more confusing than helping. A better (and real) example of objectified enum -- the direction class -- is in the 'convert' documentation.
Just as another data point, looking at *our* classes, I see many more types that are not IStreamable, but are default-constructable. In looking at my own types, those that are not default-constructable would not make sense to be IStreamable. The types I find that are non-default-constructable tend to hold references to items passed as a ctor args.
I am not sure what to say. I guess, our domains, programming styles, etc. differ. Does it mean that one needs to be better supported than the other or another? Still, I have a suspicion (and that's just that -- a suspicion) that many default-constructors can be reasonably questioned. Even a such a simlpe "class" as int default-constructed to 0. I am tempted to ask 'why 0?' why not MAX_INT or '-1'. My point is that an object needs to be constructed explicitly with the valu it needs to be assigned to, i.e. int v(0); that is for readability, maintainability, etc. Yes, built-in type do have default constructors. However, it was not a design choice but rather the practical necessity to be able to incorporate built-in types into C++ framework. However, that technical "hack" to a legacy-support-related issue as been misunderstood and carried over to proper classes. That's my view of course and as any view it can be wrong.
I'm not opposed to supporting non-default-constructable types, I'm opposed to paying the cost of a more complicated interface to support them.
You certainly entitled to your opinion. Still, I'd expect a library to work equally hard to be equally useful for different use-cases. In reality I see it all the time that the interface is made a bit "more complicated" for a particular use-case to gain considerably wider audience by covering other use-cases. Again, opinions and experiences differ and that's healthy (I think :-) ). There is a convert_cast interface discussion gong on lead by Rob Stewart, please consider contributing your ideas and views so that there will be something tangible in the end. V.

On 05/06/2011 04:10 PM, Vladimir Batov wrote:
From: "Jeff Flinn" <TriumphSprint2000@hotmail.com>
has no default. I am not sure if extending the above to
But is an enum IStreamable? Aren't you relying on implicit conversion to/from int for streaming? An int *is* default constructable.
You are correct. The example I gave in a haste is probably more confusing than helping. A better (and real) example of objectified enum -- the direction class -- is in the 'convert' documentation.
Just as another data point, looking at *our* classes, I see many more types that are not IStreamable, but are default-constructable. In looking at my own types, those that are not default-constructable would not make sense to be IStreamable. The types I find that are non-default-constructable tend to hold references to items passed as a ctor args.
I am not sure what to say. I guess, our domains, programming styles, etc. differ. Does it mean that one needs to be better supported than the other or another?
Still, I have a suspicion (and that's just that -- a suspicion) that many default-constructors can be reasonably questioned. Even a such a simlpe "class" as int default-constructed to 0. I am tempted to ask 'why 0?' why not MAX_INT or '-1'. My point is that an object needs to be constructed explicitly with the valu it needs to be assigned to, i.e.
int v(0);
that is for readability, maintainability, etc. Yes, built-in type do have default constructors. However, it was not a design choice but rather the practical necessity to be able to incorporate built-in types into C++ framework. However, that technical "hack" to a legacy-support-related issue as been misunderstood and carried over to proper classes. That's my view of course and as any view it can be wrong.
The fact that built-in types in C++ have a special "uninitialized" state separate from the default constructed state necessary makes them a special case. Nonetheless, a default constructor is often useful even if it serves to create an arbitrary initial state with only the guarantee that it won't leak memory, etc. Basically it allows the type to serve as its own optional<>, with the caveat that you cannot check if it has been initialized and don't need to dereference it explicitly. This often is more convenient/leads to better syntax that having to use optional<> or a smart pointer of some type everywhere. For instance, arrays (and some other containers) require a default constructor. Also, if the type is a member of some other class, it may be convenient to be able to initialize it in the constructor body or elsewhere, as constructing it using the initializer list may be too limiting or inconvenient.

From: "Jeremy Maitin-Shepard" <jeremy@jeremyms.com> The fact that built-in types in C++ have a special "uninitialized" state separate from the default constructed state necessary makes them a special case. Nonetheless, a default constructor is often useful even if it serves to create an arbitrary initial state with only the guarantee that it won't leak memory, etc.
In my nick of the woods that is not a sufficient guarantee. That requires *all* the code dealing with the relevant object to have checks if that object indeed has been constructed properly and has value or it just "won't leak memory". That is a case similar to pointers vs. references debate.
Basically it allows the type to serve as its own optional<>, with the caveat that you cannot check if it has been initialized and don't need to dereference it explicitly.
When I need optional<>, I'll use optional<>.
This often is more convenient/leads to better syntax that having to use optional<> or a smart pointer of some type everywhere.
I disagree. In my view it leads to avoidable and error-prone code where I forget to check if the passed-in object has, in-fact, a valid state and proceed using it.
For instance, arrays (and some other containers) require a default constructor.
I knew containers will come up as an example sooner and later. An empty container is a valid object in a useble state. There is nothing optional about it. V.

Message du 05/05/11 23:37 De : "Vladimir Batov" A : boost@lists.boost.org Copie à : Objet : Re: [boost] [review] string convert
Vicente BOTET wanadoo.fr> writes:
Sorry. I believed we had a consensus ...
Did you actually say 'consensus'? LOL
Yes, I did. And I added I believed. I see you don't see the utility yet. But I'm sure you will see it soon..
Could we state if the default_value CP is adopted? How think is not a good thing and why?
I am certainly looking at the idea from my original 'convert' point of view where such a thing was not needed. I find the need to write something like
template <> default_value { static example apply() { return example(3); } };
quite bothersome as I know I really do not have to. In the original wacky design the fallback and the default were synonyms. Now you are introducing an additional piece of machinery/code that is needed to deploy a class with conversion and I have to provide the default and the fallback. With that in mind I am not sure I am personally warming up for default_value concept yet.
I was not talking here of the fallback. Note that you don't need to do that for the classes having a default constructor, which are most of them. Best, Vicente

Vicente BOTET <vicente.botet <at> wanadoo.fr> writes: ...
template <> default_value { static example apply() { return example(3); } };
quite bothersome as I know I really do not have to. In the original wacky design the fallback and the default were synonyms. Now you are introducing an additional piece of machinery/code that is needed to deploy a class with conversion and I have to provide the default and the fallback. With that in mind I am not sure I am personally warming up for default_value concept yet.
I was not talking here of the fallback.
Note that you don't need to do that for the classes having a default constructor, which are most of them.
Vicente, If I were proposing something for a potentially wide user-base, then I'd personally be probably cautious with "most of them" statements. The classes you happen to write/come across might indeed have default constructors. My projects do not. In fact, only a small fraction of (relatively low-level) classes will have default constructors. Our classes happen to be quite complex and require parameters to be created. Those classes cannot be instantiated with default constructors. One could argue that the default constructor *could* be still provided for those classes by creating invalid instances. I argue that that's a bad idea as from the design perspectives that code pollution to work around limitations of some other library. Even simple classes do not often have default constructors. Take the example from my 'convert' documentation -- the direction class with up and dn values. What do you consider the default constructor would do? Or you'd suggest introducing another 'not-defined' state just to be user in the default constructor? Then, it'll have far-reaching ripples through all the code as now I never know if my direction object is valid or not and, therefore, have to always write error-prone "if (dir != undefined) proceed". V.

Message du 06/05/11 01:19 De : "Vladimir Batov" A : boost@lists.boost.org Copie à : Objet : Re: [boost] [review] string convert
Vicente BOTET wanadoo.fr> writes: ...
template <> default_value { static example apply() { return example(3); } };
quite bothersome as I know I really do not have to. In the original wacky design the fallback and the default were synonyms. Now you are introducing an additional piece of machinery/code that is needed to deploy a class with conversion and I have to provide the default and the fallback. With that in mind I am not sure I am personally warming up for default_value concept yet.
I was not talking here of the fallback.
Note that you don't need to do that for the classes having a default constructor, which are most of them.
Vicente,
If I were proposing something for a potentially wide user-base, then I'd personally be probably cautious with "most of them" statements. The classes you happen to write/come across might indeed have default constructors. My projects do not. In fact, only a small fraction of (relatively low-level) classes will have default constructors. Our classes happen to be quite complex and require parameters to be created. Those classes cannot be instantiated with default constructors. One could argue that the default constructor *could* be still provided for those classes by creating invalid instances. I argue that that's a bad idea as from the design perspectives that code pollution to work around limitations of some other library.
I suspect that these complex classes you are talking off don't define the input/ouput stream operator to work with your conversion framework. Or did them? The classes I expect to be used are regular classes with value semantics. In any case, you have two options: the default value is created via a default_value function implicitly or explicitly via a constructor with parameters . As a user of a class I will prefer to don't have to create it explicitly. Of course this force the designer of the class to define a default value. Could you give some more examples of regular classes that don't have a default constructor, neither a valid instance that can be constructed by a default value function?
Even simple classes do not often have default constructors. Take the example from my 'convert' documentation -- the direction class with up and dn values. What do you consider the default constructor would do?
I could try to follow the builtin behavior. I will define one of them as 0 and init the default constructor with it.
Or you'd suggest introducing another 'not-defined' state just to be user in the default constructor? Then, it'll have far-reaching ripples through all the code as now I never know if my direction object is valid or not and, therefore, have to always write error-prone "if (dir != undefined) proceed".
No please. Do not do that. Best, Vicente

Vicente BOTET <vicente.botet <at> wanadoo.fr> writes: .. I suspect that these complex classes you are talking off don't define the input/ouput stream operator to work with your conversion framework.
Jeez, is it really that hard to accept that 'convert' works and is used with many classes complex or not. Say, a Train class is complex as it has a *lot* of stuff. We serialize/unserialize the class to/from XML using 'convert'. Train has op>>() op<<().
The classes I expect to be used are regular classes with value semantics.
Our classes with value- and pointer-semantics (pimpl-based) are as regular/normal. They just happen to be not your classes.
In any case, you have two options: the default value is created via a default_value function ... explicitly. Of course this force the designer of the class to define a default value.
I am not sure if I am being heard. I have no default values and I do not want to provide anything resembling default value as it'll be misused by the library user. In fact, I have a solution to my problem. A library called 'convert'. So, if you want me to switch to some library of yours, then you'll have to do better than asking me to go an extra mile (like writing default_value<> specializations for every class and then suffering through complains of those being misused/misunderstood/whatever).
Even simple classes do not often have default constructors. Take the example from my 'convert' documentation -- the direction class with up and dn values. What do you consider the default constructor would do?
I could try to follow the builtin behavior.
It's not immediately obvious to me why (from the design perspectives and not due to technical shortcomings) would a proper class "follow the builtin behavior".
I will define one of them as 0 and init the default constructor with it.
I know. The "builtin behavior". It's wrong but I am afraid emails are too hard a medium to discuss that.
Or you'd suggest introducing another 'not-defined' state just to be user in the default constructor? Then, it'll have far-reaching ripples through all the code as now I never know if my direction object is valid or not and, therefore, have to always write error-prone "if (dir != undefined) proceed".
No please. Do not do that.
Here we go. After all I did not need to explain anything. That default '0' you initially suggested is that 'undefined'. V.

On 05/05/2011 05:57 PM, Vladimir Batov wrote:
Vicente BOTET<vicente.botet<at> wanadoo.fr> writes: .. I suspect that these complex classes you are talking off don't define the input/ouput stream operator to work with your conversion framework.
Jeez, is it really that hard to accept that 'convert' works and is used with many classes complex or not. Say, a Train class is complex as it has a *lot* of stuff. We serialize/unserialize the class to/from XML using 'convert'. Train has op>>() op<<().
I don't like default_value as a customization point either. However, it seems that for a non-default constructable type, operator>> is likely not the ideal interface for unserialization. How would you use your unserialization functionality separately from the convert library? Presumably you would need some way to obtain a constructed instance in a form suitable for invoking operator>>. If the non-default-constructable type has any members that aren't default constructable, you would have to somehow construct the members as well.

From: "Jeremy Maitin-Shepard" <jeremy@jeremyms.com> ...
Say, a Train class is complex as it has a *lot* of stuff. We serialize/unserialize the class to/from XML using 'convert'. Train has op>>() op<<().
I don't like default_value as a customization point either. However, it seems that for a non-default constructable type, operator>> is likely not the ideal interface for unserialization.
Agreed. And you nailed it -- for unserialization we've been in fact moving towards more direct approach. As simple as a class needs to provide a specific constructor "class::class(xml::element const&)" for that purpose. I personally like it better. Less overhead; do what you mean; easier to follow. As with long-running projects is does not happen overnight especially when the old supporting framework is in place.
How would you use your unserialization functionality separately from the convert library? Presumably you would need some way to obtain a constructed instance in a form suitable for invoking operator>>. If the non-default-constructable type has any members that aren't default constructable, you would have to somehow construct the members as well.
I am not sure I understand. A non-default-constructable object is constructed in whatever way it can be constructed and then is fed to convert::from(). Like // Constructed by whatever means available. Not the def. cntr direction fallback (direction::up); convert<direction>::result res = convert<direction>::from(str, fallback); direction reconstructed_from_str = res.value(); For big classes with value-semantics it might be quite an overhead. However, our classes are overwhelmingly pointer-semantics pimpl-based classes. That way we essentially apply something that probably can be called in-place re-initialization. For me it's somewhat over-engineered and we've been moving towards "class::class(xml::element const&)" for unserialization. Still, all the current features of 'convert' are needed. They will be (hopefully) used on smaller scale though. Not sure if answered your question to your satisfaction though. :-) V.

On 5/6/2011 5:45 AM, Vladimir Batov wrote:
Again, my typical concern is twofold -- result of conversion and the return value (converted or fallback). The following is my typical use-case (note the second -- to_be -- parameter that you seemingly missed in your example)
optional<hamlet_problem> t = optional_convert_to<hamlet_problem>(s, to_be);
if (!t) { message("Invalid input. Using fallback"); t = to_be; }
[...different post...]
// Constructed by whatever means available. Not the def. cntr direction fallback (direction::up); convert<direction>::result res = convert<direction>::from(str, fallback); direction reconstructed_from_str = res.value();
For big classes with value-semantics it might be quite an overhead. However, our classes are overwhelmingly pointer-semantics pimpl-based classes. That way we essentially apply something that probably can be called in-place re-initialization. For me it's somewhat over-engineered and we've been moving towards "class::class(xml::element const&)" for unserialization. Still, all the current features of 'convert' are needed. They will be (hopefully) used on smaller scale though. Your examples are hypocritical based on your earlier argument about there not being a meaningful default value for your non-defaultable types. We agreed there is no meaningful default for the "to_be or not_to_be" problem, so your usage here of defaulting it to to_be doesn't make sense. Especially when you proceed to use the return value that's invalid...
If a default value makes sense, then you use: hamlet_problem t = to_be; if (!try_convert_to<hamlet_problem>(s, t)) { message("Invalid input. Using fallback"); } It's briefer than your version. The same goes for your direction class. We really need more context to understand your use case and why either the optional<T> optional_convert_to (without a fallback/default provided, because the value isn't needed if it fails) or try_convert_to doesn't work for you. I know your argument about the output by reference, but that is merely an interface concern which other participants here haven't yet shared. -Matt

From: "Matt Chambers" <matt.chambers42@gmail.com> ... Your examples are hypocritical based on your earlier argument about there not being a meaningful default value for your non-defaultable types. We agreed there is no meaningful default for the "to_be or not_to_be" problem, so your usage here of defaulting it to to_be doesn't make sense. Especially when you proceed to use the return value that's invalid...
Well, indeed I am such a hypocrite. :-P On the serious note though I feel we are going in circles. We've discussed that before. Thare are, in fact, two different use-cases: 1. the relevant class is not DefaultConstructible; 2. the relevant class does not have meaningful fallback. These two cases are *separate*.
Especially when you proceed to use the return value that's invalid...
That's incorrect. I proceeded to use the value of *my choice* after I found out the conversion failed. Consider the following tedious code doing the same: optional<hamlet_problem> t = optional_convert_to<hamlet_problem>(s); if (!t) { message("Invalid input. Using fallback"); t = to_be; } With "t = to_be" I *choose* to use a certain value because I have to choose something to keep application operational.
If a default value makes sense, then you use: hamlet_problem t = to_be; if (!try_convert_to<hamlet_problem>(s, t)) { message("Invalid input. Using fallback"); }
It's briefer than your version.
Yes, indeed, it is an alternative. My problem with it (and I repeat it's *my* problem) is that it uses modifiable by-reference argument passing which I personally quite dislike. Secondly I cannot pass the result of try_convert_to() to another function which could evaluate the success and do something with the value. But to be honest the second "argument" is smoke and mirrors -- I just can't stand modifiable by-reference arguments. :-)
The same goes for your direction class. We really need more context to understand your use case and why either the optional<T> optional_convert_to (without a fallback/default provided, because the value isn't needed if it fails) or try_convert_to doesn't work for you. I know your argument about the output by reference, but that is merely an interface concern which other participants here haven't yet shared.
I am pretty sure I covered all relevant use-cases in the 'convert' documentation. If you feel you've covered them all with the interface of your choice, then that's great. If that's accepted into Boost, I'll gladly use it rather that maintaining mine. That's a promise. V.

Vladimir Batov wrote:
From: "Matt Chambers" <matt.chambers42@gmail.com> ... Your examples are hypocritical based on your earlier argument about there not being a meaningful default value for your non-defaultable types. We agreed there is no meaningful default for the "to_be or not_to_be" problem, so your usage here of defaulting it to to_be doesn't make sense. Especially when you proceed to use the return value that's invalid...
Well, indeed I am such a hypocrite. :-P
Actually, no, it isn't hypocritical. There's a difference between supplying a default in a particular context and one for general use. The default_value CP Vicente suggested provides a default value for use in the context of conversions. Vladimir's I/F is based upon the idea of supplying the default at the point of call. These approaches differ from providing a default constructor that supplies a default for all contexts.
On the serious note though I feel we are going in circles. We've discussed that before. Thare are, in fact, two different use-cases:
1. the relevant class is not DefaultConstructible; 2. the relevant class does not have meaningful fallback.
These two cases are *separate*.
They can be separate or comingled, but must be addressed separately. _____ Rob Stewart robert.stewart@sig.com Software Engineer using std::disclaimer; Dev Tools & Components Susquehanna International Group, LLP http://www.sig.com IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

Message du 06/05/11 02:58 De : "Vladimir Batov" A : boost@lists.boost.org Copie à : Objet : Re: [boost] [review] string convert
Vicente BOTET wanadoo.fr> writes: .. I suspect that these complex classes you are talking off don't define the input/ouput stream operator to work with your conversion framework.
Jeez, is it really that hard to accept that 'convert' works and is used with many classes complex or not. Say, a Train class is complex as it has a *lot* of stuff. We serialize/unserialize the class to/from XML using 'convert'. Train has op>>() op<<().
The classes I expect to be used are regular classes with value semantics.
Our classes with value- and pointer-semantics (pimpl-based) are as regular/normal. They just happen to be not your classes.
When I used regular classes I was not referring to normal or abnormal classes, but what Adobe ASL and Stepanov call regular types. Sorry for the imprecision. You can assimilate a regular class as one that behaves like a built-in type. But, please see http://www.google.fr/url?sa=t&source=web&cd=1&sqi=2&ved=0CBwQFjAA&url=http%3A%2F%2Fciteseerx.ist.psu.edu%2Fviewdoc%2Fdownload%3Fdoi%3D10.1.1.94.3881%26rep%3Drep1%26type%3Dpdf&rct=j&q=regular%20type%20c%2B%2B%20stepanov%20programming&ei=AjrFTbqaHs22hAfd8-H2Aw&usg=AFQjCNGejAW7Pq59laUIqx6CqJNlRKc--g&sig2=rPtYF_ZvlWVUX_SYEempqA&cad=rja for more information) or google for Fundamentals of Generic Programming, regular types, Stepanov. Best, Vicente

Vicente BOTET <vicente.botet <at> wanadoo.fr> writes: ...
The classes I expect to be used are regular classes with value semantics.
Our classes with value- and pointer-semantics (pimpl-based) are as regular/normal. They just happen to be not your classes.
When I used regular classes I was not referring to normal or abnormal classes, but what Adobe ASL and Stepanov call regular types. Sorry for the imprecision.
Ah, I thought you used 'regular' casually. Apologies.
You can assimilate a regular class as one that behaves like a built-in type. But, please see [snipped the URL] for more information) or google for Fundamentals of Generic Programming, regular types, Stepanov.
Yes, I am aware of the paper. In the "Summary" it says: "In this paper, we have investigated several of the fundamental operations on built-in types in C++, and identified characteristics they should have when applied to user-defined types." In typical Russian style Stepanov pursues something he believes in with a vigor. However, "built-in characteristics applied to user-defined types" is something I seriously believe is mis-guided even though I understand the desire to simplify and unify. It's unrealistic to expect complex user-defined types to fit in the Procrustean bed of built-ins. IMO is should be the other way around... and I believe that was the original C++ understanding (see Stroustrup "Evolution"). V.

Message du 04/05/11 20:19 De : "Stewart, Robert" A : "'boost@lists.boost.org'" Copie à : Objet : Re: [boost] [review] string convert
Matthew Chambers wrote:
On 5/4/2011 11:05 AM, Vicente BOTET wrote:
I think we need to clarify one thing. Vladimir library uses values for two purposes: * as a default value when the type is not default constructible * as a fail-back in case of the conversion fails
And I think we should mix them. To cover the first case we can use as I said in another post a default_value metafunction that can be specialized for the non default constructible type.
Presumably you meant we should *not* mix the two uses.
Yes.
I agree that when a fail-back is given the user is not interested in knowing if the conversion succeeded or not, so in this case the return value should be T and not optional T. The question now is what function should be used, convert_cast or try_convert_cast. As the function doesn't throw I will use try_convert_cast, but as the function returns type T I will use convert_cast.
I disagree. I think the fallback with conversion success is a reasonable use case. Vladimir's case of notifying the user when a fallback value is being used is reasonable. It's difficult to leave that logic outside the conversion because only the conversion knows if and why the input is invalid.
I agree with Matt.
I don't see the added value then. If the user needs to check if the conversion succeeded, why not setting herself the fail-back int i; if (! try_convert_to(s,i)) { i = fail_back; }
Let me comment a little more on the function try_convert_cast returning optional T. The function can not be used directly where the target type T was expected so we can not consider it to follow the cast pattern.
You're right.
Other try_ functions return just bool. If we follow this pattern the preceding code could be written as
int i; if (try_convert(s,i)) { // do whatever you want with i; }
Yeah, I think that's right, too: a "try_" function should return bool. However, the name should be "try_convert_to" if you go that route.
OK.
If you want to preserve the convert_cast that returns a optional T, I will prefer to name it optional_convert_cast, so the user that will read it will be advertised that the result is an optional T.
auto r(optional_convert_cast(s)); if (r) { i = r.get(); }
I'd much rather see convert_cast>(S) than optional_convert_cast(). It indicates what is happening better.
Yes, this can be equivalent and doable.
If others agree, I could also go with Vincente's version of try_convert. It doesn't bother me that passing in the variable by reference makes it a two-liner, since the expression itself can go inside the if statement and it implicitly supports non-default-constructable types.
It is certainly non-surprising.
Using Vicente's default_value customization point still makes convert_cast> a viable candidate:
int i; if (try_convert_to(s)) { // use i }
You missed the i parameter the call to try_convert_to.
auto const c(convert_cast>(s)); if (c) { // use c.get() }
Note that try_convert_to() makes for simpler, shorter, and more direct code and that both are two-liners.
And we can also provide optional_convert, which CAN be one-lined (if the user doesn't care about conversion success, and if they DO, then they should use the try_ version!).
Offering both is possible, and depends upon the other functions in the set, though generally, offering two ways to do something can be a source of confusion.
string s = "4-2";
// Matt likes to throw: int i = convert_cast(s);
// Vladimir cares about success: int i = 17; if (!try_convert(s,i)) { /* log that fallback value is being used */ }
// except when he doesn't (but he never throws) int i = convert_cast(s, 17);
// Vincente thinks success is optional: optional i = optional_convert(s);
Note that in several of these, target typename is no longer needed, right?
try_convert(s, i) doesn't tell me whether s is being converted to i's type or i to s's type.
Is try_convert_to(s,i) better, or you see the same problem. Do you think it is better to use named parameters _from_ and _to?
convert_cast(s, 17) has the same problem and breaks with the new-style cast pattern.
would you prefer to use a _failback named parameter? convert_cast(s, _failback=17) or rename the function if_fail_conversion_cast(s,17); I don't really like the incidence on the interface/design of the failback feature. As no option satisfy completely I will just do int i; if (! try_convert_to(s,i)) i = 17; If try_convert_to doesn't updates the target variable if conversion fails we can even write int i=17; try_convert_to(s,i);
With this setup, is there any reason that convert_cast and optional_convert couldn't just be thin wrappers around try_convert?
Yes, this seems a good implementation option.
Perhaps, but there's still too much churn to think about that yet.
Yes, we need first to try to fix the interface. Best, Vicente

Vicente BOTET wrote:
De : "Stewart, Robert"
Matthew Chambers wrote:
On 5/4/2011 11:05 AM, Vicente BOTET wrote:
I agree that when a fail-back is given the user is not interested in knowing if the conversion succeeded or not, so in this case the return value should be T and not optional T. The question now is what function should be used, convert_cast or try_convert_cast. As the function doesn't throw I will use try_convert_cast, but as the function returns type T I will use convert_cast.
I disagree. I think the fallback with conversion success is a reasonable use case. Vladimir's case of notifying the user when a fallback value is being used is reasonable. It's difficult to leave that logic outside the conversion because only the conversion knows if and why the input is invalid.
I agree with Matt.
I don't see the added value then. If the user needs to check if the conversion succeeded, why not setting herself the fail-back
int i; if (! try_convert_to(s,i)) { i = fail_back; }
What are we comparing against? I've lost track. Shall we assume the default_value<T> customization point?
string s = "4-2";
// Matt likes to throw: int i = convert_cast(s);
// Vladimir cares about success: int i = 17; if (!try_convert(s,i)) { /* log that fallback value is being used */ }
// except when he doesn't (but he never throws) int i = convert_cast(s, 17);
// Vincente thinks success is optional: optional i = optional_convert(s);
Note that in several of these, target typename is no longer needed, right?
try_convert(s, i) doesn't tell me whether s is being converted to i's type or i to s's type.
Is try_convert_to(s,i) better, or you see the same problem. Do you think it is better to use named parameters _from_ and _to?
It's try_convert_to<int>(s, i) which is better. "_to<int>" makes it clear.
convert_cast(s, 17) has the same problem and breaks with the new-style cast pattern.
would you prefer to use a _failback named parameter?
s/failback/fallback/ My concern was that it needs to be convert_cast<int>(s, 17).
If try_convert_to doesn't updates the target variable if conversion fails we can even write
int i=17; try_convert_to(s,i);
I think that's the right semantic. _____ Rob Stewart robert.stewart@sig.com Software Engineer using std::disclaimer; Dev Tools & Components Susquehanna International Group, LLP http://www.sig.com IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

Barend Gehrels wrote:
On 4-5-2011 16:33, Stewart, Robert wrote:
Barend Gehrels wrote:
optional<int> i ... (similar as other one with optional)
I presume you mean the following, but why?
optional<int> i = convert_cast < optional<int> , throw_even_though_i_specified_a_failback_ >(s, 17);
Good question - I included it because it was in the original list of Gordon, but I actually also wonder what is the specific usage of this function.
I think you misunderstood one of the use cases. See my reply to Gordon.
The only other one I haven't commented on is the pair<bool,int> version. It shouldn't require a second argument.
You mean the pair<bool,...> is redudant because there is also the version with optional. I agree.
No, I mean the second argument should be supported but optional (overloading is fine). _____ Rob Stewart robert.stewart@sig.com Software Engineer using std::disclaimer; Dev Tools & Components Susquehanna International Group, LLP http://www.sig.com IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

On 5/4/2011 8:01 AM, Gordon Woodhull wrote:
So, here's something complete (?) for people to rip apart:
int i = convert_cast<int>(s); // default behavior: might throw optional<int> i = convert_cast<int>(s, use_optional_instead_of_throwing_); int i = convert_cast<int>(s, 17); // won't throw; can't tell if conversion successful int i = convert_cast<int>(s, 17, throw_even_though_i_specified_a_failback_); optional<int> i = convert_cast<int>(s, 17, use_optional_instead_of_throwing_); pair<bool,int> i = convert_cast<int>(s, 17, fallback_and_report_success_);
I am not concerned with names, just with syntax. So I'm using "convert_cast" although I actually don't care what it's called, and I have some very ugly very descriptive tag values with distinct types to trigger different overloads, which might be abbreviated dont_throw_, do_throw_, and, um, tell_fallback_ or something.
All would also accept extra manip_/format_= and locale_ arguments as in Vladimir's proposal, except they have to be appended to the first parameter list because of the "no proxies" requirement.
Doesn't this now cover all the functionality we've been talking about?
The 5th and 6th forms cover nondefaultable types / types with no sentinel value, and Vladimir's "print a warning if failing back" use-case.
You mention noncopyable types but I'm not able to find any reference to that in the documentation or in previous discussion. I don't see how the value can be non-copyable since it's being returned by the function.
I'd actually prefer the behavior flags were put in the template parameters, because they affect the return type, but that would be tricky because of the defaulted Destination type. (I don't think it's impossible, but it might be messy.)
Maybe just having more functions would be simpler; I'm just taking an idea to its conclusion.
I definitely could do without the tags to pick an overload. I'd much rather have the try_ prefix. So: int i = convert_cast<int>(s); // default behavior: might throw int i = convert_cast<int>(s, 17); // won't throw; can't tell if conversion successful; but how often do people actually care about that if they're providing a fallback value? it's still very practical and useful! optional<int> i = try_convert_cast<int>(s); // won't throw; specialized version of optional is not necessary since there's no fallback value convert<int>::result i = try_convert_cast<int>(s, 17); // won't throw; specialized version of optional is necessary to detect failure // I'd get rid of these: //int i = convert_cast<int>(s, 17, throw_even_though_i_specified_a_failback_); // what's the purpose of this overload? //optional<int> i = try_convert_cast<int>(s, 17); // this doesn't allow checking for failure, so there's not much point //pair<bool,int> i = convert_cast<int>(s, 17, fallback_and_report_success_); // it's burning my eyes
Finding the right prefix for "_cast" will be a challenge. Sigh, another naming debate. (Naming is important but so difficult!) It does seem like the direction that *_cast<>() is clearer to people than convert<>()
I also like as<>() :-) but somehow I don't think that'll generate consensus. The first overload is a drop-in replacement for lexical_cast as far as I'm concerned (even with additional arguments for locale and manipulators). If it can pass all the lexical_cast tests, what's the harm?
-Matt

On May 4, 2011, at 9:57 AM, Matt Chambers wrote:
I definitely could do without the tags to pick an overload. I'd much rather have the try_ prefix. So:
int i = convert_cast<int>(s); // default behavior: might throw int i = convert_cast<int>(s, 17); // won't throw; can't tell if conversion successful; but how often do people actually care about that if they're providing a fallback value? it's still very practical and useful! optional<int> i = try_convert_cast<int>(s); // won't throw; specialized version of optional is not necessary since there's no fallback value
Yep, not bad.
convert<int>::result i = try_convert_cast<int>(s, 17); // won't throw; specialized version of optional is necessary to detect failure
No, no special optional thing is needed. Please reread my 8:35am reply to Vladimir. Sorry, it's a pair<bool, int>. Why do people hate pair? There's a boolean and a value, why is that not a pair? The returned value is not optional in this case.
// I'd get rid of these: //int i = convert_cast<int>(s, 17, throw_even_though_i_specified_a_failback_); // what's the purpose of this overload?
This is the nondefaultable case where you still want it to throw.
//optional<int> i = try_convert_cast<int>(s, 17); // this doesn't allow checking for failure, so there's not much point
Again, it's for nondefaultable types. You need to supply a value, but you wouldn't actually use it if conversion failed.
//pair<bool,int> i = convert_cast<int>(s, 17, fallback_and_report_success_); // it's burning my eyes
Hahaha. Again, this is replaced by your try_convert_cast<int>(s, 17) and you could call it something else but it's really a pair. So you get rid of two tags by adding one new function name. Sounds good to me. On May 4, 2011, at 9:57 AM, Matt Chambers wrote:
Finding the right prefix for "_cast" will be a challenge. Sigh, another naming debate. (Naming is important but so difficult!) It does seem like the direction that *_cast<>() is clearer to people than convert<>()
I also like as<>() :-) but somehow I don't think that'll generate consensus. The first overload is a drop-in replacement for lexical_cast as far as I'm concerned (even with additional arguments for locale and manipulators). If it can pass all the lexical_cast tests, what's the harm?
I don't see any harm. Lexical Cast just needs a new maintainer who's willing to implement this stuff, against the intentions of the original author. IMO things do change. Cheers, Gordon

Gordon Woodhull wrote:
On May 4, 2011, at 9:57 AM, Matt Chambers wrote:
I definitely could do without the tags to pick an overload. I'd much rather have the try_ prefix. So:
int i = convert_cast<int>(s); // might throw int i = convert_cast<int>(s, 17); // won't throw optional<int> i = try_convert_cast<int>(s); // won't throw;
Yep, not bad.
convert<int>::result i = try_convert_cast<int>(s, 17); // won't throw; specialized version of optional is necessary to detect failure
No, no special optional thing is needed. Please reread my 8:35am reply to Vladimir.
Sorry, it's a pair<bool, int>. Why do people hate pair?
I think I covered that well in reply to Vicente.
There's a boolean and a value, why is that not a pair? The returned value is not optional in this case.
I had forgotten the case of needing an initial value because T() isn't available (default constructor or zero-initialization, depending upon type), but also not wanting an exception. That's what this one is supposed to handle, but I think it can be handled better another way. optional<int> o(try_convert_cast<int>(s, 17)); If the conversion succeeds, the optional is set and one extracts the value. If the conversion fails, the optional isn't set. 17 represents a value supplied for a non-DefaultConstructible UDT to create the function local variable needed to store the return value. Thus: 1. T convert_cast<T, S>(S) 2. T convert_cast<T, S>(S, T) 3. T convert_cast<T, S>(S, T, nothrow_t) 4. optional<T> try_convert_cast<T, S>(S) 5. optional<T> try_convert_cast<T, S>(S, T) In 1, the result valid unless there's an exception. It doesn't work for non-DefaultConstructible UDTs. In 2, the result is always valid since it never throws. It works for non-DefaultConstructible UDTs, but not for types without a suitable fallback value. In 3, the result is valid unless there's an exception. 2 and 3 could be merged, but keeping them distinct is probably more efficient and should be easier to document. In 4 and 5, the optional is set iff the conversion succeeds. 4 is useful for built-in types and DefaultConstructible UDTs. 5 is useful for all types. Arguably, "try_convert_cast" wants a better name. What's missing, then, is getting an exception when a conversion to a non-DefaultConstructible UDT or a type with no fallback value fails. That is, a variant of 2 that throws on conversion failure:
// I'd get rid of these: //int i = convert_cast<int>(s, 17, throw_even_though_i_specified_a_failback_); // what's the purpose of this overload?
This is the nondefaultable case where you still want it to throw.
Right. My case 3.
//optional<int> i = try_convert_cast<int>(s, 17); // this doesn't allow checking for failure, so there's not much point
Again, it's for nondefaultable types. You need to supply a value, but you wouldn't actually use it if conversion failed.
Right. My case 5.
//pair<bool,int> i = convert_cast<int>(s, 17, fallback_and_report_success_); // it's burning my eyes
Hahaha. Again, this is replaced by your try_convert_cast<int>(s, 17) and you could call it something else but it's really a pair.
Not needed. _____ Rob Stewart robert.stewart@sig.com Software Engineer using std::disclaimer; Dev Tools & Components Susquehanna International Group, LLP http://www.sig.com IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

Gordon Woodhull wrote:
int i = convert_cast<int>(s); // default behavior: might throw optional<int> i = convert_cast<int>(s, use_optional_instead_of_throwing_); int i = convert_cast<int>(s, 17); // won't throw; can't tell if conversion successful
Why do you prefer the tags rather than just adding the error policy to the name? And why do you prefer template specialisation rather than just adding the type to the name? (Keep It Simple.) int i = must_parse_int(s); // Personally I use "must" to mean "else throw", but I // wouldn't want to impose that on anyone else. optional<int> i = try_parse_int(s); // I've never used optional<> here, but I tend to // write try_* for things that return a boolean to // indicate success. int i = parse_int_with_default(s, 17); But per my last posts, anyone can easily write those in whatever style they want, if they have the core function, e.g. template <typename ITER> void parse_int(ITER begin, ITER end, int& i, bool& success) { .... } int must_parse_int(std::string s) { int i; bool success; parse_int(s.begin(),s.end(),i,success); if (!success) throw ParseError(); return i; } optional<int> try_parse_int(std::string s) { int i; bool success; parse_int(s.begin(),s.end(),i,success); return success ? i : optional<int>(); } int parse_int_with_default(std::string s, int def) { int i; bool success; parse_int(s.begin(),s.end(),i,success); return success ? i : def; } (I'm confused by the following, which look pointless; have I missed something?)
int i = convert_cast<int>(s, 17, throw_even_though_i_specified_a_failback_); optional<int> i = convert_cast<int>(s, 17, use_optional_instead_of_throwing_); pair<bool,int> i = convert_cast<int>(s, 17, fallback_and_report_success_);
Regards, Phil.

On May 4, 2011, at 11:32 AM, Phil Endecott wrote:
Gordon Woodhull wrote:
int i = convert_cast<int>(s); // default behavior: might throw optional<int> i = convert_cast<int>(s, use_optional_instead_of_throwing_); int i = convert_cast<int>(s, 17); // won't throw; can't tell if conversion successful
Why do you prefer the tags rather than just adding the error policy to the name?
And why do you prefer template specialisation rather than just adding the type to the name?
(Keep It Simple.)
I am fine either way. I was just taking the thought to its conclusion. I think if there end up being a lot of behaviors and they interact, then overloads are better.
int i = must_parse_int(s); // Personally I use "must" to mean "else throw", but I // wouldn't want to impose that on anyone else. optional<int> i = try_parse_int(s); // I've never used optional<> here, but I tend to // write try_* for things that return a boolean to // indicate success. int i = parse_int_with_default(s, 17);
Decent suggestions.
But per my last posts, anyone can easily write those in whatever style they want, if they have the core function, e.g.
template <typename ITER> void parse_int(ITER begin, ITER end, int& i, bool& success) { .... }
int must_parse_int(std::string s) { int i; bool success; parse_int(s.begin(),s.end(),i,success); if (!success) throw ParseError(); return i; }
optional<int> try_parse_int(std::string s) { int i; bool success; parse_int(s.begin(),s.end(),i,success); return success ? i : optional<int>(); }
int parse_int_with_default(std::string s, int def) { int i; bool success; parse_int(s.begin(),s.end(),i,success); return success ? i : def; }
Right, I am thinking something like your parse_int combined with Vicente's two-type customization method.
(I'm confused by the following, which look pointless; have I missed something?)
I think you have. But I'll go through it one last time.
int i = convert_cast<int>(s, 17, throw_even_though_i_specified_a_failback_); optional<int> i = convert_cast<int>(s, 17, use_optional_instead_of_throwing_);
These are for nondefaultable types. I probably shouldn't have used int in my example; what's a well-known example of a nondefaultable type? You must supply an initial value, but you don't want to use it. Pretty rare, but if you're in that case, you'll be annoyed if you don't have these. That's why they're the ones which still take ugly extra tags even after either Barend's or Matt's simplifications.
pair<bool,int> i = convert_cast<int>(s, 17, fallback_and_report_success_);
Please read my 11:39am message to Jeff. Cheers, Gordon

On 5/4/2011 10:51 AM, Gordon Woodhull wrote:
(I'm confused by the following, which look pointless; have I missed something?)
I think you have. But I'll go through it one last time.
int i = convert_cast<int>(s, 17, throw_even_though_i_specified_a_failback_); optional<int> i = convert_cast<int>(s, 17, use_optional_instead_of_throwing_);
These are for nondefaultable types. I probably shouldn't have used int in my example; what's a well-known example of a nondefaultable type? You must supply an initial value, but you don't want to use it.
Pretty rare, but if you're in that case, you'll be annoyed if you don't have these. That's why they're the ones which still take ugly extra tags even after either Barend's or Matt's simplifications.
I understand these cases and indeed illustration would be much clearer with a non-default type. Honestly I think it's a rare enough use case that it shouldn't pollute the general interface. Separate names would perhaps be more appropriate like: non_defaultable_type i = no_default_convert_cast<non_defaultable_type>(s, 17); optional<non_defaultable_type> i = try_no_default_convert_cast<non_defaultable_type>(s, 17); I don't really like it either. I haven't yet seen a tasteful way to support these non-defaultable types. -Matt

On May 4, 2011, at 12:03 PM, Matthew Chambers wrote:
On 5/4/2011 10:51 AM, Gordon Woodhull wrote:
int i = convert_cast<int>(s, 17, throw_even_though_i_specified_a_failback_); optional<int> i = convert_cast<int>(s, 17, use_optional_instead_of_throwing_);
These are for nondefaultable types. I probably shouldn't have used int in my example; what's a well-known example of a nondefaultable type? You must supply an initial value, but you don't want to use it.
Pretty rare, but if you're in that case, you'll be annoyed if you don't have these. That's why they're the ones which still take ugly extra tags even after either Barend's or Matt's simplifications.
I understand these cases and indeed illustration would be much clearer with a non-default type. Honestly I think it's a rare enough use case that it shouldn't pollute the general interface. Separate names would perhaps be more appropriate like:
non_defaultable_type i = no_default_convert_cast<non_defaultable_type>(s, 17); optional<non_defaultable_type> i = try_no_default_convert_cast<non_defaultable_type>(s, 17);
I don't really like it either. I haven't yet seen a tasteful way to support these non-defaultable types.
I don't think extra tags which hardly anyone has to use, pollute the general interface. But unused ugly tags or unused ugly names are all the same to me...

Gordon Woodhull wrote:
int i = convert_cast<int>(s, 17, throw_even_though_i_specified_a_failback_); optional<int> i = convert_cast<int>(s, 17, use_optional_instead_of_throwing_);
These are for nondefaultable types.
Right, sorry, I am reading this somewhat out-of-order. But: all of the built-in numeric types are default-constructable, and I would guess that any definition of "numeric type concept" would include a default ctor that sets it to zero. So, I believe this is not useful. Presumably someone will now argue that this is a "general purpose conversion framework" that should work with lots of different types including some that are not default-constructable. I would reply that that is an unnecessary over-complication of the problem. We have perhaps four cases: - Numbers. - Enumerations. - Booleans, which can be considered much like one or other of the above. - Compound types. I would consider each of those independently. By all means try to be consistent between them, but don't try to unnecessarily coerce them all into a single feature. Regards, Phil.

On 5/4/2011 10:32 AM, Phil Endecott wrote:
But per my last posts, anyone can easily write those in whatever style they want, if they have the core function, e.g.
template<typename ITER> void parse_int(ITER begin, ITER end, int& i, bool& success) { .... }
int must_parse_int(std::string s) { int i; bool success; parse_int(s.begin(),s.end(),i,success); if (!success) throw ParseError(); return i; }
optional<int> try_parse_int(std::string s) { int i; bool success; parse_int(s.begin(),s.end(),i,success); return success ? i : optional<int>(); }
int parse_int_with_default(std::string s, int def) { int i; bool success; parse_int(s.begin(),s.end(),i,success); return success ? i : def; }
It's not clear what complexity you're eliding and what you are really excluding. I don't think completely non-generic overloads like you've suggested will fly in boost. It also seems to emphasize string->type. What about int->string? Although you do bring up the interesting case of converting a string iterator_range. I think that is a very reasonable use case (to avoid copying). But I think we can do it with a single iterator_range argument instead of a pair of iterators. For example, it would be nice to be able to do: string node = "<value>1234</value>"; lexical_cast<int>(iterator_range(node.begin()+7, node.begin()+11)); -Matt

Matthew Chambers wrote:
On 5/4/2011 10:32 AM, Phil Endecott wrote:
But per my last posts, anyone can easily write those in whatever style they want, if they have the core function, e.g.
template<typename ITER> void parse_int(ITER begin, ITER end, int& i, bool& success) { .... }
int must_parse_int(std::string s) { int i; bool success; parse_int(s.begin(),s.end(),i,success); if (!success) throw ParseError(); return i; }
optional<int> try_parse_int(std::string s) { int i; bool success; parse_int(s.begin(),s.end(),i,success); return success ? i : optional<int>(); }
int parse_int_with_default(std::string s, int def) { int i; bool success; parse_int(s.begin(),s.end(),i,success); return success ? i : def; }
It's not clear what complexity you're eliding and what you are really excluding.
I don't think I understand what you're saying. Do you mean, "what is hidden in the '....' in the first function above"?
I don't think completely non-generic overloads like you've suggested will fly in boost.
<sarcasm>Yes, you're right, Boost tends to prefer things that use every available feature of the language even when not needed - after all, the project exists mainly to find obscure bugs in compilers.</sarcasm> Seriously though: what on earth is wrong with just functions that do things, with names that say what they do?
It also seems to emphasize string->type. What about int->string?
Well I have something like this, which uses printf()-style format specifiers: std::string format(const char* fmt, ...); Once again, I would not want to impose this on other people since it comes down to personal preferences, and anyone can write their own set of conversion functions once they have the right building blocks. I see no reason why conversions in both directions (i.e. parsing and formatting) should be rolled into a single function; it just complicates things. Regards, Phil.

Message du 04/05/11 19:50 De : "Phil Endecott" A : boost@lists.boost.org Copie à : Objet : Re: [boost] [review] string convert
Matthew Chambers wrote:
On 5/4/2011 10:32 AM, Phil Endecott wrote:
But per my last posts, anyone can easily write those in whatever style they want, if they have the core function, e.g.
template void parse_int(ITER begin, ITER end, int& i, bool& success) { .... }
int must_parse_int(std::string s) { int i; bool success; parse_int(s.begin(),s.end(),i,success); if (!success) throw ParseError(); return i; }
optional try_parse_int(std::string s) { int i; bool success; parse_int(s.begin(),s.end(),i,success); return success ? i : optional(); }
int parse_int_with_default(std::string s, int def) { int i; bool success; parse_int(s.begin(),s.end(),i,success); return success ? i : def; }
It's not clear what complexity you're eliding and what you are really excluding.
I don't think I understand what you're saying. Do you mean, "what is hidden in the '....' in the first function above"?
I don't think completely non-generic overloads like you've suggested will fly in boost.
Yes, you're right, Boost tends to prefer things that use every available feature of the language even when not needed - after all, the project exists mainly to find obscure bugs in compilers.
Seriously though: what on earth is wrong with just functions that do things, with names that say what they do?
It also seems to emphasize string->type. What about int->string?
Well I have something like this, which uses printf()-style format specifiers:
std::string format(const char* fmt, ...);
Once again, I would not want to impose this on other people since it comes down to personal preferences, and anyone can write their own set of conversion functions once they have the right building blocks. I see no reason why conversions in both directions (i.e. parsing and formatting) should be rolled into a single function; it just complicates things.
Phil. I encourage you to read the motivation of Boost.Conversion. (https://svn.boost.org/svn/boost/sandbox/conversion/libs/conversion_ext/doc/h...), the problem with naming functions like parse_int and not parse is that you are unable to use them in templates. Who would you name a function that parse a std::pair of int? and how to name to parse a std::pair of int and bool? or a std::pair of T and U? Generic names is the response, and it is not personal preference, but a need of generic programming Best, Vicente

Gordon Woodhull <gordon <at> woodhull.com> writes: .. Here's again the alternate "simpler" syntax which I put in my original review, which I think must have gotten buried from typing too much. :-/
optional<int> i = string_convert<int, dont_throw>(s); // or string_convert<int>(s, _throw=false) if that's impossible int i = string_convert<int>(s, manip_ = std::hex);
Apologies if you expected me to reply to something that I missed. I believe the functionality you are proposing is available via convert<int>::result res = convert<int>::from(s); The deployment of boost::optional might be quite appropriate in this particular case. However, I fee that the deployment of dedicated convert::result is better suited across (uniformity) all library uses. I'd probably like to hear more what clear advantages your proposed interface offers over the existing one.
Well, Jeremy pointed out
You propose that a bunch of tag_ names be used, but in practice they would likely have to be something like boost::convert::tag_, which would substantially reduce the benefits of any such change. ... I suppose that Vladimir's nice named parameters exhibit the same problem. Yuck.
Yes, my examples have or assume using namespace boost; using namespace boost::conversion::parameter;
... Anyway, I remain in favor of the library with simplified semantics. It only boils down to three things I find distressing.
I am not sure it's not 'distressing'. More like something you are not immediately comfortable with, right? ;-) I certainly do not want anyone distressed over something I suggest.
manipulators within it.
1. A special context which causes an apparent function call to either throw or not throw. 2. A streaming operation outside an apparent function call which applies IO 3. "::from"
Given that I'll have to juggle different suggestions/comments,/etc., I can't promise I'll change the above as you request (depending on the review outcome). I promise thought to make an honest effort to address your and all other concerns raised during the review. If ultimately I will not be able to proceed as you (or anyone else) suggested, then I'll try provide my reasoning behind my decision. I understand it's far from "yes, I'll fix #1-3" but that's all I can responsibly say at this moment.
int i = lexical_cast<int>(str); int i = convert<int>::from(str);
It's two more tokens, and I don't see the value to the user of separating the two parameters. I admit it's partly a matter of taste, but I think following the lexical_cast syntax as far as possible has objective value.
I thought it was one more token rather than two (lexica_cast=1; convert+from=2; 2-1=1). And as a user I probably do not care one way or the other. Given I am not convinced that boost::optional does it as well as convert::result I might be still leaning towards convert::from so that convert::result did not feel lonely. :-)
Since you say it's there for technical reasons, let's take a look at how Vicente fixed it in his Conversion library.
Yes, thans for the pointer. Looked briefly and it looked promising. Putting on my TODO list.
The complexity I and others are referring to is obviously not the number of functions! It's the fact that the converter<> object returned by from() has at least three different behaviors, when I think most people from looking at it would think conversion has already happened and a simple value is being returned.
- It can throw if assigned to a regular value - It doesn't throw if assigned to ::result - It can be a functor which takes a string and returns a value, if the string was omitted in the constructor (?) or if the string was empty (?)
It's almost as good as a swiss knife! On a serious note though I was adding the functionality as the relevant need arose. What should I do instead?
The practical reason for "::from" is two-fold -- 1) it clearly shows the direction of the conversion (which's been deemed important during original discussions);
Again, I haven't reread those discussions. I am surprised that people would find a function call confusing. Usually it takes its input as an argument and returns its result. :-p
From
convert<int>(std::string) it's not immediately clear if we convert int-to-str or str-to-int. So, more explicit direction was decided would be useful/helpful. Reading convert_to<int>(str) or convert<int>::from(str) is more natural to read and easier-flowing and kind of self-documenting (I feel that way anyway).
Yes, convert::result does not offer *much* beyond boost::optional. However, it *does* offer more. That's the reason it exists. Namely, convert::result provides two pieces of information -- if the conversion failed/succeeded *and* the fallback value (if provided). boost::optional provides one *or* the other.
So the intention is something like this?
convert<int>::result i = convert<int>::from(s, 17); if(!i) return i.value(); // returns 17 (!)
That looks pretty weird to me. I don't see why someone would want to specify the default value if they are then going to check if conversion succeeded.
For better or worse my usual processing flow of configuration parameters: // Try reading a configuration parameter. convert<int>::result i = convert<int>::from(s, 17); // Log error if parameter was bad. if(!i) message ("parameter ignored. default used"); // Proceed with the default. return i.value(); // returns 17 (!)
If that's really wanted, I think simply pair<bool,T> would be more clear.
Well, so far you suggest boost::optional in one place std::pair in another. I feel that providing one convert::result is more polished and easier to grasp. I personally is botherd by std::pair<iterator, bool> from std::map::insert(). Equally I am not thrilled about pair<bool,T> in this context... more so because it'll probably be pair<bool, boost::optional<T> > -- something convert::result encapsulates.
Why should the LHS modify the behavior of the proxy object returned by ::from?
Because usages of 'convert' differ -- namely, throwing and non-throwing behavior. I personally do not use throwing behavior at all but I feel it was needed to provide/support the behavior (and resulting mess) for lexical_cast backward compatibility and to cater for usages different from mine.
Personally I would want both behaviors.
Agreed.
It's just the context-sensitive value that I object to.
Yes, it probably takes some getting used to but in reality I do not think it's that bad. People are likely to use one or the other. Like I personally always use the non-throwing version.
Is the following syntax possible? I am not up on Boost.Parameter yet.
int i = convert<int>::from(s)(_dothrow); optional<int> j = convert<int>::from(s)(_dontthrow);
Yes, it is possible. However, in #1 (_dothrow) is stating the obvious. We have to throw as there is nothing to return when failed. So, initially your #1 suggestion might be useful (if only for documenting purposes) but later I am afraid bothersome. #2 is available as convert<int>::result res = convert<int>::from(s); So, essentially we are talking about different deployment/API of the same functionality. I'll take your concern on board and we'll try addressing it in due course.
convert<int>::result res = convert<int>::from("blah", -1); if (!res) message("conversion failed"); int value = res.value(); // proceed with the fallback anyway.
The locality of 'convert' application (IMHO of course) does not leave much to think how 'convert' is to behave.
Clear enough in itself (although I'm still reeling from the idea of an object which converts to false having a value).
It's just that the next line might read
int res2 = convert<int>::from("blah");
and throw, and there's no indication why one version throws and the other does not.
Understood. I have no answer to that. Different cases, different behavior. If I could make them behave the same, I would. Any suggestions how to address that are most welcomed.
I think Boost.Lambda/Boost.Phoenix is pretty well-known syntax by now, but perhaps I've been indoctrinated.
Nop. Tried both but was happier with boost::bind and boost::regex. Maybe due to my use-cases, maybe due to something else. ;-)
I guess your syntax is a bit like currying - drop an argument and it becomes a function object which takes that argument. Don't think I've seen that in C++ before.
Could that be a good thing? :-)
Maybe an alternative might be to delay reviewing this library for a year (?) and let guys try extending lexical_cast as there were numerous suggestions. Then, we'll re-evaluate the situation. Although my suspicion is that in 1-2 years that lexical_cast-extension push will fizzle out once again, new people will come in asking the same questions and we won't move an inch.
I think I made suggestions which answer almost all of the concerns raised in this review, except for the insistence on lexical_cast.
As I said, I can't promise as I'll have to take into account other suggestions. However, I'll make a genuine effort and try incorporating your suggestions... unless it is a matter of one API over another API. Then we can't win that battle no matter which way we go.
The functionality is clearly there. It's just this odd use of Expression Templates where IMO it's not needed, that irks me. For what? To specify a context where a function won't throw.
I really do not see it as a big deal. To me the library's policy is -- it won't throw unless it is the only thing it can do. Like lexical_cast really. Still, we could consider adding something like
int i = convert<int>::from(s)(_dothrow); optional<int> j = convert<int>::from(s)(_dontthrow);
to make the behavior more explicit.
To apply io manipulators using a familiar operator.
I remember considering int i = convert<int>::from(s)(format_ = std::hex); or something along these lines. Can't see it as a problem adding. Removing op>>() though might be harder as I remember people fairly evenly split supporting and disliking the existing op>>()-based interface.
As a shortcut for lambdas.
The returned from convert::from() converter is a functor which we then feed to an algorithm. I fairly standard deployment, don't you think? Thanks for all your input. It's much and truly appreciated. V.

Vladimir Batov wrote:
Gordon Woodhull <gordon <at> woodhull.com> writes: .. Here's again the alternate "simpler" syntax which I put in my original review, which I think must have gotten buried from typing too much. :-/
optional<int> i = string_convert<int, dont_throw>(s); // or string_convert<int>(s, _throw=false) if that's impossible int i = string_convert<int>(s, manip_ = std::hex);
Apologies if you expected me to reply to something that I missed. I believe the functionality you are proposing is available via
convert<int>::result res = convert<int>::from(s);
The deployment of boost::optional might be quite appropriate in this particular case. However, I fee that the deployment of dedicated convert::result is better suited across (uniformity) all library uses. I'd probably like to hear more what clear advantages your proposed interface offers over the existing one.
In my mind boost::optional 'is' the way to represent optional value types, and this promotes "uniformity" non only across used of this library, but all boost libraries. Jeff

Hi Vladimir, I'm sorry to hear you've withdrawn Boost.Convert. I hope you'll continue to participate in the conversation about replacing or improving lexical_cast. You brought a lot of interesting ideas and it's always helpful to have a concrete proposal for people to tear apart! :-/ On May 3, 2011, at 8:36 AM, Vladimir Batov wrote:
Gordon Woodhull <gordon <at> woodhull.com> writes: ... Anyway, I remain in favor of the library with simplified semantics. It only boils down to three things I find distressing.
I am not sure it's not 'distressing'. More like something you are not immediately comfortable with, right? ;-) I certainly do not want anyone distressed over something I suggest.
:-)
From
convert<int>(std::string)
it's not immediately clear if we convert int-to-str or str-to-int. So, more explicit direction was decided would be useful/helpful. Reading
convert_to<int>(str) or convert<int>::from(str)
is more natural to read and easier-flowing and kind of self-documenting (I feel that way anyway).
Yes, something seems ambiguous about convert<> on its own. But I'd prefer a longer name than these "prepositional phrases" - although I have to admit there is something neat about them!
For better or worse my usual processing flow of configuration parameters:
// Try reading a configuration parameter. convert<int>::result i = convert<int>::from(s, 17); // Log error if parameter was bad. if(!i) message ("parameter ignored. default used"); // Proceed with the default. return i.value(); // returns 17 (!)
Aha, I see now. I still think that's a pair, see my next message.
If that's really wanted, I think simply pair<bool,T> would be more clear.
Well, so far you suggest boost::optional in one place std::pair in another. I feel that providing one convert::result is more polished and easier to grasp. I personally is botherd by std::pair<iterator, bool> from std::map::insert(). Equally I am not thrilled about pair<bool,T> in this context... more so because it'll probably be pair<bool, boost::optional<T> > -- something convert::result encapsulates.
Whoah! No, there is only one failure to record, there should be no need for two bools. If conversion succeeded, then we have a value. If conversion failed, then the user has chosen to throw, to use a default, or use an optional. Further, the user can choose when using a default, whether they want to know whether they're getting that value because of a conversion failure. IIUC your convert<T>::result would be in the state {false, empty} if conversion failed and no failback was specified. However, there is no need to remember whether a failback was specified inside the function which took it or didn't. :-) In other words, just return the appropriate structure based on the request type tag. There is no need to have a one-size-fits-all result type. Or, equivalently, have different names for different behaviors instead of overloads. I'm not really opposed to that, but I guess I would prefer the overloads if there are going to be more behaviors and especially if behaviors could combine to produce yet more return types.
Is the following syntax possible? I am not up on Boost.Parameter yet.
int i = convert<int>::from(s)(_dothrow); optional<int> j = convert<int>::from(s)(_dontthrow);
Yes, it is possible. However, in #1 (_dothrow) is stating the obvious.
Yes _dothrow would be the default, unless there's a failback value. It would explicitly be needed in one bizarre case, though. Suppose the value is not defaultable, but you still want convert to throw. So you have to supply a failback but don't want it returned... Pretty uncommon, but it would be annoying if you needed it and it wasn't there.
So, essentially we are talking about different deployment/API of the same functionality.
Yes that is the essence of my review.
I think Boost.Lambda/Boost.Phoenix is pretty well-known syntax by now, but perhaps I've been indoctrinated.
Nop. Tried both but was happier with boost::bind and boost::regex. Maybe due to my use-cases, maybe due to something else. ;-)
Boost.Bind is monomorphic. I believe Phoenix Bind would work with the convert_cast in my next message.
I guess your syntax is a bit like currying - drop an argument and it becomes a function object which takes that argument. Don't think I've seen that in C++ before.
Could that be a good thing? :-)
Yes! but Lambda & Bind are the normal way to do it.
Thanks for all your input. It's much and truly appreciated.
It's been fun! Gordon

Gordon Woodhull wrote:
Hi Vladimir, all,
First off, I'll respond to Rob and Jeremy.
Here's again the alternate "simpler"
[purposefully snip context that may not appear in code] string_convert<int, dont_throw>(s); // or string_convert<int>(s, _throw=false) if that's impossible So I'm creating a string from an int but not throwing, is my first reading of that snippet(maybe I'm "wacky" though). The function name starts with a common type followed by a verb, followed by a type, so I This function may appear in some function call: some_function(string_convert<int, dont_throw>(s)); This requires that I dig up both some_function's signature and the type of s. Easy you say! What about template< typename T> void some_function(T t); template< typename S> void outerFunction(S s) { some_function(string_convert<int, dont_throw>(s)); } I find the "wacky" convert<int>::from(s) much less ambiguous which I literally read as "convert int from s". Jeff

Message du 03/05/11 04:37 De : "Vladimir Batov" A : boost@lists.boost.org Copie à : Objet : Re: [boost] [review] string convert
Gordon Woodhull woodhull.com> writes:
I also want to mention type-to-type conversion, which Vicente Botet has thought out much more thoroughly in his Conversion library. This library offers type-to-type conversion seemingly as an afterthought, just because the syntax allows it.
I have to disagree. Again I consider 'convert' to be more of a conversion framework rather than a library. Indeed, 'convert' does not provide much more beyond string-to-type conversions because I personally did/do not need more. However, it does not mean I/we should deny that possibility to others as their needs may differ. Therefore, 'convert' was designed with that in mind. If Vicente needs type-to-type conversion functionality, I think he might consider implementing that functionality within the 'convert' framework by extending the 'convert' library for everyone's benefit.
Vladimir, as you know I started Boos.Conversion as a more generic response to type-to-type conversion. Your design was limited to string conversion and your library was named StringConvert. Then you redesigned your library so now you are able to make type-to-type conversions also, but the interface is not satisfactory for me, for my taste.
I'd really like to know why you think it's necessary to do convert::from(s) rather than the much more familiar and easy-to-grasp string_convert(s).
I am not sure if I managed to explain that above. The reason why TypeIn and TypeOut need to be separated is that if we have specialization for, say, string-to-type enabled with
template typename boost::enable_if, TypeOut>::type convert_to(StringIn const&) {...}
then, say, string-to-bool further specialization/optimization won't be possible
template bool convert_to(StringIn const&) {...}
It won't compile as both "specializations" will match. It did not work for me. If you can get it to work by some other means, I'll be only happy.
I think the way Boost.Conversion is designed allows such overloading. It use a dummy parameter taking the type of the return type. template typename boost::enable_if, TypeOut>::type convert_to(const StringIn& from, boost::dummy::type_tag const&); template bool convert_to(const StringIn& from, boost::dummy::type_tag const&); The library provides a convert_to function that adds the specific dummy parameter. I recognize this is tricky and that the overloading seems not natural from the user perspective but it works at least for the cases I have tested. Best, Vicente

Vicente BOTET <vicente.botet <at> wanadoo.fr> writes:
Vladimir, as you know I started Boos.Conversion as a more generic response to type-to-type conversion. ... I think the way Boost.Conversion is designed allows such overloading. It use a dummy parameter taking the type of the return type.
template typename boost::enable_if, TypeOut>::type convert_to(const StringIn& from, boost::dummy::type_tag const&);
template bool convert_to(const StringIn& from, boost::dummy::type_tag const&);
The library provides a convert_to function that adds the specific dummy parameter. I recognize this is tricky and that the overloading seems not natural from the user perspective but it works at least for the cases I have tested.
Vicente, Gordon, Thank you for the pointer, I had a quick look and indeed it seems to solve the specialization problem I was facing. Should we cancel/postpone this review and review Vicente's and mine together or something of that sort? If Vicente's is better as a starting point, it's fine by me. I am happy either way a s long as things are moving. V.

Vicente BOTET <vicente.botet <at> wanadoo.fr> writes: The library provides a convert_to function that adds the specific dummy
Vladimir Batov <vbatov <at> people.net.au> writes: parameter. I recognize this is
tricky and that the overloading seems not natural from the user perspective but it works at least for the cases I have tested.
Vicente, Gordon,
Thank you for the pointer, I had a quick look and indeed it seems to solve the specialization problem I was facing. Should we cancel/postpone this review and review Vicente's and mine together or something of that sort? If Vicente's is better as a starting point, it's fine by me. I am happy either way a s long as things are moving.
Vicente, I had a quick look and your proposal. Conceptually it seems to be an extension of lexical_cast philosophy on to type-to-type conversions. It does not seem to provide any of the functionality that I (as a user) care about, i.e., fallback value, throwing/non-throwing, formatting, locales, etc. Given convert_to actually returns a Target value, I suspect, formatting, locales, etc. are simply not possible as I believe we have to have an intermediary object (converter) to apply additional formatting, etc. Am I correct in my reading? If so, then you and I are trying to address totally unrelated issues. I needed 'convert' as lexical_cast not not doing what I needed it to do. I do not think I immediately have a need for type-to-type extension of lexical_cast. V.

Message du 03/05/11 11:11 De : "Vladimir Batov" A : boost@lists.boost.org Copie à : Objet : Re: [boost] [review] string convert
Vicente BOTET wanadoo.fr> writes: The library provides a convert_to function that adds the specific dummy
Vladimir Batov people.net.au> writes: parameter. I recognize this is
tricky and that the overloading seems not natural from the user perspective but it works at least for the cases I have tested.
Vicente, Gordon,
Thank you for the pointer, I had a quick look and indeed it seems to solve the specialization problem I was facing. Should we cancel/postpone this review and review Vicente's and mine together or something of that sort? If Vicente's is better as a starting point, it's fine by me. I am happy either way a s long as things are moving.
Vicente,
I had a quick look and your proposal. Conceptually it seems to be an extension of lexical_cast philosophy on to type-to-type conversions. It does not seem to provide any of the functionality that I (as a user) care about, i.e., fallback value, throwing/non-throwing, formatting, locales, etc. Given convert_to actually returns a Target value, I suspect, formatting, locales, etc. are simply not possible as I believe we have to have an intermediary object (converter) to apply additional formatting, etc. Am I correct in my reading?
If so, then you and I are trying to address totally unrelated issues. I needed 'convert' as lexical_cast not not doing what I needed it to do. I do not think I immediately have a need for type-to-type extension of lexical_cast.
Vladimir, you are right. My library is not designed to take care of yours needs, and never was. I could do my best trying to update my library if there is enough interest and agreement in the interface. Next follows how I see the interface that could respond to the Boost.Convert features: In order to take care of types without default constructor I see several options: * Add an overloading having a default parameter template Target convert_to(Source const& s, Target const& t); and use it as follows Target t = convert_to(s, t1); * Use the existing assign_to function template Target& assign_to(Target& t, Source const& s); as follows Target t (x); assign_to(t, s); * Add a meta-function that returns a default value for a type template struct default_value { T apply() { return T(); } }; The user will need to specialize this function for types that are not default constructible. This meta-function will be used instead of declaring a defaulted variable in the string conversion function. T v(default_value::apply()); As a user I would prefer the second one and then the last one it is transparent except when the specialization is needed. The first option is not appealing for my taste. In order to take care of no throwing semantics, I will choose to add a convert_to overloading with a error code parameter, not throwing without not knowing why fails seems too specific to me. template Target convert_to(Source const& s, err_code& ec); Target t = convert_to(s, ec); if (ec .... But I could understand there are some cases where the single reason is a bad format. We could also use the no_throw_t as additional parameter, but in this case the return type should allow to know if the operation has succeed or not. If we follow the C++ standard interface the use of pair seems to be the more appropriated. template pair convert_to(Source const& s, no_thow_t const&); pair p = convert_to(s, no_thow); if (p.second) Note also that there could be some conversions that could transport whether the conversion succeeds or not optional t = convert_to >(s); This conversion could be overloaded and never throw. or a Robert suggested overload the pointer to a source template optional convert_to(Source const* s); Both mechanism can be mixed template Target convert_to(Source const& s, Target const& t, err_code& ec); Target t = convert_to(s, Target(x), ec); if (ec ... template Target& assign_to(Target& t, Source const& s, err_code& ec); Target t (x); assign_to(t, s, ec); if (ec ... I think these could be added to Boost.Conversion without not to much trouble. I could do my best for the Boost.Conversion part, if there is enough interest and agreement in the interface. Respect to the stream manipulators, what do we want? output an output streameable type and extract another type after some manipulations. Source s; a_iostream ios; Target t; ios << s; ios >> m1 >> m2 >> t; But you want to hide the intermediary iostream. As I stated on one of my post it will be great if we can define a specific input stream that is built from an output stremable type that allows to extract the value stating whatever format is needed. What about as_istream(s) >> m1 >> m2 >> t; Respect to the functor I think that we need to use Phoenix syntax and overload the functions with a Phoenix placeholder to create the functor. So convert_to(_1) will return a functor that can be called with one Source parameter and return a Target. The same applies to each one of the other functions. In this way each feature is separated and compassable, avoiding a bloated class that do everything. The generic part of Boost.Conversion will not take care are the string conversions and the input stream view. Boost.StringConvert could take care of the string specializations. I don't know if there are some features I have miss. Please let me know. Best, Vicente

Vicente BOTET wrote: Vicente, you really need to try a different mail client or else use it differently. The angle brackets for your template parameter and argument lists are being dropped.
In order to take care of types without default constructor I see several options:
template Target convert_to(Source const& s, Target const& t);
template Target& assign_to(Target& t, Source const& s);
template struct default_value { T apply() { return T(); } };
The user will need to specialize this function for types that are not default constructible.
This meta-function will be used instead of declaring a defaulted variable in the string conversion function.
T v(default_value::apply());
None of those options work for types without a default constructor *and* without a sentinel value.
As a user I would prefer the second one
The name "assign_to" just seems odd to me. The problem is that assigning "123" to an int is the wrong idea. It isn't assignment but conversion. I understand you wanted a different name to account for the different behavior, but that's not it.
In order to take care of no throwing semantics, I will choose to add a convert_to overloading with a error code parameter, not throwing without not knowing why fails seems too specific to me.
template Target convert_to(Source const& s, err_code& ec);
That's fine if there are specific errors to report other than "bad input." The err_code type should also support a safe-bool conversion.
But I could understand there are some cases where the single reason is a bad format. We could also use the no_throw_t as additional parameter, but in this case the return type should allow to know if the operation has succeed or not. If we follow the C++ standard interface the use of pair seems to be the more appropriated.
template pair convert_to(Source const& s, no_thow_t const&);
This was already rejected by several folks. Allow me to add my dislike for it.
Note also that there could be some conversions that could transport whether the conversion succeeds or not
I can't quite parse that.
optional t = convert_to >(s);
Something important was lost in your message.
or a Robert suggested overload the pointer to a source
template optional convert_to(Source const* s);
Unfortunately, I realized that wouldn't work well for string literals, for example. _____ Rob Stewart robert.stewart@sig.com Software Engineer using std::disclaimer; Dev Tools & Components Susquehanna International Group, LLP http://www.sig.com IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

Sorry, but I think I have yet a problem with less and greater symbols <>, and some important parts are missing. I will use (* and *) instead if needed. I will resent the preceding post in an attached file :(
Message du 03/05/11 20:27 De : "Stewart, Robert" A : "'boost@lists.boost.org'" Copie à : Objet : Re: [boost] [review] string convert
Vicente BOTET wrote:
Vicente, you really need to try a different mail client or else use it differently. The angle brackets for your template parameter and argument lists are being dropped.
In order to take care of types without default constructor I see several options:
template Target convert_to(Source const& s, Target const& t);
template Target& assign_to(Target& t, Source const& s);
template struct default_value { T apply() { return T(); } };
The user will need to specialize this function for types that are not default constructible.
This meta-function will be used instead of declaring a defaulted variable in the string conversion function.
T v(default_value::apply());
None of those options work for types without a default constructor *and* without a sentinel value.
Why? For me the 3 options work.
As a user I would prefer the second one
The name "assign_to" just seems odd to me. The problem is that assigning "123" to an int is the wrong idea. It isn't assignment but conversion. I understand you wanted a different name to account for the different behavior, but that's not it.
assign_to is the functional for of operator= (assign). This operator can accept any type on its lhs, making a conversion. I think the name assign_to respect this schema.
In order to take care of no throwing semantics, I will choose to add a convert_to overloading with a error code parameter, not throwing without not knowing why fails seems too specific to me.
template Target convert_to(Source const& s, err_code& ec);
That's fine if there are specific errors to report other than "bad input." The err_code type should also support a safe-bool conversion.
I was thinking on the error code of the C++ standard.
But I could understand there are some cases where the single reason is a bad format. We could also use the no_throw_t as additional parameter, but in this case the return type should allow to know if the operation has succeed or not. If we follow the C++ standard interface the use of pair seems to be the more appropriated.
template pair convert_to(Source const& s, no_thow_t const&);
This was already rejected by several folks. Allow me to add my dislike for it.
Maybe be some rejected it, but not all.
Note also that there could be some conversions that could transport whether the conversion succeeds or not
I can't quite parse that.
Sorry for my bad English. I meant the optional can store the converted value and if the conversion failed.
optional t = convert_to >(s);
Something important was lost in your message.
Yes. See my initial comment.
or a Robert suggested overload the pointer to a source
template optional convert_to(Source const* s);
Unfortunately, I realized that wouldn't work well for string literals, for example.
I like the try_convert_to also. Best, Vicente

Vicente BOTET wrote:
De : "Stewart, Robert"
Vicente BOTET wrote:
In order to take care of types without default constructor I see several options:
template Target convert_to(Source const& s, Target const& t);
template Target& assign_to(Target& t, Source const& s);
template struct default_value { T apply() { return T(); } };
This meta-function will be used instead of declaring a defaulted variable in the string conversion function.
T v(default_value::apply());
None of those options work for types without a default constructor *and* without a sentinel value.
Why? For me the 3 options work.
Consider conversion to int, where all values are allowed: int const fallback(/* what to use here? */); int const t(convert_to<int>("some string", fallback)); if (t == fallback) { // is this because the conversion failed or because // of the second parameter? } -------------- int value; assign_to(value, "some string"); if (value == ???) // how test for failure? -------------- Cannot specialize default_value because there is no suitable default value. Zero is not appropriate because it is within range. In each case, because there is no special, sentinel value that is out of range, there is no way to detect conversion failure.
As a user I would prefer the second one
The name "assign_to" just seems odd to me. The problem is that assigning "123" to an int is the wrong idea. It isn't assignment but conversion. I understand you wanted a different name to account for the different behavior, but that's not it.
assign_to is the functional for of operator= (assign). This operator can accept any type on its lhs, making a conversion. I think the name assign_to respect this schema.
I'm not arguing why you have the function, I'm just telling you that I don't find the name to be good. I also don't thing assignment for conversions is appropriate.
template Target convert_to(Source const& s, err_code& ec);
That's fine if there are specific errors to report other than "bad input." The err_code type should also support a safe-bool conversion.
I was thinking on the error code of the C++ standard.
Hmmm. Those codes aren't extensible. Do they really describe the sorts of errors likely to occur during such conversions? If the codes don't cover everything, then they won't work. You'd need a different list, though a similar design would work. _____ Rob Stewart robert.stewart@sig.com Software Engineer using std::disclaimer; Dev Tools & Components Susquehanna International Group, LLP http://www.sig.com IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

Message du 03/05/11 22:21 De : "Stewart, Robert" A : "'boost@lists.boost.org'" Copie à : Objet : Re: [boost] [review] string convert
Vicente BOTET wrote:
De : "Stewart, Robert"
Vicente BOTET wrote:
In order to take care of types without default constructor I see several options:
template Target convert_to(Source const& s, Target const& t);
template Target& assign_to(Target& t, Source const& s);
template struct default_value { T apply() { return T(); } };
This meta-function will be used instead of declaring a defaulted variable in the string conversion function.
T v(default_value::apply());
None of those options work for types without a default constructor *and* without a sentinel value.
Why? For me the 3 options work.
Consider conversion to int, where all values are allowed:
int const fallback(/* what to use here? */); int const t(convert_to("some string", fallback)); if (t == fallback) { // is this because the conversion failed or because // of the second parameter? }
Note that in this case, if conversion fails an exception is thrown.
-------------- int value; assign_to(value, "some string"); if (value == ???) // how test for failure?
The same here.
-------------- Cannot specialize default_value because there is no suitable default value. Zero is not appropriate because it is within range.
In each case, because there is no special, sentinel value that is out of range, there is no way to detect conversion failure.
default_value is not used to detect conversion failure, but to initialize a non default constructor type.
As a user I would prefer the second one
The name "assign_to" just seems odd to me. The problem is that assigning "123" to an int is the wrong idea. It isn't assignment but conversion. I understand you wanted a different name to account for the different behavior, but that's not it.
assign_to is the functional for of operator= (assign). This operator can accept any type on its lhs, making a conversion. I think the name assign_to respect this schema.
I'm not arguing why you have the function, I'm just telling you that I don't find the name to be good.
Would you have a better name?
I also don't thing assignment for conversions is appropriate.
Well, this is part of the language, I'm quite sure the C++ standard and Boost uses this kind of assignations. As a generic function emulating extrinsic assignation you should I must follows the standard mechanism conventions. It is up to the user to overload or not this function which by default relies on the operator=.
template Target convert_to(Source const& s, err_code& ec);
That's fine if there are specific errors to report other than "bad input." The err_code type should also support a safe-bool conversion.
I was thinking on the error code of the C++ standard.
Hmmm. Those codes aren't extensible. Do they really describe the sorts of errors likely to occur during such conversions? If the codes don't cover everything, then they won't work. You'd need a different list, though a similar design would work.
error_code is extensible because error_category is. You just need to define a specific category and use it to instantiate your error codes, isn't it? Best, Vicente

Vicente BOTET wrote:
De : "Stewart, Robert" [repeat several times!]
template Target convert_to(Source const& s, Target const& t);
template Target& assign_to(Target& t, Source const& s);
template struct default_value { T apply() { return T(); } };
This meta-function will be used instead of declaring a defaulted variable in the string conversion function.
T v(default_value::apply());
None of those options work for types without a default constructor *and* without a sentinel value.
Why? For me the 3 options work.
Consider conversion to int, where all values are allowed:
int const fallback(/* what to use here? */); int const t(convert_to("some string", fallback)); if (t == fallback) { // is this because the conversion failed or because // of the second parameter? }
Note that in this case, if conversion fails an exception is thrown.
Ah, sorry. I stated the use case you didn't address incompletely and didn't realize you meant for such a call to convert_to() to throw an exception. In Vladimir's library, adding the fallback argument implied non-throwing behavior and I assumed the same here. What your three options don't address, then, is indicating conversion failure, without an exception, for a type with neither a default constructor nor a usable sentinel value. That was the purpose of Vladimir's result type: it could be queried for success and for the value.
assign_to is the functional for of operator= (assign). This operator can accept any type on its lhs, making a conversion. I think the name assign_to respect this schema.
I'm not arguing why you have the function, I'm just telling you that I don't find the name to be good.
Would you have a better name?
Not presently. I need to think about it more, but first...
I also don't thing assignment for conversions is appropriate.
Well, this is part of the language, I'm quite sure the C++ standard and Boost uses this kind of assignations. As a generic function emulating extrinsic assignation you should I must follows the standard mechanism conventions. It is up to the user to overload or not this function which by default relies on the operator=.
I haven't looked at the design of your library, but presumably these assignment operators are on a conversion type. Your conversion type's state is being set from another type in the various assignment operators, which is the normal sense of assignment, but your purpose of doing so is simply to provide the source value in order to extract it subsequently as the target type. That seems an abuse of assignment.
Hmmm. Those codes aren't extensible. Do they really describe the sorts of errors likely to occur during such conversions? If the codes don't cover everything, then they won't work. You'd need a different list, though a similar design would work.
error_code is extensible because error_category is. You just need to define a specific category and use it to instantiate your error codes, isn't it?
Yeah, I forgot about the error categories. _____ Rob Stewart robert.stewart@sig.com Software Engineer using std::disclaimer; Dev Tools & Components Susquehanna International Group, LLP http://www.sig.com IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

Message du 04/05/11 13:12 De : "Stewart, Robert" A : "'boost@lists.boost.org'" Copie à : Objet : Re: [boost] [review] string convert
Vicente BOTET wrote:
De : "Stewart, Robert" [repeat several times!]
What your three options don't address, then, is indicating conversion failure, without an exception, for a type with neither a default constructor nor a usable sentinel value. That was the purpose of Vladimir's result type: it could be queried for success and for the value.
Please, check the post around May 03, 2011; 10:09pm that includes a file with the complete post without angle brackets issues. There you will find all the cases.
assign_to is the functional for of operator= (assign). This operator can accept any type on its lhs, making a conversion. I think the name assign_to respect this schema.
I'm not arguing why you have the function, I'm just telling you that I don't find the name to be good.
Would you have a better name?
Not presently. I need to think about it more, but first...
I also don't thing assignment for conversions is appropriate.
Well, this is part of the language, I'm quite sure the C++ standard and Boost uses this kind of assignations. As a generic function emulating extrinsic assignation you should I must follows the standard mechanism conventions. It is up to the user to overload or not this function which by default relies on the operator=.
I haven't looked at the design of your library, but presumably these assignment operators are on a conversion type. Your conversion type's state is being set from another type in the various assignment operators, which is the normal sense of assignment, but your purpose of doing so is simply to provide the source value in order to extract it subsequently as the target type. That seems an abuse of assignment.
If you have a class C1 you can define the following operator C1& operator=(C2 const& rhs); As I understand this, we are converting a C2 to a C1 via an assignment. The assign_to function is a way do it extrinsically, i.e. outside any of the classes C1, C2. C1& assign_to(C1 & lhs, C2 const& rhs); We can say assign_to is to C1& operator=(C1&,C2const&) as boost::swap(C&lhs,C&rhs) is to C::swap(C& rhs). convert_to is the counterpart of the explicit conversion operator (or explicit constructor) If we had the possibility to overload operator= as a free function I will not needed to add an assign_to function. If we had the possibility to overload explicit conversion operators as a free function I will not needed to add an conver_to function. So we can say Boost.Conversion is an emulation of these operator overloading as free functions. Best, Vicente Best, Vicente

Vicente BOTET wrote:
De : "Stewart, Robert"
Vicente BOTET wrote:
De : "Stewart, Robert"
What your three options don't address, then, is indicating conversion failure, without an exception, for a type with neither a default constructor nor a usable sentinel value. That was the purpose of Vladimir's result type: it could be queried for success and for the value.
Please, check the post around May 03, 2011; 10:09pm that includes a file with the complete post without angle brackets issues. There you will find all the cases.
I thought you were proposing those three options as the solution. I now understand that you still meant for an error_code overload or a no_throw overload to be the means to determine whether conversion fails without relying on fallback values. The latter involves returning a pair<value, bool> which I dislike. boost::optional would be better as it supports safe-bool and a more meaningful get() for extracting the value (versus .second and .first for your pair). If there's truly value in using an error_code, that interface is good because it affords the ability to provide more specific diagnostics. Otherwise, I'd prefer the optional interface with nothrow or even a "try" variant to avoid the argument.
I haven't looked at the design of your library, but presumably these assignment operators are on a conversion type. Your conversion type's state is being set from another type in the various assignment operators, which is the normal sense of assignment, but your purpose of doing so is simply to provide the source value in order to extract it subsequently as the target type. That seems an abuse of assignment.
If you have a class C1 you can define the following operator
C1& operator=(C2 const& rhs);
As I understand this, we are converting a C2 to a C1 via an assignment.
A constructor with such an argument is, certainly, a converting constructor. While I don't think of assignment in the same terms, it is not much of a stretch to consider it similar. Nevertheless, the assignments in your case are to a converter, not to the target type, which I still contend is abusing assignment.
The assign_to function is a way do it extrinsically, i.e. outside any of the classes C1, C2.
C1& assign_to(C1 & lhs, C2 const& rhs);
We can say assign_to is to C1& operator=(C1&,C2const&) as boost::swap(C&lhs,C&rhs) is to C::swap(C& rhs).
Your assignment operators are not on the target type, but on a converting proxy. Using assign_to() implies that the operation is lhs = rhs, but that's incorrect. The operation is, instead, lhs = f(rhs), where f() applies some transformation to rhs to produce lhs. Hence, "assign_to" is the wrong name. _____ Rob Stewart robert.stewart@sig.com Software Engineer using std::disclaimer; Dev Tools & Components Susquehanna International Group, LLP http://www.sig.com IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

Message du 04/05/11 15:30 De : "Stewart, Robert" A : "'boost@lists.boost.org'" Copie à : Objet : Re: [boost] [review] string convert
Vicente BOTET wrote:
De : "Stewart, Robert"
Vicente BOTET wrote:
De : "Stewart, Robert"
What your three options don't address, then, is indicating conversion failure, without an exception, for a type with neither a default constructor nor a usable sentinel value. That was the purpose of Vladimir's result type: it could be queried for success and for the value.
Please, check the post around May 03, 2011; 10:09pm that includes a file with the complete post without angle brackets issues. There you will find all the cases.
I thought you were proposing those three options as the solution. I now understand that you still meant for an error_code overload or a no_throw overload to be the means to determine whether conversion fails without relying on fallback values.
The latter involves returning a pair which I dislike. boost::optional would be better as it supports safe-bool and a more meaningful get() for extracting the value (versus .second and .first for your pair).
If there's truly value in using an error_code, that interface is good because it affords the ability to provide more specific diagnostics. Otherwise, I'd prefer the optional interface with nothrow or even a "try" variant to avoid the argument.
Yes, the try variant is the most appealing to me to mean no_throw.
I haven't looked at the design of your library, but presumably these assignment operators are on a conversion type. Your conversion type's state is being set from another type in the various assignment operators, which is the normal sense of assignment, but your purpose of doing so is simply to provide the source value in order to extract it subsequently as the target type. That seems an abuse of assignment.
If you have a class C1 you can define the following operator
C1& operator=(C2 const& rhs);
As I understand this, we are converting a C2 to a C1 via an assignment.
A constructor with such an argument is, certainly, a converting constructor. While I don't think of assignment in the same terms, it is not much of a stretch to consider it similar. Nevertheless, the assignments in your case are to a converter, not to the target type, which I still contend is abusing assignment.
The type traits extension operator doesn't make a name difference when the type is the same or different, it uses just has_assign can_call_assign, can_assign or whatever name.
The assign_to function is a way do it extrinsically, i.e. outside any of the classes C1, C2.
C1& assign_to(C1 & lhs, C2 const& rhs);
Your assignment operators are not on the target type, but on a converting proxy. Using assign_to() implies that the operation is lhs = rhs, but that's incorrect. The operation is, instead, lhs = f(rhs), where f() applies some transformation to rhs to produce lhs. Hence, "assign_to" is the wrong name.
Note that the default behavior of assign_to is lhs = rhs. Only the assign_to user overloading will make something different because in C++ we can not extrinsically the operator=. Think of the assign_to overloading as if the user were able to make the following free overloading C1& operator=(C1&, C2 const&); as is not correct in C++. But if it was possible, how would you call this operator. Following your reasoning convert_to will not be a good name as in my library the default behavior it implies returning Target(rhs), that is calling the conversion operator/conversion constructor. But this is not the case when the user overload the convert_to function as the returned type should be assimilated to some transformation f of the rhs to get a Target. something like Target(f(rhs)). Best, Vicente

Vicente BOTET wrote:
De : "Stewart, Robert"
Vicente BOTET wrote:
De : "Stewart, Robert"
Vicente BOTET wrote:
De : "Stewart, Robert"
The assign_to function is a way do it extrinsically, i.e. outside any of the classes C1, C2.
C1& assign_to(C1 & lhs, C2 const& rhs);
Your assignment operators are not on the target type, but on a converting proxy. Using assign_to() implies that the operation is lhs = rhs, but that's incorrect. The operation is, instead, lhs = f(rhs), where f() applies some transformation to rhs to produce lhs. Hence, "assign_to" is the wrong name.
Note that the default behavior of assign_to is lhs = rhs. Only the assign_to user overloading will make something different because in C++ we can not extrinsically the operator=. Think of the assign_to overloading as if the user were able to make the following free overloading
C1& operator=(C1&, C2 const&);
as is not correct in C++. But if it was possible, how would you call this operator.
IOW, in your library, f() is a built-in conversion or promotion when possible, which is good. When it isn't possible, then it is some conversion operation that must be applied. Your idea of a free assignment operator is interesting. It certainly does fit the model pretty well. However, C++ programmers don't think in terms of an extrinsic assignment operator but of conversion functions, factory functions, etc. I'm still not completely comfortable with "assign_to" but you're making headway!
Following your reasoning convert_to will not be a good name as in my library the default behavior it implies returning Target(rhs), that is calling the conversion operator/conversion constructor. But this is not the case when the user overload the convert_to function as the returned type should be assimilated to some transformation f of the rhs to get a Target. something like Target(f(rhs)).
Nope. "convert_to" is "f" in my discussion above. It *is* the conversion function, regardless of how it is implemented. _____ Rob Stewart robert.stewart@sig.com Software Engineer using std::disclaimer; Dev Tools & Components Susquehanna International Group, LLP http://www.sig.com IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

Hi Vicente, On May 4, 2011, at 8:57 AM, Vicente BOTET wrote:
Please, check the post around May 03, 2011; 10:09pm that includes a file with the complete post without angle brackets issues. There you will find all the cases.
I'm reposting that message with my own comments. Vicente, you need to be able to receive messages off-list (and send angle brackets ;-) if you're submitting libraries for review.
Vladimir,
you are right. My library is not designed to take care of yours needs, and never was. I could do my best trying to update my library if there is enough interest and agreement in the interface. Next follows how I see the interface that could respond to the Boost.Convert features:
In order to take care of types without default constructor I see several options:
* Add an overloading having a default parameter
template <typename Target, typename Source> Target convert_to(Source const& s, Target const& t);
and use it as follows
Target t = convert_to<Target>(s, Target(x));
* Use the existing assign_to function
template <typename Target, typename Source> Target& assign_to(Target& t, Source const& s);
as follows
Target t (x); assign_to(t, s);
* Add a meta-function that returns a default value for a type
template <typename T> struct default_value { T apply() { return T(); } };
The user will need to specialize this function for types that are not default constructible.
This meta-function will be used instead of declaring a defaulted variable in the string conversion function.
T v(default_value<T>::apply());
As a user I would prefer the second one and then the last one it is transparent except when the specialization is needed. The first option is not appealing for my taste.
The first option is the only one that allows specifying a locally appropriate fallback value in a single line. Conceivably another spelling could be (Perl || style) if_else(convert_cast<int>(s, dont_throw_), 17) or convert_cast<int>(s, dont_throw_).get_value_or(17) or, combining with above convert_cast<optional<int> >(s).get_value_or(17) Also don't forget Vladimir's alternate syntax convert_cast<int>(s, fallback_ = 17)
In order to take care of no throwing semantics, I will choose to add a convert_to overloading with a error code parameter, not throwing without not knowing why fails seems too specific to me.
template <typename Target, typename Source> Target convert_to(Source const& s, err_code& ec);
Target t = convert_to<Target>(s, ec); if (ec ....
Hmm, nice.
But I could understand there are some cases where the single reason is a bad format. We could also use the no_throw_t as additional parameter, but in this case the return type should allow to know if the operation has succeed or not. If we follow the C++ standard interface the use of pair seems to be the more appropriated.
template <typename Target, typename Source> pair<T,bool> convert_to(Source const& s, no_thow_t const&);
pair<T,bool> p = convert_to<T>(s, no_thow); if (p.second)
Note also that there could be some conversions that could transport whether the conversion succeeds or not
optional<Target> t = convert_to<optional<Target> >(s);
This conversion could be overloaded and never throw.
That would nicely eliminate one of the three tags I was suggesting.
or a Robert suggested overload the pointer to a source
template <typename Target, typename Source> optional<Target> convert_to(Source const* s);
I find that creative but confusing; would you really pass char ** if your input was a c-string?
I think these could be added to Boost.Conversion without not to much trouble. I could do my best for the Boost.Conversion part, if there is enough interest and agreement in the interface.
My concern about adding string-to-type to Boost.Conversion is that lexical_cast does type-to-string-to-type as a fallback for everything. Does that make sense for Boost.Conversion?
Respect to the stream manipulators, what do we want? output an output streameable type and extract another type after some manipulations.
Source s; a_iostream ios; Target t;
ios << s; ios >> m1 >> m2 >> t;
But you want to hide the intermediary iostream. As I stated on one of my post it will be great if we can define a specific input stream that is built from an output stremable type that allows to extract the value stating whatever format is needed. What about
as_istream(s) >> m1 >> m2 >> t;
istringstream is fine for that. But again, people want to declare a variable and assign its value from a conversion in one line of code. IMO that's an absolute requirement for any lexical_cast replacement. So you end up with convert_cast<int>("feed", format_ = std::hex); Or something like that.
Respect to the functor I think that we need to use Phoenix syntax and overload the functions with a Phoenix placeholder to create the functor. So
convert_to<Target, Source>(_1)
will return a functor that can be called with one Source parameter and return a Target. The same applies to each one of the other functions.
In this way each feature is separated and compassable, avoiding a bloated class that do everything.
The generic part of Boost.Conversion will not take care are the string conversions and the input stream view. Boost.StringConvert could take care of the string specializations.
Are you suggesting that they are still two different libraries? I am still not sure if these really are the same problem, although they might have similar interfaces. Boost.Coerce also needs to be considered. Jeroen, have you been following this?
I don't know if there are some features I have miss. Please let me know.
Mostly dealing with fallback and manipulators within a single expression. HTH, Gordon

On 5/3/2011 1:26 PM, Stewart, Robert wrote:
Unfortunately, I realized that wouldn't work well for string literals, for example.
I had no idea that dynamic_cast behavior differed between reference and pointers types. I'm no C++ guru but I'm no amateur either, so I don't think that's a good model to follow even if it did work for string literals. C# has Parse (returns value and throws if parse fails) and TryParse (returns bool if parse succeeds and returns parsed value in a variable passed by reference) for each primitive numeric type. It's very intuitive but not very generic. So I can definitely see a convert_to and try_convert_to but I'd rather simply have a no-throw version of lexical_cast: 'try_lexical_cast'. The default construct-ability use case is really a hassle though. I think it needs to be discussed how important it is for simple lexical conversion to support non-default-constructable target types. The "simple things should be simple" principle seems very applicable here. -Matt

Message du 03/05/11 18:26 De : "Vicente BOTET" A : boost@lists.boost.org Copie à : Objet : Re: [boost] [review] string convert
Message du 03/05/11 11:11 De : "Vladimir Batov" A : boost@lists.boost.org Copie à : Objet : Re: [boost] [review] string convert
Vicente BOTET wanadoo.fr> writes: The library provides a convert_to function that adds the specific dummy
Vladimir Batov people.net.au> writes: parameter. I recognize this is
tricky and that the overloading seems not natural from the user perspective but it works at least for the cases I have tested.
Vicente, Gordon,
Thank you for the pointer, I had a quick look and indeed it seems to solve the specialization problem I was facing. Should we cancel/postpone this review and review Vicente's and mine together or something of that sort? If Vicente's is better as a starting point, it's fine by me. I am happy either way a s long as things are moving.
Vicente,
I had a quick look and your proposal. Conceptually it seems to be an extension of lexical_cast philosophy on to type-to-type conversions. It does not seem to provide any of the functionality that I (as a user) care about, i.e., fallback value, throwing/non-throwing, formatting, locales, etc. Given convert_to actually returns a Target value, I suspect, formatting, locales, etc. are simply not possible as I believe we have to have an intermediary object (converter) to apply additional formatting, etc. Am I correct in my reading?
If so, then you and I are trying to address totally unrelated issues. I needed 'convert' as lexical_cast not not doing what I needed it to do. I do not think I immediately have a need for type-to-type extension of lexical_cast.
If there is a problem with less and greater symbols, please open the attached file (I've some configuration issues) Vladimir, you are right. My library is not designed to take care of yours needs, and never was. I could do my best trying to update my library if there is enough interest and agreement in the interface. Next follows how I see the interface that could respond to the Boost.Convert features: In order to take care of types without default constructor I see several options: * Add an overloading having a default parameter template Target convert_to(Source const& s, Target const& t); and use it as follows Target t = convert_to(s, Target(x)); * Use the existing assign_to function template Target& assign_to(Target& t, Source const& s); as follows Target t (x); assign_to(t, s); * Add a meta-function that returns a default value for a type template struct default_value { T apply() { return T(); } }; The user will need to specialize this function for types that are not default constructible. This meta-function will be used instead of declaring a defaulted variable in the string conversion function. T v(default_value::apply()); As a user I would prefer the second one and then the last one it is transparent except when the specialization is needed. The first option is not appealing for my taste. In order to take care of no throwing semantics, I will choose to add a convert_to overloading with a error code parameter, not throwing without not knowing why fails seems too specific to me. template Target convert_to(Source const& s, err_code& ec); Target t = convert_to(s, ec); if (ec .... But I could understand there are some cases where the single reason is a bad format. We could also use the no_throw_t as additional parameter, but in this case the return type should allow to know if the operation has succeed or not. If we follow the C++ standard interface the use of pair seems to be the more appropriated. template pair convert_to(Source const& s, no_thow_t const&); pair p = convert_to(s, no_thow); if (p.second) Note also that there could be some conversions that could transport whether the conversion succeeds or not optional t = convert_to >(s); This conversion could be overloaded and never throw. or a Robert suggested overload the pointer to a source template optional convert_to(Source const* s); Both mechanism can be mixed template Target convert_to(Source const& s, Target const& t, err_code& ec); Target t = convert_to(s, Target(x), ec); if (ec ... template Target& assign_to(Target& t, Source const& s, err_code& ec); Target t (x); assign_to(t, s, ec); if (ec ... I think these could be added to Boost.Conversion without not to much trouble. I could do my best for the Boost.Conversion part, if there is enough interest and agreement in the interface. Respect to the stream manipulators, what do we want? output an output streameable type and extract another type after some manipulations. Source s; a_iostream ios; Target t; ios << s; ios >> m1 >> m2 >> t; But you want to hide the intermediary iostream. As I stated on one of my post it will be great if we can define a specific input stream that is built from an output stremable type that allows to extract the value stating whatever format is needed. What about as_istream(s) >> m1 >> m2 >> t; Respect to the functor I think that we need to use Phoenix syntax and overload the functions with a Phoenix placeholder to create the functor. So convert_to(_1) will return a functor that can be called with one Source parameter and return a Target. The same applies to each one of the other functions. In this way each feature is separated and compassable, avoiding a bloated class that do everything. The generic part of Boost.Conversion will not take care are the string conversions and the input stream view. Boost.StringConvert could take care of the string specializations. I don't know if there are some features I have miss. Please let me know. Best, Vicente

Message du 03/05/11 18:26 De : "Vicente BOTET" A : boost@lists.boost.org Copie à : Objet : Re: [boost] [review] string convert
Message du 03/05/11 11:11 De : "Vladimir Batov" A : boost@lists.boost.org Copie à : Objet : Re: [boost] [review] string convert
Vicente BOTET wanadoo.fr> writes: The library provides a convert_to function that adds the specific dummy
Vladimir Batov people.net.au> writes: parameter. I recognize this is
tricky and that the overloading seems not natural from the user perspective but it works at least for the cases I have tested.
Vicente, Gordon,
Thank you for the pointer, I had a quick look and indeed it seems to solve the specialization problem I was facing. Should we cancel/postpone this review and review Vicente's and mine together or something of that sort? If Vicente's is better as a starting point, it's fine by me. I am happy either way a s long as things are moving.
Vicente,
I had a quick look and your proposal. Conceptually it seems to be an extension of lexical_cast philosophy on to type-to-type conversions. It does not seem to provide any of the functionality that I (as a user) care about, i.e., fallback value, throwing/non-throwing, formatting, locales, etc. Given convert_to actually returns a Target value, I suspect, formatting, locales, etc. are simply not possible as I believe we have to have an intermediary object (converter) to apply additional formatting, etc. Am I correct in my reading?
If so, then you and I are trying to address totally unrelated issues. I needed 'convert' as lexical_cast not not doing what I needed it to do. I do not think I immediately have a need for type-to-type extension of lexical_cast.
I have a problem with less and greater symbols, please open the attached file that corresponds to my preceding mail (I've some configuration issues). Sorry for the inconvenience. Vicente

Vladimir Batov wrote:
Gordon Woodhull <gordon <at> woodhull.com> writes:
On a similar nice-feature-but-weird-syntax note, let's look at the Using Boost.Convert with Standard Algorithms section, which offers a few ways that you can use convert<T>::from() to create a functor:
std::transform( strings.begin(), strings.end(), std::back_inserter(integers), convert<int>::from(string(), -1) >> std::hex); ... I think it would be much more appropriate to provide a Phoenix function object for the purpose, something like
std::transform( strings.begin(), strings.end(), std::back_inserter(integers), convert_<int>(_1, -1, std::hex));
Sigh of relief. I see what that means immediately, ...
Apologies for sounding as a broken record but again that preference seems quite personal. As a user not familiar with Phoenix your variant would not look familiar to me. In order to use it I'd need to learn/remember/know additional API -- convert_, _1, args order. My deployment seems simpler (not surprisingly some bias here :-) ) as the user uses the same API and does not need to learn more to use 'convert' with algorithms.
I didn't mention it before, but I do think Gordon's suggestion is a decent one. Your concern, Vladimir, is valid, but it is reasonably easy to teach enough Phoenix to explain how to use the converter function objects that way and would be helpful to Convert library users in other contexts (not that it is your responsibility to teach them lambdas for other contexts!). I do realize that generalizing in this way means that users would see whatever template error stack traces mishandling Phoenix would trigger. That's a decided problem with adopting that interface, but in order to use an increasing number of libraries, one must learn a lambda library. Perhaps focusing on enabling lambdas generally, particularly C++1x lambdas, is a better approach than specifically focusing on Phoenix. _____ Rob Stewart robert.stewart@sig.com Software Engineer using std::disclaimer; Dev Tools & Components Susquehanna International Group, LLP http://www.sig.com IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.
participants (13)
-
Barend Gehrels
-
Gordon Woodhull
-
Ivan Le Lann
-
Jeff Flinn
-
Jeremy Maitin-Shepard
-
Marsh Ray
-
Matt Chambers
-
Matthew Chambers
-
Phil Endecott
-
Scott McMurray
-
Stewart, Robert
-
Vicente BOTET
-
Vladimir Batov