[convert] Default Syntax Idea

I was thinking a bit about syntax for the default value for the notional boost::convert() and kin. How about the following: int const i(3); string const s("something"); int const j(convert<int>(s) | 5); convert<DestType,SourceType>(SourceType) can return a type which throws on failure in operator DestType(), but has an operator |(SourceType) which returns a second type constructed with the SourceType value. The second type provides its own operator |(SourceType) which returns that value in its operator DestType() on failure. Returning a default value in lieu of an exception is a special case; it ought not be treated like formatting. Thus, while Boost.Parameter might be appropriate to package the extra information desired for formatting, I don't like the idea of writing "default_ = 5" in the call, and I don't think ", 5" in the argument list is as clear as it could be. The syntax above is obvious and concise while being distinct from whatever may be done for formatting. _____ Rob Stewart robert.stewart@sig.com Software Engineer, Core Software using std::disclaimer; 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:
I was thinking a bit about syntax for the default value for the notional boost::convert() and kin. How about the following:
int const i(3); string const s("something"); int const j(convert<int>(s) | 5);
convert<DestType,SourceType>(SourceType) can return a type which throws on failure in operator DestType(), but has an operator |(SourceType) which returns a second type constructed with the SourceType value. The second type provides its own operator |(SourceType) which returns that value in its operator DestType() on failure.
Returning a default value in lieu of an exception is a special case; it ought not be treated like formatting. Thus, while Boost.Parameter might be appropriate to package the extra information desired for formatting, I don't like the idea of writing "default_ = 5" in the call, and I don't think ", 5" in the argument list is as clear as it could be. The syntax above is obvious and concise while being distinct from whatever may be done for formatting.
That's a nice idea. The default value may indeed be special enough to support this syntax.

"Andrey Semashev" <andrey.semashev@gmail.com> wrote convert< string >((param1 = val1, param2 = val2, ...));
Andrey, I started playing with your example integrating it in to the conversion framework and managed to get it working. Thank you for the example and that parameter_type extension. The whole thing seems to rely on that facility. Well, the interface I am using (without Boost.Parameter) is int i = convert::to<int>(str); int i = convert::from(str, -1); (almost what I initially started from :-) ) I tried to get rid of 'to&from' (several times) but in the end decided I needed to keep the two variations as the 'from' deduces both (TypeIn, TypeOut) parameters from the signature when the 'to' requires TypeOut explicitly specified. Having both named convert() reads wrong for the 'to' specialization: int i = convert<int>(str); and I could not stand it. Another option would be to ditch 'to' altogether but I am not sure how it might be taken. Also I tried deducing TypeOut parameter for 'to' as well via template<class TypeIn> implementation { template<class TypeOut> operator TypeOut() }; but did not get too far either. Well, I digressed from my original post about Boost.Parameter as I did not want us to get bogged down with convert::to and convert::from again (and BTW you cannot shortcut them so they are very much like convert_to, convert_from). OK. What I got working (thanks to your help) is int i = convert::to<int>(str, radix_ = 10); int i = convert::from(str, default_ = -1); int i = convert::from(str, (default_ = -1, radix_ = 10)); I understand the benefits of passing additional conversion parameters like 'radix_ = 10' down to the conversion *implementation*. However, I feel these parameters are of no value (and not accessible) to the "casual" user (as opposed to the conversion implementer)... unlike I/O Stream-based approach which allows the user to write his own manipulator and deploy it like int i = convert::from(str, -1) >> my_special_formatter; Is my understanding correct or you have some ideas of getting that *user* extensibility with Boost.Parameter? Secondly, I kinda like the explicitness of int i = convert::from(str, default_ = -1); however, does it really add much (apart from verbosity) compared with int i = convert::from(str, -1); I do not feel the above line is the trickiest in the whole Boost so that we need to get out of our way to spell out every damn thing. What do you think? That leads me to my question. Would that be sensible to have it as follows: int i = convert::to<int>(str, (radix_ = 10, locale_= ...)); int i = convert::from(str, -1, (radix_ = 10, locale_= ...)); or you might have something else in mind? Best, V.

Vladimir Batov wrote: [snip] Just to restate my opinion, I don't quite like the "to" and "from" names. At least "from", in your notation, is confusing to me.
OK. What I got working (thanks to your help) is
int i = convert::to<int>(str, radix_ = 10); int i = convert::from(str, default_ = -1); int i = convert::from(str, (default_ = -1, radix_ = 10));
I understand the benefits of passing additional conversion parameters like 'radix_ = 10' down to the conversion *implementation*. However, I feel these parameters are of no value (and not accessible) to the "casual" user (as opposed to the conversion implementer)...
I don't understand what exactly you are trying to achieve. The conversion logic has to be concentrated somewhere, and I think it should be somewhere related to the source or the target type. In the nearby thread we were discussing conversion of user-defined types: namespace user { struct uuid; } user::uuid u; std::string str = convert::to< std::string >(u); Do you plan to support this use case? How would you suggest the "casual user" to extend the conversion to std::string str = convert::to< std::string >(u, uppercase_ = true); without support from the underlying conversion implementation for user::uuid?
unlike I/O Stream-based approach which allows the user to write his own manipulator and deploy it like
int i = convert::from(str, -1) >> my_special_formatter;
Do I understand correctly that the manipulators are used to format or parse the input by defining their streaming operators? If so, then how do the manipulators compose with each other? If I write: int i = convert::from(str, -1) >> my_special_formatter >> my_super_special_formatter; which one will be called?
Secondly, I kinda like the explicitness of
int i = convert::from(str, default_ = -1);
however, does it really add much (apart from verbosity) compared with
int i = convert::from(str, -1);
I do not feel the above line is the trickiest in the whole Boost so that we need to get out of our way to spell out every damn thing. What do you think?
I think that the default value should be immediately recognizable. The default_ keyword does that. There were alternative syntax suggestions that provided that, too. I think, we should strive to the cleanest interface we can provide.
That leads me to my question. Would that be sensible to have it as follows:
int i = convert::to<int>(str, (radix_ = 10, locale_= ...)); int i = convert::from(str, -1, (radix_ = 10, locale_= ...));
or you might have something else in mind?
If you declare conversion functions with Boost.Parameter macros, you will be able to get rid of the inner parenthesis: int i = convert::to<int>(str, radix_ = 10, locale_= ...); int i = convert::from(str, -1, radix_ = 10, locale_= ...); That way the default value really gets lost in the arguments. So, unless we move to an alternative syntax, I'm for making it a named parameter as well: int i = convert::from(str, default_ = -1, radix_ = 10, locale_= ...); The particular "default_" name, however, is not what I'm tied with.

From: "Andrey Semashev" <andrey.semashev@gmail.com> Just to restate my opinion, I don't quite like the "to" and "from" names. At least "from", in your notation, is confusing to me.
Yes, I remember. :-) We happen to like different things. So far I find to/from the cleanest of all the alternatives. Somehow you insist on int i = convert<int>(str) that in turn I do not find that great. ;-)
I don't understand what exactly you are trying to achieve. The conversion logic has to be concentrated somewhere,
The conversion logic for user-defined typed is often merely to provide op>> and op<< (as lexical_cast integrates user types). When one needs more, additional conversion logic is often concentrated in I/O Stream manipulators.
and I think it should be somewhere related to the source or the target type. In the nearby thread we were discussing conversion of user-defined types:
namespace user { struct uuid; }
user::uuid u; std::string str = convert::to< std::string >(u);
Do you plan to support this use case? How would you suggest the "casual user" to extend the conversion to
std::string str = convert::to< std::string >(u, uppercase_ = true);
Yes, it's is a typical case. All needed to have it integrated is to provide op>> and op<<. That is std::ostream& operator<<(std::ostream& str, uuid const& id) { str << id.as_string(); return str; } and a similar op>> get uuid integrated. I.e. nothing close to the complexity of your example. If you need additional (like "uppercase") configuration, one writes uppercase manipulator that will pass an additional parameter to op<<. Like string s = convert::to<string>(uuid) >> uppercase; std::ostream& operator<<(std::ostream& str, uuid const& id) { ...extract uppercase command from the stream... str << id.as_string(); return str; }
without support from the underlying conversion implementation for user::uuid?
unlike I/O Stream-based approach which allows the user to write his own manipulator and deploy it like
int i = convert::from(str, -1) >> my_special_formatter;
Do I understand correctly that the manipulators are used to format or parse the input by defining their streaming operators? If so, then how do the manipulators compose with each other? If I write:
int i = convert::from(str, -1) >> my_special_formatter >> my_super_special_formatter;
which one will be called?
They are called in the order or their appearance. Sometimes it matters, sometimes it does not. Like std::ws (enabled by default) needs to be first.
I think that the default value should be immediately recognizable. The default_ keyword does that. There were alternative syntax suggestions that provided that, too. I think, we should strive to the cleanest interface we can provide.
Oh, well, sounds great... but I am not sure. Feels like you are chasing that immediate recognize-ability a bit too hard. People somehow have survived without exlicitly naming every parameter and convert(str, -1) does not look the trickiest of all. And somehow you are not that concerned with the verbosity any more. I remember boost::string::from(s, -1); seemed long. Now convert::from(s, default = -1); does not seem that long at all. Nuh, that "immediate recognize-ability" pitch somehow does not stick with me. Sorry.
If you declare conversion functions with Boost.Parameter macros, you will be able to get rid of the inner parenthesis:
int i = convert::to<int>(str, radix_ = 10, locale_= ...); int i = convert::from(str, -1, radix_ = 10, locale_= ...);
Yes, I know. I am not that keen though... and does not it have some limit on the number of arguments?
That way the default value really gets lost in the arguments. So, unless we move to an alternative syntax, I'm for making it a named parameter as well:
int i = convert::from(str, default_ = -1, radix_ = 10, locale_= ...);
Yes, in this setting all parameters kinda asking for a name. A different style I guess. I am not sure it's growing on me. We'll see. V.

From: "Vladimir Batov" <batov@people.net.au>
int i = convert::from(str, default_ = -1, radix_ = 10, locale_= ...);
How about int i = convert::from(str, -1)(radix_ = 10, locale_= ...); and both styles seem to coexist happily. I'd expect many people to be comfortable with I/O Stream-based approach. As above we'd accommodate all. A "unifying" compromise? V.

Vladimir Batov wrote:
From: "Vladimir Batov" <batov@people.net.au>
int i = convert::from(str, default_ = -1, radix_ = 10, locale_= ...);
How about
int i = convert::from(str, -1)(radix_ = 10, locale_= ...);
and both styles seem to coexist happily. I'd expect many people to be comfortable with I/O Stream-based approach. As above we'd accommodate all. A "unifying" compromise?
Yes, that's a possible alternative.

Vladimir Batov wrote:
From: "Vladimir Batov" <batov@people.net.au>
int i = convert::from(str, default_ = -1, radix_ = 10, locale_= ...);
How about
int i = convert::from(str, -1)(radix_ = 10, locale_= ...);
and both styles seem to coexist happily. I'd expect many people to be comfortable with I/O Stream-based approach. As above we'd accommodate all. A "unifying" compromise?
...or even better: int i = convert::from(str, -1)[radix_ = 10, locale_= ...]; so that the "limited number of arguments" issue doesn't arise anymore.

Vladimir Batov wrote:
I don't understand what exactly you are trying to achieve. The conversion logic has to be concentrated somewhere,
The conversion logic for user-defined typed is often merely to provide op>> and op<< (as lexical_cast integrates user types). When one needs more, additional conversion logic is often concentrated in I/O Stream manipulators.
and I think it should be somewhere related to the source or the target type. In the nearby thread we were discussing conversion of user-defined types:
std::string str = convert::to< std::string >(u, uppercase_ = true);
Yes, it's is a typical case. All needed to have it integrated is to provide op>> and op<<. That is
std::ostream& operator<<(std::ostream& str, uuid const& id) { str << id.as_string(); return str; }
and a similar op>> get uuid integrated. I.e. nothing close to the complexity of your example.
I'm all for using the user-defined operator<< for default lexical_cast-style conversion. The code snippet I posted did not support it as I did not invest much time in it. What isn't clear to me is how the more elaborate cases will be covered by your library.
If you need additional (like "uppercase") configuration, one writes uppercase manipulator that will pass an additional parameter to op<<. Like
string s = convert::to<string>(uuid) >> uppercase;
std::ostream& operator<<(std::ostream& str, uuid const& id) { ...extract uppercase command from the stream... str << id.as_string(); return str; }
Two notes regarding this: 1. If I understand you correctly, the "casual" user won't be able to extend the conversion, because the operator<< defined by the uuid developer still has to know what kind of hint the manipulator may have put into the stream. 2. It's not clear to me how the conversion hint will be put into the stream. What's behind the "...extract uppercase command from the stream..." line?
Do I understand correctly that the manipulators are used to format or parse the input by defining their streaming operators? If so, then how do the manipulators compose with each other? If I write:
int i = convert::from(str, -1) >> my_special_formatter >> my_super_special_formatter;
which one will be called?
They are called in the order or their appearance. Sometimes it matters, sometimes it does not. Like std::ws (enabled by default) needs to be first.
I assume, you mean that the manipulators don't actually do formatting/parsing, but instead do something tricky with the stream, am I right? If so, it begins to look like you're trying to invent a run-time alternative to Boost.Parameter with a different syntax.
I think that the default value should be immediately recognizable. The default_ keyword does that. There were alternative syntax suggestions that provided that, too. I think, we should strive to the cleanest interface we can provide.
Oh, well, sounds great... but I am not sure. Feels like you are chasing that immediate recognize-ability a bit too hard.
You asked my opinion, didn't you? :)
If you declare conversion functions with Boost.Parameter macros, you will be able to get rid of the inner parenthesis:
int i = convert::to<int>(str, radix_ = 10, locale_= ...); int i = convert::from(str, -1, radix_ = 10, locale_= ...);
Yes, I know. I am not that keen though... and does not it have some limit on the number of arguments?
Well, everything has its limits. Are up to 10 arguments limiting for you? 20? 50? Since we're talking about syntax nuances, trying to polish the interface, having more than 10 arguments for type conversion is a bit out of line, no matter how these arguments are specified.

On Saturday, February 28, 2009 10:22 AM Andrey Semashev wrote:
Vladimir Batov wrote:
If you need additional (like "uppercase") configuration, one writes uppercase manipulator that will pass an additional parameter to op<<. Like
string s = convert::to<string>(uuid) >> uppercase;
std::ostream& operator<<(std::ostream& str, uuid const& id) { ...extract uppercase command from the stream... str << id.as_string(); return str; }
Two notes regarding this: 1. If I understand you correctly, the "casual" user won't be able to extend the conversion, because the operator<< defined by the uuid developer still has to know what kind of hint the manipulator may have put into the stream.
The uuid author will create both the insertion operator and the manipulators it recognizes.
2. It's not clear to me how the conversion hint will be put into the stream. What's behind the "...extract uppercase command from the stream..." line?
If IOStreams don't already support the required formatting option, then it must be attached to the stream via its iword/pword/xalloc interfaces.
Do I understand correctly that the manipulators are used to format or parse the input by defining their streaming operators? If so, then how do the manipulators compose with each other? If I write:
int i = convert::from(str, -1) >> my_special_formatter >> my_super_special_formatter;
which one will be called?
Both are called and the interoperate or work orthogonally as warranted by their intended behavior and use of the iword/pword/xalloc APIs.
I assume, you mean that the manipulators don't actually do formatting/parsing, but instead do something tricky with the stream, am I right?
If so, it begins to look like you're trying to invent a run-time alternative to Boost.Parameter with a different syntax.
Quite the contrary. It's an interface built upon IOStreams, which predates Boost.Parameter. Which is better, or whether 'tis better to follow the Spirit path, is yet open to debate.
I think that the default value should be immediately recognizable. The default_ keyword does that. There were alternative syntax suggestions that provided that, too. I think, we should strive to the cleanest interface we can provide.
I certainly agree with that. _____ Rob Stewart robert.stewart@sig.com Software Engineer, Core Software using std::disclaimer; 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, the Boost.Range library introduce the concept of adaptor (or view). I'm wondering if this could be also convenient for the Boost.Convert library. Take the uuid user data type. uuid u; string res; u | corverted_to<string> | uppercased > res; The evaluation should be lazy, only when we assign throwg the > operator we force the evaluation. or string str; int i = str | radix(10) | locale(...) | converted_to<int>; Could this approach work for the Boost.Convert library? Best Vicente ----- Original Message ----- From: "Vladimir Batov" <batov@people.net.au> To: <boost@lists.boost.org> Sent: Saturday, February 28, 2009 2:37 PM Subject: Re: [boost] Boost.Convert+Boost.Parameter
From: "Andrey Semashev" <andrey.semashev@gmail.com> Just to restate my opinion, I don't quite like the "to" and "from" names. At least "from", in your notation, is confusing to me.
Yes, I remember. :-) We happen to like different things. So far I find to/from the cleanest of all the alternatives. Somehow you insist on
int i = convert<int>(str)
that in turn I do not find that great. ;-)
I don't understand what exactly you are trying to achieve. The conversion logic has to be concentrated somewhere,
The conversion logic for user-defined typed is often merely to provide op>> and op<< (as lexical_cast integrates user types). When one needs more, additional conversion logic is often concentrated in I/O Stream manipulators.
and I think it should be somewhere related to the source or the target type. In the nearby thread we were discussing conversion of user-defined types:
namespace user { struct uuid; }
user::uuid u; std::string str = convert::to< std::string >(u);
Do you plan to support this use case? How would you suggest the "casual user" to extend the conversion to
std::string str = convert::to< std::string >(u, uppercase_ = true);
Yes, it's is a typical case. All needed to have it integrated is to provide op>> and op<<. That is
std::ostream& operator<<(std::ostream& str, uuid const& id) { str << id.as_string(); return str; }
and a similar op>> get uuid integrated. I.e. nothing close to the complexity of your example. If you need additional (like "uppercase") configuration, one writes uppercase manipulator that will pass an additional parameter to op<<. Like
string s = convert::to<string>(uuid) >> uppercase;
std::ostream& operator<<(std::ostream& str, uuid const& id) { ...extract uppercase command from the stream... str << id.as_string(); return str; }
without support from the underlying conversion implementation for user::uuid?
unlike I/O Stream-based approach which allows the user to write his own manipulator and deploy it like
int i = convert::from(str, -1) >> my_special_formatter;
Do I understand correctly that the manipulators are used to format or parse the input by defining their streaming operators? If so, then how do the manipulators compose with each other? If I write:
int i = convert::from(str, -1) >> my_special_formatter >> my_super_special_formatter;
which one will be called?
They are called in the order or their appearance. Sometimes it matters, sometimes it does not. Like std::ws (enabled by default) needs to be first.
I think that the default value should be immediately recognizable. The default_ keyword does that. There were alternative syntax suggestions that provided that, too. I think, we should strive to the cleanest interface we can provide.
Oh, well, sounds great... but I am not sure. Feels like you are chasing that immediate recognize-ability a bit too hard. People somehow have survived without exlicitly naming every parameter and convert(str, -1) does not look the trickiest of all. And somehow you are not that concerned with the verbosity any more. I remember
boost::string::from(s, -1);
seemed long. Now
convert::from(s, default = -1);
does not seem that long at all. Nuh, that "immediate recognize-ability" pitch somehow does not stick with me. Sorry.
If you declare conversion functions with Boost.Parameter macros, you will be able to get rid of the inner parenthesis:
int i = convert::to<int>(str, radix_ = 10, locale_= ...); int i = convert::from(str, -1, radix_ = 10, locale_= ...);
Yes, I know. I am not that keen though... and does not it have some limit on the number of arguments?
That way the default value really gets lost in the arguments. So, unless we move to an alternative syntax, I'm for making it a named parameter as well:
int i = convert::from(str, default_ = -1, radix_ = 10, locale_= ...);
Yes, in this setting all parameters kinda asking for a name. A different style I guess. I am not sure it's growing on me. We'll see.

the Boost.Range library introduce the concept of adaptor (or view). I'm wondering if this could be also convenient for the Boost.Convert library.
Take the uuid user data type.
uuid u; string res;
u | corverted_to<string> | uppercased > res;
The evaluation should be lazy, only when we assign throwg the > operator we force the evaluation.
or
string str;
int i = str | radix(10) | locale(...) | converted_to<int>;
Could this approach work for the Boost.Convert library?
Well, in this case I really prefer: using namespace spirit::std_locale; spirit::qi::parse(str, spirit::int_, i); or, if you need another radix: typedef spirit::qi::int_parser<int, 16> hex; spirit::qi::parse(str, hex(), i); or, if you need exactly 4 hex digits: typedef spirit::qi::int_parser<int, 16, 4, 4> hex4; spirit::qi::parse(str, hex4(), i); etc. Which is far less convoluted. Same/similar works for converting the other way around: typedef spirit::karma::int_generator<int, 16> hex; spirit::karma::generate(std::back_inserter(str), hex(), i); <rant> I still don't get it. I agree that it might be favorable to have a simple API for type<-->string conversions. But as you can clearly see the requirements are piling up and it's everything else but simple by now. So I ask again: What's the benefit of coming up with just another full blown parser/generator interface in Boost? What's the benefit of discarding/obsoleting lexical_cast<>() as the _really_ minimal, simple interface? Guy's. You're trying to come up with a DSEL (domain specific embedded language) for parsing and generating. What's it exactly you don't like in Spirits DSEL? Just the fact that it wasn't _you_ to come up with this particular syntax? Have you even looked? Wouldn't it be better to use the experience the Spirit developers/users have collected over _years_ (!) and try to find a simpler API on top of Spirit directly extending from the existing one, certainly while limiting the possibilities in favor of a reduced feature set and a simplified usage model? </rant> Regards Hartmut

Hartmut Kaiser wrote:
I still don't get it. I agree that it might be favorable to have a simple API for type<-->string conversions.
But as you can clearly see the requirements are piling up and it's everything else but simple by now.
So I ask again:
What's the benefit of coming up with just another full blown parser/generator interface in Boost? What's the benefit of discarding/obsoleting lexical_cast<>() as the _really_ minimal, simple interface?
Guy's. You're trying to come up with a DSEL (domain specific embedded language) for parsing and generating. What's it exactly you don't like in Spirits DSEL? Just the fact that it wasn't _you_ to come up with this particular syntax? Have you even looked?
Wouldn't it be better to use the experience the Spirit developers/users have collected over _years_ (!) and try to find a simpler API on top of Spirit directly extending from the existing one, certainly while limiting the possibilities in favor of a reduced feature set and a simplified usage model?
So far we were mostly discussing the interface. As for the implementation, I have nothing against using Spirit as a working horse for string-related conversions between the types it supports. However, I have expressed two requests that don't fit well (as far as I know) with Spirit: 1. Employ operator<< and operator>> for user-defined types. 2. Allow extension for non-string-related conversions as well. I believe that support for these requests can live side-by-side with the "powered by Spirit" part that handles ints, floats, etc.

I still don't get it. I agree that it might be favorable to have a simple API for type<-->string conversions.
But as you can clearly see the requirements are piling up and it's everything else but simple by now.
So I ask again:
What's the benefit of coming up with just another full blown parser/generator interface in Boost? What's the benefit of discarding/obsoleting lexical_cast<>() as the _really_ minimal, simple interface?
Guy's. You're trying to come up with a DSEL (domain specific embedded language) for parsing and generating. What's it exactly you don't like in Spirits DSEL? Just the fact that it wasn't _you_ to come up with this particular syntax? Have you even looked?
Wouldn't it be better to use the experience the Spirit developers/users have collected over _years_ (!) and try to find a simpler API on top of Spirit directly extending from the existing one, certainly while limiting the possibilities in favor of a reduced feature set and a simplified usage model?
So far we were mostly discussing the interface.
Sorry, if I didn't make myself clear. I was referring to the interface as well. IMHO, any simplified interface for conversion operations should be compatible with the full blown Spirit API to allow for easy migration in case things get more complicated. It wouldn't be good to have yet another DSEL just for the conversion ops.
As for the implementation, I have nothing against using Spirit as a working horse for string-related conversions between the types it supports. However, I have expressed two requests that don't fit well (as far as I know) with Spirit: 1. Employ operator<< and operator>> for user-defined types.
Spirit supports that out of the box in two ways. a) it has the 'stream' primitive utilizing the user type supplied operator>>() and operator<<() for parsing/generating. I.e. user_type u; spirit::qi::parse(str, spirit::stream, u); spirit::karma::generate(std::back_inserter(str), spirit::stream, u); use the user supplied streaming operators for user_type. b) it exposes manipulator objects allowing to integrate parsing and generation with std io streams. I.e. int i; std::cin >> spirit::qi::match(spirit::int_, i); std::cout << spirit::karma::format(spirit::int_, i);
2. Allow extension for non-string-related conversions as well.
Spirit parsing and generation API's are based on iterators, not strings. So this request is already implemented as well.
I believe that support for these requests can live side-by-side with the "powered by Spirit" part that handles ints, floats, etc.
Again, I'm talking about the interface here, not the implementation (which is superior, BTW - Spirits numeric primitives are faster and more accurate than most std library primitives supplied by the existing compilers). Regards Hartmut

From: "Hartmut Kaiser" <hartmut.kaiser@gmail.com>
1. Employ operator<< and operator>> for user-defined types.
Spirit supports that out of the box in two ways.
a) it has the 'stream' primitive utilizing the user type supplied operator>>() and operator<<() for parsing/generating. I.e.
user_type u; spirit::qi::parse(str, spirit::stream, u); spirit::karma::generate(std::back_inserter(str), spirit::stream, u);
Hartmut, 'parse' does str->user_type and 'generate' does user_type->str, right? Is it (the support for conversions and user types) anywhere in the documentation? If it all that easy, I'll wrap it up into the API of my liking and we'll use it instead of lexical_cast. The whole 'convert' saga seems for nothing. Given how long this lexical_cast/convert discussion's been dragging on I feel you might spend some time educating more people (besides me) how to deploy Spirit for mundane taks like conversion. How do you handle failed conversion? Where do I do to learn Spirit? The docs seem preoccupied with big stuff. [rant] As a side note I just love to see "spirit::qi::parse...spirit::stream" given some've could not stand my humble "convert::to" and "convert::from". I am sure they gonna love yours. LOL. Best, V.

From: "Hartmut Kaiser" <hartmut.kaiser@gmail.com>
1. Employ operator<< and operator>> for user-defined types.
Spirit supports that out of the box in two ways.
a) it has the 'stream' primitive utilizing the user type supplied operator>>() and operator<<() for parsing/generating. I.e.
user_type u; spirit::qi::parse(str, spirit::stream, u); spirit::karma::generate(std::back_inserter(str), spirit::stream, u);
Hartmut, 'parse' does str->user_type and 'generate' does user_type-
str, right?
Yep, exactly. And 'stream' is a placeholder.
Is it (the support for conversions and user types) anywhere in the documentation?
The documentation for Spirit 2 is still in the process of being written. Parts of it are in place, but not all of it. Joel and I are working on it to have it ready for BoostCon.
If it all that easy, I'll wrap it up into the API of my liking and we'll use it instead of lexical_cast. The whole 'convert' saga seems for nothing. Given how long this lexical_cast/convert discussion's been dragging on I feel you might spend some time educating more people (besides me) how to deploy Spirit for mundane taks like conversion. How do you handle failed conversion?
The functions qi::parse() and karma::generate() simply return a bool indicating success, the manipulators qi::match() and karma::format() integrate with the IO stream facilities (i.e. set the ios::bad flag or throw).
Where do I do to learn Spirit? The docs seem preoccupied with big stuff.
Did you look at the Spirit2 docs (http://www.boost.org/doc/libs/1_38_0/libs/spirit/doc/html/index.html)? I think they start slow, even if they do not explicitly cover conversion. A good source of information you will find by looking at the examples and tests in the repository.
[rant] As a side note I just love to see "spirit::qi::parse...spirit::stream" given some've could not stand my humble "convert::to" and "convert::from". I am sure they gonna love yours. LOL.
Understood. Spirits interface is not optimized for simplicity, though. It's meant to cover the whole thing (parsing/generation), so it's a bit too verbose for 'simple' conversion tasks. That's why I'm all in favor of having a simple interface. But, IMHO, such an interface needs to be downwards compatible just to avoid having two different DSEL for the same purpose in Boost. Any suggestion on that is highly welcome. It might just be that you need to allow Spirits placeholder types to be used for the convert<> function template parameters (in addition to the buildins like int, long, etc): For int's: convert<int>::to() convert<int>::from() which uses optimal conversion functions; and for user types (uses operator<<() and operator>>()): convert<spirit::tag::stream>::to() convert<spirit::tag::stream>::from() (spirit::stream is an instance of spirit::tag::stream). The idea would be to use Spirit directly if you need more powerful constructs like sequences and convert for conversion of single items. Regards Hartmut

From: "Hartmut Kaiser" <hartmut.kaiser@gmail.com> The documentation for Spirit 2 is still in the process of being written. Parts of it are in place, but not all of it. Joel and I are working on it to have it ready for BoostCon.
Yes, the docs are admittedly patchy. With you mentioning parse&generate I ran straight to .../parsing_and_generating.html just to find a work-in-progress page. Poked here and there. Often the same. That's OK I can wait. I hope you'll keep all this lexical_cast/convert noise in mind when filling in the blanks. :-)
... A good source of information you will find by looking at the examples and tests in the repository.
Thanks. Will do.
and for user types (uses operator<<() and operator>>()):
convert<spirit::tag::stream>::to() convert<spirit::tag::stream>::from()
Is it possible to have something like convert<int>::from() convert<user_type>::from() (we do not need 'to' with the interface).
(spirit::stream is an instance of spirit::tag::stream). The idea would be to use Spirit directly if you need more powerful constructs like sequences and convert for conversion of single items.
For more powerful constructs my preference will probably be to "use Spirit directly" literally instead of through the (likely limiting) convert interface. V.

and for user types (uses operator<<() and operator>>()):
convert<spirit::tag::stream>::to() convert<spirit::tag::stream>::from()
Is it possible to have something like
convert<int>::from() convert<user_type>::from()
(we do not need 'to' with the interface).
Sure. I just wanted to highlight my point of the API's being compatible.
(spirit::stream is an instance of spirit::tag::stream). The idea would be to use Spirit directly if you need more powerful constructs like sequences and convert for conversion of single items.
For more powerful constructs my preference will probably be to "use Spirit directly" literally instead of through the (likely limiting) convert interface.
Agreed. Regards Hartmut

On Saturday, February 28, 2009 12:18 PM Hartmut Kaiser wrote:
Well, in this case I really prefer:
using namespace spirit::std_locale; spirit::qi::parse(str, spirit::int_, i);
or, if you need another radix:
typedef spirit::qi::int_parser<int, 16> hex; spirit::qi::parse(str, hex(), i);
or, if you need exactly 4 hex digits:
typedef spirit::qi::int_parser<int, 16, 4, 4> hex4; spirit::qi::parse(str, hex4(), i);
etc.
Which is far less convoluted.
I wouldn't want to impose spirit::int_ on a user when int is what they meant. Provided that can be packaged easily enough, then the idea has merit. How is user-defined formatting done, though? Part of the discussion has been to support UDTs with specialized formatting flags. How and where would those be injected into the process if i = convert<int>(str) were written as parse(str, spirit::int_, i)? How does your idea handle any-to-any conversions like uuid to long long? The convert interface offers the ability to convert among any two types. Will that work with Spirit or does that require both a parser and a generator with some intermediate format?
<rant> I still don't get it. I agree that it might be favorable to have a simple API for type<-->string conversions.
But as you can clearly see the requirements are piling up and it's everything else but simple by now.
I'm not certain anyone has really decided that the large pile of requirements are appropriate. One of the benefits of lexical_cast is its simplicity. Emil has argued from the start for nothing more than to_string() and from_string(). Who knows, we may return to that before we're finished. I know that I'm exploring options and ideas and yours has merit and I like the idea of migrating to full Spirit parsing as needs grow more complex.
So I ask again:
What's the benefit of coming up with just another full blown parser/generator interface in Boost?
Possibly interface simplicity, possibly non-overlapping requirements. I'm sure most of the alternatives arise from ignorance of what you practically breathe daily.
What's the benefit of discarding/obsoleting lexical_cast<>() as the _really_ minimal, simple interface?
That's been documented: at least non-throwing calls and support for defaults. The convert interface also provides for the possibility of any-to-any type conversions.
Guy's. You're trying to come up with a DSEL (domain specific embedded language) for parsing and generating. What's it exactly you don't like in Spirits DSEL? Just the fact that it wasn't _you_ to come up with this particular syntax? Have you even looked?
I think you're overly sensitive here. I suspect Spirit seems overwhelming to those unfamiliar with it and seems targeted for different purposes. Those not accustomed to parsing, much less using a DSEL for parsing, don't think in those terms. As another noted, we've also been discussing interface, focusing on simplicity and suitability to the purpose. Now might be a great time to see how it can be guided to something a little closer to Spirit syntax for that growth path from the simple -- convert -- to the complex -- full Spirit.
Wouldn't it be better to use the experience the Spirit developers/users have collected over _years_ (!) and try to find a simpler API on top of Spirit directly extending from the existing one, certainly while limiting the possibilities in favor of a reduced feature set and a simplified usage model?
I don't think the Spirit developers have made such an offer previously. You suggested Spirit syntax before but not like here. I'm happy to see what can be done on this tack. _____ Rob Stewart robert.stewart@sig.com Software Engineer, Core Software using std::disclaimer; 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 Saturday, February 28, 2009 10:57 AM vicente.botet wrote:
the Boost.Range library introduce the concept of adaptor (or view). I'm wondering if this could be also convenient for the Boost.Convert library.
Take the uuid user data type.
uuid u; string res;
u | corverted_to<string> | uppercased > res;
The evaluation should be lazy, only when we assign throwg the
operator we force the evaluation.
or
string str;
int i = str | radix(10) | locale(...) | converted_to<int>;
Could this approach work for the Boost.Convert library?
It's an interesting idea, but I don't know how daunting the task is to create the likes of uppercased, radix, and locale as shown in your examples. Such components must be created rather easily for UDT authors to be willing to create them for Boost.Convert support. _____ Rob Stewart robert.stewart@sig.com Software Engineer, Core Software using std::disclaimer; 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 Sat Feb 28 2009, "vicente.botet" <vicente.botet-AT-wanadoo.fr> wrote:
int i = str | radix(10) | locale(...) | converted_to<int>;
Could this approach work for the Boost.Convert library?
I don't much like this syntax, because str | radix(10) isn't meaningful by itself. Also, for what it's worth, when this discussion moved from simple to_string/to_int type functionality into the realm of a "generalized conversion framework," I mostly lost interest. I haven't been watching closely, so I could be wrong, it seems like a classic premature generalization... the same one, in fact, that makes lexical_cast unsuitable for many people's purposes. Generic components can only properly arise from a survey of many specific, non-generic components. Have we done that for this "generalized conversion utility" or is the interface being designed speculatively to allow for something that maybe nobody wants or needs? We have to_string and to_int as non-generic exemplars. What else has been looked at? -- Dave Abrahams BoostPro Computing http://www.boostpro.com

From: "David Abrahams" <dave@boostpro.com> Also, for what it's worth, when this discussion moved from simple to_string/to_int type functionality into the realm of a "generalized conversion framework," I mostly lost interest.
I haven't been watching closely, so I could be wrong, it seems like a classic premature generalization... the same one, in fact, that makes lexical_cast unsuitable for many people's purposes. Generic components can only properly arise from a survey of many specific, non-generic components. Have we done that for this "generalized conversion utility" or is the interface being designed speculatively to allow for something that maybe nobody wants or needs?
Yes, I have to agree as I've been feeling the same. I read/write a lot of configuration data and I have to rely on XML for communications. I need/use string-to/from-type conversions a LOT in the lexical_cast manner. I've never had the urge of integrating type-to-type conversions into any kind of a framework as those conversions seem so type-specific that the following always felt as the natural choice: class Some { Some(Other const&) } That said, I was not sure if my experience was that exemplary so I did not want to shut the door for such a possibility. I am still hoping that that fairly neutral interface int i = convert<int>::from(str); does not sacrifice the string-based origins of that whole discussion and at the same time leaves the door open whatever might come our way... if ever. V.

On Mon, Mar 2, 2009 at 7:07 PM, Vladimir Batov <batov@people.net.au> wrote:
From: "David Abrahams" <dave@boostpro.com> Also, for what it's worth, when this discussion moved from simple to_string/to_int type functionality into the realm of a "generalized conversion framework," I mostly lost interest.
I haven't been watching closely, so I could be wrong, it seems like a classic premature generalization... the same one, in fact, that makes lexical_cast unsuitable for many people's purposes. Generic components can only properly arise from a survey of many specific, non-generic components. Have we done that for this "generalized conversion utility" or is the interface being designed speculatively to allow for something that maybe nobody wants or needs?
Yes, I have to agree as I've been feeling the same. I read/write a lot of configuration data and I have to rely on XML for communications. I need/use string-to/from-type conversions a LOT in the lexical_cast manner. I've never had the urge of integrating type-to-type conversions into any kind of a framework as those conversions seem so type-specific that the following always felt as the natural choice:
class Some { Some(Other const&) }
That said, I was not sure if my experience was that exemplary so I did not want to shut the door for such a possibility. I am still hoping that that fairly neutral interface
int i = convert<int>::from(str);
does not sacrifice the string-based origins of that whole discussion and at the same time leaves the door open whatever might come our way... if ever.
The point is, you're generalizing the interface without anybody having posted even a single use case that requires that generalization. Such generalization is not needed by anyone, or if it is needed we have no idea if that use case will fit this interface. So why is it supported? Just in case? We have lexical_cast for that. :) Emil Dotchevski Reverge Studios, Inc. http://www.revergestudios.com/reblog/index.php?n=ReCode

From: "Emil Dotchevski" <emildotchevski@gmail.com> The point is, you're generalizing the interface without anybody having posted even a single use case that requires that generalization.
Yes, I've been playing along that generalization effort as I did not see it compromising the interface or muddling the intent. For me the clarity was the goal, the generalization was a bonus. I do not feel int i = convert<int>::from(string) has been generalized at the expense of clarity. Quite the opposite. To me above does exactly what it says... in the best possible (IMHO) way. I do not believe that something clearer like "from_string" will better reflect its purpose as the "string" is open for interpretation and the above converts from various string-like *containers* (like vector<char>, list<wchar_t>). Same goes for 'to_string' as I need to convert to char and wchar_t-based strings and most likely need to convert to u8char strings. Therefore, I do not feel to_string<wstring>(-1) looks/reads better than convert<wstring>::from(-1)
Such generalization is not needed by anyone, or if it is needed we have no idea if that use case will fit this interface. So why is it supported? Just in case? We have lexical_cast for that. :)
Yes, I indeed have that feeling that the generalization option will never be exercised. However, to me the generalization has been accidental, a bonus. The main goal of the exercise (for me) has been clearing up the interface to say what it does. V.

On Tue, Mar 3, 2009 at 1:58 AM, Vladimir Batov <batov@people.net.au> wrote:
From: "Emil Dotchevski" <emildotchevski@gmail.com> The point is, you're generalizing the interface without anybody having posted even a single use case that requires that generalization.
Yes, I've been playing along that generalization effort as I did not see it compromising the interface or muddling the intent. For me the clarity was the goal, the generalization was a bonus. I do not feel
int i = convert<int>::from(string)
has been generalized at the expense of clarity. Quite the opposite. To me above does exactly what it says... in the best possible (IMHO) way. I do not believe that something clearer like "from_string" will better reflect its purpose as the "string" is open for interpretation and the above converts from various string-like *containers* (like vector<char>, list<wchar_t>).
Generic algorithms shouldn't target containers as such, instead containers should be accessed in terms of iterators. By the way, have you ever needed to convert a vector<char> to int?
Same goes for 'to_string' as I need to convert to char and wchar_t-based strings and most likely need to convert to u8char strings. Therefore, I do not feel
to_string<wstring>(-1)
looks/reads better than
convert<wstring>::from(-1)
If you think about the standard library stream operators, they nail down one of the arguments as a stream. In string conversions, ideally we should nail down the source or the return as a "string" because otherwise the interface is too generic. The problem is that there is no single string type in C++. If we have a function that converts to string, should it return std::string, std::wstring, char const *, wchar_t const *? Should it support user-defined string types? One possible line of thought is to say oh well, we need to accommodate all of the above. That leads to lexical_cast-style interface (I suspect that the proposed convert<> interface can in fact be implemented as an extension of lexical_cast.) I'd rather limit the interface to std::string/std::wstring and types that can be implicitly converted to string/wstring. So the question is how to provide this limitation? A template like convert<std::string> or to_string<std::string> can't provide this limitation. If I must pick between convert template and to_string template, I'd prefer to_string specifically to discourage users from thinking about supporting non-string types, but as you pointed out, semantically convert<> and to_string<> are the same. For me, they both have two issues: 1) convert<std::string>(x) disables ADL (I think it's important for users to be able to inject overloads.) 2) convert<std::string> necessarily returns std::string (I think it's important user-defined conversions to have the freedom to return char const *. Many interfaces that take strings are commonly overloaded for std::string and char const * for efficiency reasons.)
Such generalization is not needed by anyone, or if it is needed we have no idea if that use case will fit this interface. So why is it supported? Just in case? We have lexical_cast for that. :)
Yes, I indeed have that feeling that the generalization option will never be exercised. However, to me the generalization has been accidental, a bonus.
"Bonus" implies that the generalization is desirable. :)
The main goal of the exercise (for me) has been clearing up the interface to say what it does.
convert<Target>(source) says "convert any source I pass in to an object of static type Target." For what it's worth, I want the to-string interface to say "convert any source I pass in to string" where string could be std::string/char const * (or std::wstring/wchar_t const *) Emil Dotchevski Reverge Studios, Inc. http://www.revergestudios.com/reblog/index.php?n=ReCode

AMDG Emil Dotchevski wrote:
2) convert<std::string> necessarily returns std::string (I think it's important user-defined conversions to have the freedom to return char const *. Many interfaces that take strings are commonly overloaded for std::string and char const * for efficiency reasons.)
How common is this going to be in reality? Won't returning const char* usually introduce problems with memory management? In Christ, Steven Watanabe

On Tue, Mar 3, 2009 at 1:26 PM, Steven Watanabe <watanabesj@gmail.com> wrote:
AMDG Emil Dotchevski wrote:
2) convert<std::string> necessarily returns std::string (I think it's important user-defined conversions to have the freedom to return char const *. Many interfaces that take strings are commonly overloaded for std::string and char const * for efficiency reasons.)
How common is this going to be in reality? Won't returning const char* usually introduce problems with memory management?
I think that whether this is common or not is not important, my point is that it is a valuable optimization technique. If conversion of objects of type foo to string is for some reason critical for performance, you can have foo contain a std::string and make its to-string overload return a char const *, through std::string::c_str(). Even if I wanted to-string to return std::string const &, convert<std::string> won't work. Emil Dotchevski Reverge Studios, Inc. http://www.revergestudios.com/reblog/index.php?n=ReCode

AMDG Emil Dotchevski wrote:
On Tue, Mar 3, 2009 at 1:26 PM, Steven Watanabe <watanabesj@gmail.com> wrote:
AMDG Emil Dotchevski wrote:
2) convert<std::string> necessarily returns std::string (I think it's important user-defined conversions to have the freedom to return char const *. Many interfaces that take strings are commonly overloaded for std::string and char const * for efficiency reasons.)
How common is this going to be in reality? Won't returning const char* usually introduce problems with memory management?
I think that whether this is common or not is not important, my point is that it is a valuable optimization technique.
I don't think it is. It's too fragile. Functions taking const char* arguments don't have the same problems.
If conversion of objects of type foo to string is for some reason critical for performance, you can have foo contain a std::string and make its to-string overload return a char const *, through std::string::c_str().
which will work fine, until some generic code converts a temporary to a string.
Even if I wanted to-string to return std::string const &, convert<std::string> won't work.
In Christ, Steven Watanabe

On Tue, Mar 3, 2009 at 2:15 PM, Steven Watanabe <watanabesj@gmail.com> wrote:
Emil Dotchevski wrote:
On Tue, Mar 3, 2009 at 1:26 PM, Steven Watanabe <watanabesj@gmail.com> wrote:
Emil Dotchevski wrote:
2) convert<std::string> necessarily returns std::string (I think it's important user-defined conversions to have the freedom to return char const *. Many interfaces that take strings are commonly overloaded for std::string and char const * for efficiency reasons.)
How common is this going to be in reality? Won't returning const char* usually introduce problems with memory management?
I think that whether this is common or not is not important, my point is that it is a valuable optimization technique.
I don't think it is. It's too fragile. Functions taking const char* arguments don't have the same problems.
Functions that take char const * arguments are the reason why it's important to allow for to-string conversions to return char const *.
If conversion of objects of type foo to string is for some reason critical for performance, you can have foo contain a std::string and make its to-string overload return a char const *, through std::string::c_str().
which will work fine, until some generic code converts a temporary to a string.
What you probably mean is that if to_string returns char const *, the following would be undefined: char const * to_string( foo const & ); foo bar(); char const * s=to_string(bar()); //oops That's true, however this other scenario, where the foo object is also temporary, is valid: char const * to_string( foo const & ); foo bar(); void print( char const * ); void print( std::string const & ); print(to_string(bar())); So yes, it's tricky but it is still a valid optimization technique, IMO. We can specify to_string accordingly, saying that the returned object is only valid for the lifetime of the object being converted to string so the caller would know to do std::string s=to_string(bar()) which would be fine always. By the way, returning std::string doesn't guard against such errors: std::string to_string( foo const & ); foo bar(); char const * s=to_string(bar()).c_str(); //oops. Emil Dotchevski Reverge Studios, Inc. http://www.revergestudios.com/reblog/index.php?n=ReCode

----- Original Message ----- From: "Emil Dotchevski" <emildotchevski@gmail.com> To: <boost@lists.boost.org> Sent: Wednesday, March 04, 2009 12:17 AM Subject: Re: [boost] Boost.Convert+Boost.Parameter
By the way, returning std::string doesn't guard against such errors:
std::string to_string( foo const & ); foo bar(); char const * s=to_string(bar()).c_str(); //oops.
Here the problem is not that to_string returns a string but using c_str(). The same problem occurs when you use c_str on a local string. When the string goes out of scope the data pointeed by the recovred c_str is bad. char* ext; { std::string str("local"); //.. ext=str.c_str(); // } // Here ext points to a free memory area // if ext.is used the code coudl not work. Best, Vicente

From: "Emil Dotchevski" <emildotchevski@gmail.com> ... Generic algorithms shouldn't target containers as such, instead containers should be accessed in terms of iterators.
I believe generic containers do target containers. The standard algorithms are iterator-based. However, RangeEx and string_algo algorithms works with ranges which IMHO a considerable step foward compared to the standard iterator-based algorithms.
By the way, have you ever needed to convert a vector<char> to int?
The point is that "convert" works with boost::range-compatible containers that include vector<char>.
If you think about the standard library stream operators, they nail down one of the arguments as a stream. In string conversions, ideally we should nail down the source or the return as a "string" because otherwise the interface is too generic.
I am of an pinion that we do that: int i = convert<int>::from(string); string s = convert<string>::from(i); As you can see "the source or the return are nailed down as a "string".
The problem is that there is no single string type in C++. If we have a function that converts to string, should it return std::string, std::wstring, char const *, wchar_t const *? Should it support user-defined string types?
If the user wants "char const*" suported, he will implement such a conversion as: char const* s = convert<char const*>::from(i);
One possible line of thought is to say oh well, we need to accommodate all of the above.
User-specific need will be accommodated on when-needed basis.
That leads to lexical_cast-style interface (I suspect that the proposed convert<> interface can in fact be implemented as an extension of lexical_cast.)
That's been my original request. You can see the relevant lexical_cast discussion and the decision to proceed with something like "convert".
I'd rather limit the interface to std::string/std::wstring and types that can be implicitly converted to string/wstring. So the question is how to provide this limitation?
I honestly do not see such a need or any value in doing so.
A template like convert<std::string> or to_string<std::string> can't provide this limitation.
Yes, that was exactly the goal.
I'd prefer to_string specifically to discourage users from thinking about supporting non-string types,
The provided code does not provide any non-string conversions. If the user needs such he can do that himself. IMHO it should be their choice rather than yours.
... "Bonus" implies that the generalization is desirable. :)
No, bonus means you get something for nothing (say, see http://dictionary.reference.com/browse/bonus) convert<Target>(source) says "convert any source I pass in to an object of static type Target."
For what it's worth, I want the to-string interface to say "convert any source I pass in to string" where string could be std::string/char const * (or std::wstring/wchar_t const *)
That's what convert<string>::from(source) says. If you want conversion to "char const*", you'll implement that conversion yourself and then will use it as convert<char const*>::from(source) V.

Vladimir Batov wrote:
"Andrey Semashev" <andrey.semashev@gmail.com> wrote convert< string >((param1 = val1, param2 = val2, ...));
Andrey,
I started playing with your example integrating it in to the conversion framework and managed to get it working. Thank you for the example and that parameter_type extension. The whole thing seems to rely on that facility. Well, the interface I am using (without Boost.Parameter) is
int i = convert::to<int>(str); int i = convert::from(str, -1);
(almost what I initially started from :-) ) I tried to get rid of 'to&from' (several times) but in the end decided I needed to keep the two variations as the 'from' deduces both (TypeIn, TypeOut) parameters from the signature when the 'to' requires TypeOut explicitly specified. Having both named convert() reads wrong for the 'to' specialization:
int i = convert<int>(str);
and I could not stand it. Another option would be to ditch 'to' altogether but I am not sure how it might be taken. Also I tried deducing TypeOut parameter for 'to' as well via
template<class TypeIn> implementation { template<class TypeOut> operator TypeOut() };
but did not get too far either.
Well, I digressed from my original post about Boost.Parameter as I did not want us to get bogged down with convert::to and convert::from again (and BTW you cannot shortcut them so they are very much like convert_to, convert_from). OK. What I got working (thanks to your help) is
int i = convert::to<int>(str, radix_ = 10); int i = convert::from(str, default_ = -1); int i = convert::from(str, (default_ = -1, radix_ = 10));
I understand the benefits of passing additional conversion parameters like 'radix_ = 10' down to the conversion *implementation*. However, I feel these parameters are of no value (and not accessible) to the "casual" user (as opposed to the conversion implementer)... unlike I/O Stream-based approach which allows the user to write his own manipulator and deploy it like
int i = convert::from(str, -1) >> my_special_formatter;
Is my understanding correct or you have some ideas of getting that *user* extensibility with Boost.Parameter?
Secondly, I kinda like the explicitness of
int i = convert::from(str, default_ = -1);
however, does it really add much (apart from verbosity) compared with
int i = convert::from(str, -1);
I do not feel the above line is the trickiest in the whole Boost so that we need to get out of our way to spell out every damn thing. What do you think? That leads me to my question. Would that be sensible to have it as follows:
int i = convert::to<int>(str, (radix_ = 10, locale_= ...)); int i = convert::from(str, -1, (radix_ = 10, locale_= ...));
or you might have something else in mind?
Have you thought of making convert a class templated on the destination type which would allow a single function 'from', ala: int i = convert<int>::from(str, (radix_ = 10, locale_= ...)); int i = convert<int>::from(str, -1, (radix_ = 10, locale_= ...)); Which reads more naturally to me in english, at least. jeff

From: "Jeff Flinn" <TriumphSprint2000@hotmail.com> Have you thought of making convert a class templated on the destination type which would allow a single function 'from', ala:
int i = convert<int>::from(str, (radix_ = 10, locale_= ...)); int i = convert<int>::from(str, -1, (radix_ = 10, locale_= ...));
Which reads more naturally to me in english, at least.
I think I like it. :-) Thanks. V.
participants (9)
-
Andrey Semashev
-
David Abrahams
-
Emil Dotchevski
-
Hartmut Kaiser
-
Jeff Flinn
-
Steven Watanabe
-
Stewart, Robert
-
vicente.botet
-
Vladimir Batov