[convert] Now with Boost.Parameter interface.

I've incorporated, uploaded to the Vault and started playing with the Boost.Parameter-based interface that Andrey Semashev was insisting from the set-go. And it is definitely growing on me and I like int i = convert<int>::from(str, 0)(locale_ = new_locale)(throw_ = true); instead of the original int i = convert<int>::from(str, 0) >> new_locale >> dothrow; I think people generally did not like locales and dothrow behaving like manipulators. The more I use the Boost.Parameter-based interface the more I am inclined to push it further and apply it to manipulators as well. I.e. int i = convert<int>::from(str)(radix_ = 16); instead of the original direct handling of std manipulators. int i = convert<int>::from(str) >> std::hex; Something, again, Andrey was advocating from the start. Now it feels like allowing io-stream manipulators exposes too much implementation detail and raises some unwarranted expectations. That raises a few questions that I am hoping people could help me with: 1. Both interfaces (for locale and dothrow) are currently supported: #1 int i = convert<int>::from(str, 0)(locale_ = new_locale)(throw_ = true); #2 int i = convert<int>::from(str, 0) >> new_locale >> dothrow; Should I remove #1? 2. Both interfaces #1 int i = convert<int>::from(str, 0) >> std::hex; #2 int i = convert<int>::from(str, 0)(radix_ = 16); are currently supported. Should I move away from direct manipulator support and remove #1? 3. I only managed to figure out how to supply only one Boost.Parameter at a time like int i = convert<int>::from(str, 0)(locale_ = new_locale)(throw_ = true); I understand how to achieve int i = convert<int>::from(str, 0)((locale_ = new_locale, throw_ = true)); (with double quotes) but do not want to go there as it looks somewhat unorthodox. I cannot figure out how to achieve the following int i = convert<int>::from(str, 0)(locale_ = new_locale, throw_ = true); (a list inside single quotes). I remember Andrey mentioning I could do that with a Boost.Parameter macro but I admit of not being bright enough to figure it out by myself. Any help would be greatly appreciated. Thanks, Vladimir.

Should have proof-read before posting. Apologies.
Vladimir.Batov <at> wrsa.com.au> writes: 1. Both interfaces (for locale and dothrow) are currently supported:
#1 int i=convert<int>::from(str, 0)(locale_=new_locale)(throw_=true); #2 int i=convert<int>::from(str, 0) >> new_locale >> dothrow;
Should I remove #1?
Meant to say "Should I remove #2?"
I understand how to achieve
int i=convert<int>::from(str, 0)((locale_=new_locale, throw_=true));
(with double quotes) ... (a list inside single quotes).
Certainly meant double *brackets*. Apologies. Thanks, Vladimir.

Hello list, I'm migrating from 1.38 to 1.39. When building my program, I got an link error like this: LINK : fatal error LNK1104: cannot open file 'libboost_thread-vc90-mt-1_38.lib' I've set the correct include path and lib path for the IDE. And I've confirmed that - there's no items in the "Additional Dependendies" in the Liker section of the project properties dialog. - I've not introduced lib dependendies via #pragma lib directives. And it's a small program, mainly depends on boost and CGAL. I have also rebuilt all of the CGAL lib's with boost 1.39.0. I just cannot how the program depends, directly or indirectly, on libboost_thread-vc90-mt-1_38.lib. I'm using MS VS2008SP1 in Windows XP/SP1. I think ithis problem is of a general value, even though it doesn't have that much to do with boost itself. And I believe the boost guys will have an idea. Thanks for any information. B/Rgds Max

Max wrote:
Hello list,
I'm migrating from 1.38 to 1.39. When building my program, I got an link error like this:
LINK : fatal error LNK1104: cannot open file 'libboost_thread-vc90-mt-1_38.lib'
[snip]
I just cannot how the program depends, directly or indirectly, on libboost_thread-vc90-mt-1_38.lib.
Try searching your object files for that string, it might help to track down where the auto-include statement originates. Cheers, /Marcus

Max wrote:
Hello list,
Please do not ask questions by replying to an unrelated email.
I'm migrating from 1.38 to 1.39. When building my program, I got an link error like this:
LINK : fatal error LNK1104: cannot open file 'libboost_thread-vc90-mt-1_38.lib'
Your program appears to auto-link to 1.38.
I've set the correct include path and lib path for the IDE. And I've confirmed that
- there's no items in the "Additional Dependendies" in the Liker section of the project properties dialog. - I've not introduced lib dependendies via #pragma lib directives.
And it's a small program, mainly depends on boost and CGAL.
I have also rebuilt all of the CGAL lib's with boost 1.39.0.
I just cannot how the program depends, directly or indirectly, on libboost_thread-vc90-mt-1_38.lib.
Have you cleaned every single object files that compise your application. Are you sure you don't have include paths still pointing at 1.38? - Volodya

Please do not ask questions by replying to an unrelated email.
That's terrible! How do you know I was asking questions by replying to an unrelated email? Yes. I was replying an unrelated email as a basis, but I believe I have changed all of the unrelated information to those related. How do you do that? :-)
I'm migrating from 1.38 to 1.39. When building my program, I got an link error like this:
LINK : fatal error LNK1104: cannot open file 'libboost_thread-vc90-mt-1_38.lib'
Your program appears to auto-link to 1.38.
I've set the correct include path and lib path for the IDE. And I've confirmed that
- there's no items in the "Additional Dependendies" in the Liker section
of
the project properties dialog. - I've not introduced lib dependendies via #pragma lib directives.
And it's a small program, mainly depends on boost and CGAL.
I have also rebuilt all of the CGAL lib's with boost 1.39.0.
I just cannot how the program depends, directly or indirectly, on libboost_thread-vc90-mt-1_38.lib.
Have you cleaned every single object files that compise your application. Are you sure you don't have include paths still pointing at 1.38?
Yes, I've also double checked this aspect.
- Volodya
Thanks for your reply. B/Rgds Max

Max wrote:
Please do not ask questions by replying to an unrelated email.
That's terrible! How do you know I was asking questions by replying to an unrelated email? Yes. I was replying an unrelated email as a basis, but I believe I have changed all of the unrelated information to those related.
How do you do that? :-)
If that's an non-rhetoric question, take a look at http://thread.gmane.org/gmane.comp.lib.boost.devel/189020 which clearly shows your email inside unrelated thread. The "reply-to" command in all decent software sets In-Reply-To header, and most clients use that to display threads, therefore replying to unrelated email make your message appear where it might be missed, or interfere with other discussion.
I'm migrating from 1.38 to 1.39. When building my program, I got an link error like this:
LINK : fatal error LNK1104: cannot open file 'libboost_thread-vc90-mt-1_38.lib'
Your program appears to auto-link to 1.38.
I've set the correct include path and lib path for the IDE. And I've confirmed that
- there's no items in the "Additional Dependendies" in the Liker section of the project properties dialog. - I've not introduced lib dependendies via #pragma lib directives.
And it's a small program, mainly depends on boost and CGAL.
I have also rebuilt all of the CGAL lib's with boost 1.39.0.
I just cannot how the program depends, directly or indirectly, on libboost_thread-vc90-mt-1_38.lib.
Have you cleaned every single object files that compise your application. Are you sure you don't have include paths still pointing at 1.38?
Yes, I've also double checked this aspect.
Then, I guess you get to do what Marcus Lindblom suggested -- finding which object file grabs that library. - Volodya

Please do not ask questions by replying to an unrelated email.
That's terrible! How do you know I was asking questions by replying to an unrelated email? Yes. I was replying an unrelated email as a basis, but I believe I have changed all of the unrelated information to those related.
How do you do that? :-)
If that's an non-rhetoric question, take a look at
http://thread.gmane.org/gmane.comp.lib.boost.devel/189020
which clearly shows your email inside unrelated thread. The "reply-to" command in all decent software sets In-Reply-To header, and most clients use that to display threads, therefore replying to unrelated email make your message appear where it might be missed, or interfere with other discussion.
I'm asking seriously. Sorry for my lack of knowledge. I'm never aware of the ' In-Reply-To header' before you told me. And more unfortunately, I've ever made several mistakes before with the feeling that I'm doing the things in a correct and responsible manner. I thank the community for the tolerance to my mistakes, and thank you for telling me that. I appologize for the interference I've introduced, even though that's not my intention. I am willing to spent minutes of me to save more seconds of the community to get it a clean and warm place for all of us. Thank you, Volodya.
I'm migrating from 1.38 to 1.39. When building my program, I got an link error like this:
LINK : fatal error LNK1104: cannot open file 'libboost_thread-vc90-mt-1_38.lib'
Your program appears to auto-link to 1.38.
I've set the correct include path and lib path for the IDE. And I've confirmed that
- there's no items in the "Additional Dependendies" in the Liker
section
of
the project properties dialog. - I've not introduced lib dependendies via #pragma lib directives.
And it's a small program, mainly depends on boost and CGAL.
I have also rebuilt all of the CGAL lib's with boost 1.39.0.
I just cannot how the program depends, directly or indirectly, on libboost_thread-vc90-mt-1_38.lib.
Have you cleaned every single object files that compise your application. Are you sure you don't have include paths still pointing at 1.38?
Yes, I've also double checked this aspect.
Then, I guess you get to do what Marcus Lindblom suggested -- finding which object file grabs that library.
I'll have a try. Thank you, Lindblom and Volodya.
- Volodya
B/Rgds Max

on Mon May 04 2009, Vladimir Batov <vladimir.batov-AT-wrsa.com.au> wrote:
Should have proof-read before posting. Apologies.
Vladimir.Batov <at> wrsa.com.au> writes: 1. Both interfaces (for locale and dothrow) are currently supported:
#1 int i=convert<int>::from(str, 0)(locale_=new_locale)(throw_=true); #2 int i=convert<int>::from(str, 0) >> new_locale >> dothrow;
Should I remove #1?
Meant to say "Should I remove #2?"
I understand how to achieve
int i=convert<int>::from(str, 0)((locale_=new_locale, throw_=true));
(with double quotes) ... (a list inside single quotes).
Certainly meant double *brackets*. Apologies.
You should be able to do it without the double parens. Is there documentation missing? -- Dave Abrahams BoostPro Computing http://www.boostpro.com

From: "David Abrahams" <dave@boostpro.com>
I understand how to achieve
int i=convert<int>::from(str, 0)((locale_=new_locale, throw_=true));
You should be able to do it without the double parens. Is there documentation missing?
Dave, Apologies for not answering your question earlier. I am not sure how I managed to miss it. The answer is 'no', I do not think there is Boost.Parameter documentation missing. Unfortunately, my understanding was/is that to achieve that I have to resort to Boost.Parameter macros which I managed to avoid so far. I think I understand the non-macros part of Boost.Parameter. I quite like it and I am comfortable using it. Boost.Parameter macros seemed too complex and looked too "alien" which exacerbated my general dislike of macros. Please do not take it as a criticism of any kind. It's merely my "casual-user" impression which can easily be wrong as I was not prepared to spend enough time playing with Boost.Parameter macros. V.

Hi, I have read the documentation and I think that you are mixing default value with error value. The fact that a type is not DefaultConstructible do not implies that the user wants to have an exception when the conversion fails. This are two orthogonal dimensions. I think that for these non Default constructible types you can use a trait that gives you a default value for types not having a default constructor. E.g. template <typename T> struct default_value_trait { static const T value=T(); }; template <> struct default_value_trait<direction> { static const direction value=direction::up; }; So now you don't need any more to pass the default value as parameter, and the following should compile direction dir = convert<direction>::from (str); and could throw if conversion fails after minor modification on the implementation. I suppose you can reach the same effect with the error_value needed when the user want no_thow semantics. template <typename T> struct error_value_trait { static const T value=T(); }; template <> struct error_value_trait<direction> { static const direction value=direction::down; }; So the following direction dir = convert<direction>::from (str)(_thows=false); will return direction::down when conversion fails after some modification on the implementation One more question raise now. Should the fact the conversion throws or not when failing be part of the function type or a function parameter? Do we need direction dir = convert<direction, no_thows<> >::from (str); or direction dir = convert<direction>::from (str)(_thows=false); Resumein, I think these modifications are important, so the convert function can have always only the From parameter, and the throw semantics is preserved for non DefaultConstructible types. HTH, Vicente

vicente.botet <vicente.botet <at> wanadoo.fr> writes: I have read the documentation and I think that you are mixing default value with error value.
I am not sure I can agree. In my book there is *no* separate default value. There is only one value -- the conversion-failure (called default, error, etc.) value.
The fact that a type is not DefaultConstructible do not implies that the user wants to have an exception when the conversion fails. This are two orthogonal dimensions.
I think you are saying that "The fact that a type is not DefaultConstructible does not mean that the user *does not* want to have an exception when the conversion fails" because when the default/error value is provided, there is *no* exception on failure. And indeed, I strongly agree that these two issues -- the throwing behavior and providing/not-providing the default/error value -- are indeed orthogonal. I am very glad you mention that because IMHO it is a subtle but important issue that is often missed or misunderstood.
I think that for these non Default constructible types you can use a trait that gives you a default value for types not having a default constructor. E.g.
template <typename T> struct default_value_trait { static const T value=T(); };
template <> struct default_value_trait<direction> { static const direction value=direction::up; };
So now you don't need any more to pass the default value as parameter, and the following should compile
direction dir = convert<direction>::from (str);
and could throw if conversion fails after minor modification on the implementation.
Yes, it is an interesting approach and I like it as it allows to deploy throwing 'convert' uniformly for DefaultConstructible and NonDefaultConstructible types. The only hesitation that pops to my mind is that I feel 'convert' is fairly straightforward and needs to be used without much/any preparation. (That's how I tend to use it anyway). That's why I was whining about the named parameters' interface as it requires a round trip to the top of the file to add 'using'. If lazy-me has to do the same for the default_value_trait<direction> specialization, then I'll probably try avoiding that. On the other hand, integration of a Type into the lexical_cast/convert framework does require some preparation -- namely op>>() and op<<(). We could keep it for backward compatibility but might offer a new trait-based way of integrating a Type into the lexical_cast/convert framework. Like template<> struct convert_trait<Type> { } where we might put anything related to the integration of the Type into the convert framework. Is it too far-fetched? Thinking aloud really.
I suppose you can reach the same effect with the error_value needed when the user want no_thow semantics.
template <typename T> struct error_value_trait { static const T value=T(); };
template <> struct error_value_trait<direction> { static const direction value=direction::down; };
So the following
direction dir = convert<direction>::from (str)(_thows=false);
will return direction::down when conversion fails after some modification on the implementation
That feels over-engineered. Somehow I feel more comfortable with more explicit direction dir = convert<direction>::from(str, direction::dn);
One more question raise now. Should the fact the conversion throws or not when failing be part of the function type or a function parameter?
Do we need
direction dir = convert<direction, no_thows<> >::from (str);
or
direction dir = convert<direction>::from (str)(_thows=false);
Yes, that's a fair question. I considered convert<Type, Throw> before and back then it seemed that it was more flexible to configure the throwing behavior via a parameter. Having convert<Type>::result played a role as well. To me, in principle, there is no much difference between supplying a parameter as a template or as an argument. So, I chose the one that looked less restrictive and scaled better. Do you feel the template-based could do better?
Resumein, I think these modifications are important, so the convert function can have always only the From parameter, and the throw semantics is preserved for non DefaultConstructible types.
From my usage pattern I do not think I want "the convert function to always have only the From parameter". In fact, truth to be told, for me it is the other way around -- I *never* use the convert function with only the From parameter. :-) I prefer having the failure value immediately visible as in the following (typical for my usage) example:
int v = convert<int>::from(s, MAX_INT); if (v == MAX_INT) conversion failed. convert<direction>::result d = convert<direction>::from(s, direction::up); if (!d) conversion failed. That said, convert_trait seems tempting. What others think? V.

On Mon, Jul 6, 2009 at 6:03 PM, Vladimir Batov<vladimir.batov@wrsa.com.au> wrote:
vicente.botet <vicente.botet <at> wanadoo.fr> writes: I have read the documentation and I think that you are mixing default value with error value.
I am not sure I can agree. In my book there is *no* separate default value. There is only one value -- the conversion-failure (called default, error, etc.) value.
The fact that a type is not DefaultConstructible do not implies that the user wants to have an exception when the conversion fails. This are two orthogonal dimensions.
I think you are saying that "The fact that a type is not DefaultConstructible does not mean that the user *does not* want to have an exception when the conversion fails" because when the default/error value is provided, there is *no* exception on failure.
And indeed, I strongly agree that these two issues -- the throwing behavior and providing/not-providing the default/error value -- are indeed orthogonal. I am very glad you mention that because IMHO it is a subtle but important issue that is often missed or misunderstood.
default and error are *mostly* orthogonal default and DefaultConstructible are *mostly* orthogonal as well: default and error: - if I supply a default, then I do NOT expect an error - if we did (as once suggested) int x = convert<int>::from(str, 0, &error); then I supply a default so that x is set to something, but still want any error (warning?) info default and DefaultConstructible: - if the To type is NOT DefaultConstructible, then if I want a default, I need to supply one - if the To type IS DefaultConstructible, then I might STILL want to pass in a default: int min = convert<int>::from(minStr, 0); // get min or assume 0 int max = convert<int>::from(maxStr, 100); // get max, else 100 so for a given To type: if (To is DefaultConstructible) { if (specific default requested) { int max = convert<int>::from(str, 100); // default supplied so no error } else { if (want the "class-defined" default) { // ie we want convert to default-construct the result if necessary int max = convert<int>::from(str, throw_ = false); // or ? int max = convert<int>::from(str); // ie what's the default for throw_? // or, of course int max = convert<int>::from(str, int()); } else // want errors { int m = convert<int>::from(str, throw_ = true); // or maybe int m = convert<int>::from(str, &error); } } } else // NOT DefaultConstructible { struct NoDefCon { NoDefCon(int x, int y) { } }; if (want specific default) { // supply default explicitly // same form as other specific-default case above NoDefCon ndc = convert<NoDefCon>::from(str, NoDefCon(1, 3)); } else { // don't want a specific default? // no choice but to throw: NoDefCon ndc = convert<NoDefCon>::from(str); } } If all the concepts were completely orthogonal, we'd have a few more if blocks.
I think that for these non Default constructible types you can use a trait that gives you a default value for types not having a default constructor. E.g.
template <typename T> struct default_value_trait { static const T value=T(); };
template <> struct default_value_trait<direction> { static const direction value=direction::up; };
So now you don't need any more to pass the default value as parameter, and the following should compile
direction dir = convert<direction>::from (str);
and could throw if conversion fails after minor modification on the implementation.
Yes, it is an interesting approach and I like it as it allows to deploy throwing 'convert' uniformly for DefaultConstructible and NonDefaultConstructible types. The only hesitation that pops to my mind is that I feel 'convert' is fairly straightforward and needs to be used without much/any preparation. (That's how I tend to use it anyway). That's why I was whining about the named parameters' interface as it requires a round trip to the top of the file to add 'using'. If lazy-me has to do the same for the default_value_trait<direction> specialization, then I'll probably try avoiding that. On the other hand, integration of a Type into the lexical_cast/convert framework does require some preparation -- namely op>>() and op<<(). We could keep it for backward compatibility but might offer a new trait-based way of integrating a Type into the lexical_cast/convert framework. Like
template<> struct convert_trait<Type> { }
where we might put anything related to the integration of the Type into the convert framework. Is it too far-fetched? Thinking aloud really.
I suppose you can reach the same effect with the error_value needed when the user want no_thow semantics.
template <typename T> struct error_value_trait { static const T value=T(); };
template <> struct error_value_trait<direction> { static const direction value=direction::down; };
So the following
direction dir = convert<direction>::from (str)(_thows=false);
will return direction::down when conversion fails after some modification on the implementation
That feels over-engineered. Somehow I feel more comfortable with more explicit
direction dir = convert<direction>::from(str, direction::dn);
One more question raise now. Should the fact the conversion throws or not when failing be part of the function type or a function parameter?
Do we need
direction dir = convert<direction, no_thows<> >::from (str);
or
direction dir = convert<direction>::from (str)(_thows=false);
Yes, that's a fair question. I considered convert<Type, Throw> before and back then it seemed that it was more flexible to configure the throwing behavior via a parameter. Having convert<Type>::result played a role as well.
To me, in principle, there is no much difference between supplying a parameter as a template or as an argument. So, I chose the one that looked less restrictive and scaled better. Do you feel the template-based could do better?
Resumein, I think these modifications are important, so the convert function can have always only the From parameter, and the throw semantics is preserved for non DefaultConstructible types.
From my usage pattern I do not think I want "the convert function to always have only the From parameter". In fact, truth to be told, for me it is the other way around -- I *never* use the convert function with only the From parameter. :-) I prefer having the failure value immediately visible as in the following (typical for my usage) example:
int v = convert<int>::from(s, MAX_INT); if (v == MAX_INT) conversion failed.
convert<direction>::result d = convert<direction>::from(s, direction::up); if (!d) conversion failed.
That said, convert_trait seems tempting. What others think?
V.
Overall, I think defaults are not class-level decisions, but call-level choices. Like the min/max case. One convert call might use 0 as the default, the other 100. I DO think that an error_value, IS (typically) a class-level trait. Well, maybe not for ints. Sometimes, in one context, 0 might be a good error value, sometimes -1, etc. Often it may be the case that need a stronger type than 'int', but that doesn't always happen in pratice. Could always pass in the trait: int x = convert<int, my_convert_traits>::from(str); with a default of convert_trait<To> if not specified. Basically, I'm not convinced about the traits... Tony

----- Original Message ----- From: "Vladimir Batov" <vladimir.batov@wrsa.com.au> To: <boost@lists.boost.org> Sent: Tuesday, July 07, 2009 12:03 AM Subject: Re: [boost][convert] are you mixing default_value and error_value?
vicente.botet <vicente.botet <at> wanadoo.fr> writes: I have read the documentation and I think that you are mixing default value with error value.
I am not sure I can agree. In my book there is *no* separate default value. There is only one value -- the conversion-failure (called default, error, etc.) value.
The fact that a type is not DefaultConstructible do not implies that the user wants to have an exception when the conversion fails. This are two orthogonal dimensions.
I think you are saying that "The fact that a type is not DefaultConstructible does not mean that the user *does not* want to have an exception when the conversion fails" because when the default/error value is provided, there is *no* exception on failure.
Yes, this is what I wanted to say. On the case of the direction class, you are forced to give an *initial value* because direction is not default constructible, but with your interface we can't have an exception in this case. Is for this reason I say you are mixin both.
And indeed, I strongly agree that these two issues -- the throwing behavior and providing/not-providing the default/error value -- are indeed orthogonal. I am very glad you mention that because IMHO it is a subtle but important issue that is often missed or misunderstood.
So, do you plan to separate them?
I think that for these non Default constructible types you can use a trait that gives you a default value for types not having a default constructor. E.g.
template <typename T> struct default_value_trait { static const T value=T(); };
template <> struct default_value_trait<direction> { static const direction value=direction::up; };
So now you don't need any more to pass the default value as parameter, and the following should compile
direction dir = convert<direction>::from (str);
and could throw if conversion fails after minor modification on the implementation.
Yes, it is an interesting approach and I like it as it allows to deploy throwing 'convert' uniformly for DefaultConstructible and NonDefaultConstructible types. The only hesitation that pops to my mind is that I feel 'convert' is fairly straightforward and needs to be used without much/any preparation. (That's how I tend to use it anyway). That's why I was whining about the named parameters' interface as it requires a round trip to the top of the file to add 'using'. If lazy-me has to do the same for the default_value_trait<direction> specialization, then I'll probably try avoiding that.
This is surely because you are mixing the returned value when a failure occur and the initial value when the class is not default convertible.
On the other hand, integration of a Type into the lexical_cast/convert framework does require some preparation -- namely op>>() and op<<(). We could keep it for backward compatibility but might offer a new trait-based way of integrating a Type into the lexical_cast/convert framework. Like
template<> struct convert_trait<Type> { }
where we might put anything related to the integration of the Type into the convert framework. Is it too far-fetched? Thinking aloud really.
Why not?
I suppose you can reach the same effect with the error_value needed when the user want no_thow semantics.
template <typename T> struct error_value_trait { static const T value=T(); };
template <> struct error_value_trait<direction> { static const direction value=direction::down; };
So the following
direction dir = convert<direction>::from (str)(_thows=false);
will return direction::down when conversion fails after some modification on the implementation
That feels over-engineered. Somehow I feel more comfortable with more explicit
direction dir = convert<direction>::from(str, direction::dn);
You are right the failure returned value should be given at the call. What about passing it statically direction dir = convert<direction, no_throws<direction::dn> >(str); or dynamically direction dir = convert<direction>(str, (_no_throws=direction::dn));
One more question raise now. Should the fact the conversion throws or not when failing be part of the function type or a function parameter?
Do we need
direction dir = convert<direction, no_thows<> >::from (str);
or
direction dir = convert<direction>::from (str)(_thows=false);
Yes, that's a fair question. I considered convert<Type, Throw> before and back then it seemed that it was more flexible to configure the throwing behavior via a parameter.
Have you considered to return failure setting errno? How can your current interface be adapted to this use case? With a coverter template parameter you can do something like: statically direction dir = convert<direction, set_errno<EINVALID> >(str); or dynamicaly direction dir = convert<direction>(str, (_set_errno=EINVALID));
Having convert<Type>::result played a role as well.
Have you considered to convert returns just a pair<T, bool> or optional<T>? How can your current interface be adapted to these use cases? With a template parameter you can do something like pair<direction, bool> dirp = convert<direction, return_pair >(str); optional<direction> opt_dir = convert<direction, return_optional >(str); or pair<direction, bool> dirp = convert<direction>(str, _return_pair); optional<direction> opt_dir = convert<direction>(str, _return_optional);
To me, in principle, there is no much difference between supplying a parameter as a template or as an argument. So, I chose the one that looked less restrictive and scaled better. Do you feel the template-based could do better?
Templating the convert function with a converter parameter allows to have different return types that depend on the converter parameter. A model of converter is the functor class realizing the conversion using the conversion parameters template <typename To> struct converter { typedef related to To result_type; converter(); converter(converter_parameters<To> const&); template <typenam From> result_type operator()(From); }; The convert function can return the out type (To) instead of the internal converter which is implicitly convertible to the To type. template <typename To, typename Converter, typename From > Converter::retult_type convert(From f, Converter const&cv) { return cv(f); }
Resumein, I think these modifications are important, so the convert function can have always only the From parameter, and the throw semantics is preserved for non DefaultConstructible types.
Rectification, the convert function can have two parameters, first the >From type and second the converter (parameters). The converter class must define operator()(From).
From my usage pattern I do not think I want "the convert function to always have only the From parameter". In fact, truth to be told, for me it is the other way around -- I *never* use the convert function with only the From parameter. :-) I prefer having the failure value immediately visible as in the following (typical for my usage) example:
int v = convert<int>::from(s, MAX_INT); if (v == MAX_INT) conversion failed.
What about int v = convert<int>(s, _no_thows=MAX_INT); if (v == MAX_INT) conversion failed.
convert<direction>::result d = convert<direction>::from(s, direction::up); if (!d) conversion failed.
And optional<direction> d = convert<direction>(str, _return_optional ); if (!d) conversion failed.
That said, convert_trait seems tempting. What others think?
We can see _no_thows=MAX_INT _return_pair _return_optional and the other parameters as elements of a DSL configuring the conversion. This view of the convert function with a converter parameter joins a little bit the one proposed by Hartmut in a recent post. What others think? Best, Vicente

On Wed, Jul 8, 2009 at 6:42 AM, vicente.botet<vicente.botet@wanadoo.fr> wrote:
Yes, this is what I wanted to say. On the case of the direction class, you are forced to give an *initial value* because direction is not default constructible, but with your interface we can't have an exception in this case. Is for this reason I say you are mixin both.
Do we need to give an initial value just for conversion to work at all, or is it only needed as a return in the error case? Tony

Gottlob Frege wrote:
On Wed, Jul 8, 2009 at 6:42 AM, vicente.botet<vicente.botet@wanadoo.fr> wrote:
Yes, this is what I wanted to say. On the case of the direction class, you are forced to give an *initial value* because direction is not default constructible, but with your interface we can't have an exception in this case. Is for this reason I say you are mixin both.
Do we need to give an initial value just for conversion to work at all, or is it only needed as a return in the error case?
Tony
Hi It is needed for both usages: the initial value when the class is not default constructible, and the error value when we want this value to be returned when failure. Now we can not have an exception for the conversion to a class that is not default constructible because when we give a default value it is also interpreted as the error value. As I said we are mixing both concepts, which is not good, as both are orthogonal. Vicente -- View this message in context: http://www.nabble.com/-convert--Now-with-Boost.Parameter-interface.-tp233628... Sent from the Boost - Dev mailing list archive at Nabble.com.

From: "Vicente Botet Escriba" <vicente.botet@wanadoo.fr> ...
Do we need to give an initial value just for conversion to work at all, or is it only needed as a return in the error case?
Tony
It is needed for both usages: the initial value when the class is not default constructible, and the error value when we want this value to be returned when failure. Now we can not have an exception for the conversion to a class that is not default constructible because when we give a default value it is also interpreted as the error value. As I said we are mixing both concepts, which is not good, as both are orthogonal.
The is only *one* value provided -- a conversion-failure value. It happens to be used internally for some initialization (for std::stream-based conversions). It's an internal implementation matter and not guaranteed to be that way. What do we achieve by stressing it out? V.

Vladimir Batov wrote:
From: "Vicente Botet Escriba" <vicente.botet@wanadoo.fr> ...
Do we need to give an initial value just for conversion to work at all, or is it only needed as a return in the error case?
Tony
It is needed for both usages: the initial value when the class is not default constructible, and the error value when we want this value to be returned when failure. Now we can not have an exception for the conversion to a class that is not default constructible because when we give a default value it is also interpreted as the error value. As I said we are mixing both concepts, which is not good, as both are orthogonal.
The is only *one* value provided -- a conversion-failure value. It happens to be used internally for some initialization (for std::stream-based conversions). It's an internal implementation matter and not guaranteed to be that way. What do we achieve by stressing it out?
V.
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Hi, What does this does? direction d = convert<direction>::from(str); Vicente -- View this message in context: http://www.nabble.com/-convert--Now-with-Boost.Parameter-interface.-tp233628... Sent from the Boost - Dev mailing list archive at Nabble.com.

On Thursday, July 9, 2009, Vicente Botet Escriba <vicente.botet@wanadoo.fr> wrote:
Vladimir Batov wrote:
From: "Vicente Botet Escriba" <vicente.botet@wanadoo.fr> ...
Do we need to give an initial value just for conversion to work at all, or is it only needed as a return in the error case?
Tony
It is needed for both usages: the initial value when the class is not default constructible, and the error value when we want this value to be returned when failure. Now we can not have an exception for the conversion to a class that is not default constructible because when we give a default value it is also interpreted as the error value. As I said we are mixing both concepts, which is not good, as both are orthogonal.
The is only *one* value provided -- a conversion-failure value. It happens to be used internally for some initialization (for std::stream-based conversions). It's an internal implementation matter and not guaranteed to be that way. What do we achieve by stressing it out?
V.
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Hi,
What does this does?
direction d = convert<direction>::from(str);
Vicente
Not compile, I guess. You need to either write a specialization of convert, or maybe convert_traits. You had to specialize << anyhow. Maybe the default/error form: convert<direction>::from(str, down) shouldn't compile either (ie don't use error return as init value). Tony
-- View this message in context: http://www.nabble.com/-convert--Now-with-Boost.Parameter-interface.-tp233628... Sent from the Boost - Dev mailing list archive at Nabble.com.
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

Gottlob Frege wrote:
On Thursday, July 9, 2009, Vicente Botet Escriba <vicente.botet@wanadoo.fr> wrote:
What does this does?
direction d = convert<direction>::from(str);
Not compile, I guess.
Assuming direction is the exemplar Vladimir has been using all along, that won't compile because direction has no default constructor. The underlying behavior is the same as that of lexical_cast: direction result; std::istringstream stream(str); stream >> result; return result; That requires a default constructor. This code: direction d(convert<direction>::from(str, direction::up)); avoids the need for a default constructor via code like the following: direction result(direction::up); std::istringstream stream(str); stream >> result; return result;
You need to either write a specialization of convert, or maybe convert_traits. You had to specialize << anyhow.
The traits idea is inappropriate because the value to return for each invocation can change.
Maybe the default/error form: convert<direction>::from(str, down) shouldn't compile either (ie don't use error return as init value).
There isn't a choice. In order to extract a direction from an istringstream, there must be a direction instance. If direction has no default constructor, then the result *must* be initialized from the error return value. I don't know what you think is or could be happening, but I hope the code I've shown above clarifies the matter. _____ 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 Thu, Jul 9, 2009 at 9:41 AM, Stewart, Robert<Robert.Stewart@sig.com> wrote:
Gottlob Frege wrote:
Assuming direction is the exemplar Vladimir has been using all along, that won't compile because direction has no default constructor. The underlying behavior is the same as that of lexical_cast:
direction result; std::istringstream stream(str); stream >> result; return result;
That requires a default constructor.
This code:
direction d(convert<direction>::from(str, direction::up));
avoids the need for a default constructor via code like the following:
direction result(direction::up); std::istringstream stream(str); stream >> result; return result;
Maybe the default/error form: convert<direction>::from(str, down) shouldn't compile either (ie don't use error return as init value).
There isn't a choice. In order to extract a direction from an istringstream, there must be a direction instance. If direction has no default constructor, then the result *must* be initialized from the error return value. I don't know what you think is or could be happening, but I hope the code I've shown above clarifies the matter.
I'm sure you are right, and the real limitation comes down to how streams and << work. I was also trying to imagine the no_throw case: direction result(default_value); try { stream >> result; } catch (...) { result = error_value; } return result; Although I'm not even sure - if you can guarantee that "return result" doesn't throw (in some stupid complicated copy constructor) - if that is the meaning of no_throw - if that is the meaning of "error" (ie stream >> result could still have an 'error' but not have thrown, depending on how it works, and meaning, etc) - and still direction needed to be initialized, because, basically, it doesn't have a construct-from-stream: { return direction(stream); } nor do most types, thus we use >> instead, as is defacto. Basically I was leaving the implementation open to my imagination, and focusing more on how it looks from the outside.
_____ Rob Stewart
Tony

On Thursday, July 9, 2009, Vladimir Batov <batov@people.net.au> wrote:
From: "Vicente Botet Escriba" <vicente.botet@wanadoo.fr> ...
Do we need to give an initial value just for conversion to work at all, or is it only needed as a return in the error case?
Tony
It is needed for both usages: the initial value when the class is not default constructible, and the error value when we want this value to be returned when failure. Now we can not have an exception for the conversion to a class that is not default constructible because when we give a default value it is also interpreted as the error value. As I said we are mixing both concepts, which is not good, as both are orthogonal.
The is only *one* value provided -- a conversion-failure value. It happens to be used internally for some initialization (for std::stream-based conversions). It's an internal implementation matter and not guaranteed to be that way. What do we achieve by stressing it out?
Maybe it should not be used for initialization at all. Let's say MyClass has a bad_state setting, that, when set, stops the class from doing anything, even processing << operators. So if I pass in bad_state as a default return value, it won't work if it is internally being used as an init value. Tony
V.
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

From: "Gottlob Frege" <gottlobfrege@gmail.com>
Do we need to give an initial value just for conversion to work at all, or is it only needed as a return in the error case?
The user does not provide any initial values. The only value that is provided is the failure-conversion value: int i = convert<int>::from(str, 789); '789' will only be returned if the conversion fails. As int i = convert<int>::from(str); does not provide such a conversion-failure value, we have nothing to return in case of a failure. So, we throw. Indeed, under the hood that 789 (in #1 case and int() in #2) value happens to be used for some initialization. However, IMHO it is none of user's business. V.

Is it possible to sum up the issues? Unfortunately some of us have floated in and out of the conversation, and I, for one, feel that I am probably missing some important reprecussions of any suggestions I might make. ie: 0. using named parameters - is this still debatable, or pretty much decided 1. how to list params: (locale_ = locale)(throw_ = true) vs (locale_ = locale, throw_ = true) vs ((locale_ = locale, throw_ = true)) 2. where to list params: from(str, 0)(more stuff) vs from(str, 0).with(more stuff) vs (more stuff).from(str, 0) 3. returning an int (well, whatever the To type is) 4. use cases: function object, direct call, etc 5. ? Tony

Gottlob Frege <gottlobfrege <at> gmail.com> writes:
...
0. using named parameters - is this still debatable, or pretty much decided
I'd say anything is debatable and open for a discussion. Although some aspects have been discussed at length when the thread was somewhat more active. As for "decided" it feels too hard a word as people have varying opinions and preferences and I am inclined to try to accommodate those preferences (if possible) rather than "decide" on something. I am trying not to decide on anything but rather let pieces fall off based on the usage experience. I admit mainly using my own experience as a guide as I do not have much other input. As for named parameters, the interface is there and I was not rushing to take it out as I remember some people argued strongly in its favor. My only complain/observation was that my personal impression was that it was only of limited usage (for locale and throw). I was not alone advocating that stretching it further (radix, etc.) was counter-productive.
1. how to list params: (locale_ = locale)(throw_ = true) vs (locale_ = locale, throw_ = true) vs ((locale_ = locale, throw_ = true))
It's pretty much "decided"... for now. :-) Simply because ((...)) looks very odd and I personally do not want to give that syntax the user. Then, (,) (as I understand) has its limitations and requires deploying Boost.Parameter macros which I am not comfortable with. Then, the ()() chaining syntax is standard/familiar, scales well and does not impose any limitations or typing penalties. Sounds like a clear winner to me. The usual i-can-be-wrong disclaimer.
2. where to list params: from(str, 0)(more stuff) vs from(str, 0).with(more stuff) vs (more stuff).from(str, 0)
When this discussion started (or rather re-started) I revisited the 'convert' implementation and my design decisions. *I* am convinced that convert<TypeOut> and even convert<TypeIn, TypeOut> do not work in general terms and the delayed 2-step specialization (1-st on the TypeOut via convert<TypeOut>, 2-nd on the TypeIn via from()) is essential. Therefore, I *currently* feel that the suggested (more stuff).from(str, 0) interface won't work. As for Rob's from(str,0).with(more stuff) I do agree that it clears up whatever confusion there might be and I am prepared to incorporate that if there is enough support for it. I personally just can't help thinking that we are trying a bit too hard spelling everything out -- it's not the trickiest of all interfaces (many Boost components come to mind) and the seasoned users are unlikely to need 'with'. The beginners will need to read the documentation anyway.
3. returning an int (well, whatever the To type is)
I'd love 'convert' to return TypeOut directly. I feel that it cannot. Have you had a chance to read the documentation that is in the Vault with the 'convert' implementation? There I tried to cover various scenarios. That said, I feel that for simple cases 'convert' behaves *almost* like it returns TypeOut directly. The only wrinkle was using the result in template functions like template<class T> void foo(); foo(convert<int>::from(s)); For that there is foo(convert<int>::from(s).value()); or foo<int>(convert<int>::from(s));
4. use cases: function object, direct call, etc
I tried to cover the use-cases that I personally deploy and those that people mentioned and I could think of in the document that comes with the implementation (in the Vault). If you have some additional use-cases in mind, I'll be happy to address and incorporate those as well.
5. ?
I'd encourage you to read the doc. and bring up whatever you feel not covered, missing, incorrectly designed, etc. I am looking forward to your input. V.

Hi Vladimir, I think ability to set persistent behavior would be nice. Something like this; 1. convert::set(locale_ = new_locale)(throw_ = true); 2. int i = convert<int>::from(str1, 0); 3. int j = convert<int>::from(str2, 0); The line #1 effect lines following it (#2 & #3). Take care, emre On Mon, May 04, 2009 at 02:12:10PM +1000, Vladimir.Batov@wrsa.com.au wrote:
I've incorporated, uploaded to the Vault and started playing with the Boost.Parameter-based interface that Andrey Semashev was insisting from the set-go. And it is definitely growing on me and I like
int i = convert<int>::from(str, 0)(locale_ = new_locale)(throw_ = true);
instead of the original
int i = convert<int>::from(str, 0) >> new_locale >> dothrow;
I think people generally did not like locales and dothrow behaving like manipulators.
The more I use the Boost.Parameter-based interface the more I am inclined to push it further and apply it to manipulators as well. I.e.
int i = convert<int>::from(str)(radix_ = 16);
instead of the original direct handling of std manipulators.
int i = convert<int>::from(str) >> std::hex;
Something, again, Andrey was advocating from the start. Now it feels like allowing io-stream manipulators exposes too much implementation detail and raises some unwarranted expectations.
That raises a few questions that I am hoping people could help me with:
1. Both interfaces (for locale and dothrow) are currently supported:
#1 int i = convert<int>::from(str, 0)(locale_ = new_locale)(throw_ = true); #2 int i = convert<int>::from(str, 0) >> new_locale >> dothrow;
Should I remove #1?
2. Both interfaces
#1 int i = convert<int>::from(str, 0) >> std::hex; #2 int i = convert<int>::from(str, 0)(radix_ = 16);
are currently supported. Should I move away from direct manipulator support and remove #1?
3. I only managed to figure out how to supply only one Boost.Parameter at a time like
int i = convert<int>::from(str, 0)(locale_ = new_locale)(throw_ = true);
I understand how to achieve
int i = convert<int>::from(str, 0)((locale_ = new_locale, throw_ = true));
(with double quotes) but do not want to go there as it looks somewhat unorthodox. I cannot figure out how to achieve the following
int i = convert<int>::from(str, 0)(locale_ = new_locale, throw_ = true);
(a list inside single quotes). I remember Andrey mentioning I could do that with a Boost.Parameter macro but I admit of not being bright enough to figure it out by myself. Any help would be greatly appreciated.
Thanks, Vladimir. _______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

On 2009-05-06, Emre Turkay <emreturkay2@gmail.com> wrote:
I think ability to set persistent behavior would be nice. Something like this;
1. convert::set(locale_ = new_locale)(throw_ = true); 2. int i = convert<int>::from(str1, 0); 3. int j = convert<int>::from(str2, 0);
The line #1 effect lines following it (#2 & #3).
I really don't like the idea of global state for this. Making reusable "format packs" would be nice, though. Perhaps instances of the convert class should be allowed, so you could do something like this? convert<int> myconvert((locale_ = new_locale, throw_ = true)); int i = myconvert.from(str1, 0); int j = myconvert.from(str1, 0); Just musing, ~ Scott

Scott McMurray wrote: ... I really don't like the idea of global state for this.
I agree.
Making reusable "format packs" would be nice, though. Perhaps instances of the convert class should be allowed, so you could do something like this?
convert<int> myconvert((locale_ = new_locale, throw_ = true)); int i = myconvert.from(str1, 0); int j = myconvert.from(str1, 0);
Yes, that seems very sensible and seemingly straightforward with Boost.Parameter argument packs. I'll try playing and implementing the idea. Thanks, V.

On Fri, May 08, 2009 at 04:39:19PM -0400, Scott McMurray wrote:
Making reusable "format packs" would be nice, though. Perhaps instances of the convert class should be allowed, so you could do something like this?
convert<int> myconvert((locale_ = new_locale, throw_ = true)); int i = myconvert.from(str1, 0); int j = myconvert.from(str1, 0);
This is fine, too. emre

On Monday, May 04, 2009 12:12 AM Vladimir.Batov@wrsa.com.au wrote: [I'm sorry it took so long to reply to this! I lost your post for a while.]
I've incorporated, uploaded to the Vault and started playing with the Boost.Parameter-based interface that Andrey Semashev was insisting from the set-go. And it is definitely growing on me and I like
int i = convert<int>::from(str, 0)(locale_ = new_locale)(throw_ = true);
instead of the original
int i = convert<int>::from(str, 0) >> new_locale >> dothrow;
I think people generally did not like locales and dothrow behaving like manipulators.
I was one of them.
The more I use the Boost.Parameter-based interface the more I am inclined to push it further and apply it to manipulators as well. I.e.
int i = convert<int>::from(str)(radix_ = 16);
instead of the original direct handling of std manipulators.
int i = convert<int>::from(str) >> std::hex;
Something, again, Andrey was advocating from the start. Now it feels like allowing io-stream manipulators exposes too much implementation detail and raises some unwarranted expectations.
I am quite against this approach. The whole point of this mechanism is to rely upon the insertion and extraction operators of UDTs (as well as built-ins) for IOStream conversions of one type to another. You require such operators for the conversion to work. What otherwise hidden implementation detail is exposed by supporting manipulators on that stream, too?
That raises a few questions that I am hoping people could help me with:
1. Both interfaces (for locale and dothrow) are currently supported:
#1 int i = convert<int>::from(str, 0)(locale_ = new_locale)(throw_ = true); #2 int i = convert<int>::from(str, 0) >> new_locale >> dothrow;
Should I remove #1?
You should remove #2, not #1.
2. Both interfaces
#1 int i = convert<int>::from(str, 0) >> std::hex; #2 int i = convert<int>::from(str, 0)(radix_ = 16);
are currently supported. Should I move away from direct manipulator support and remove #1?
#2 provides an interesting, high level abstraction that seems attractive, but only applies to numeric conversions. The purpose is the same as >> std::hex so, I don't see the value of radix_ when that manipulator is commonly known, documented in the literature, and well understood on sight. I don't think users can add their own named parameters for convert(), so the library provided named parameters will occupy special status and shouldn't favor one sort of conversion above another. Consequently, use manipulators when available.
3. I only managed to figure out how to supply only one Boost.Parameter at a time like
int i = convert<int>::from(str, 0)(locale_ = new_locale)(throw_ = true);
I understand how to achieve
int i = convert<int>::from(str, 0)((locale_ = new_locale, throw_ = true));
(with double quotes) but do not want to go there as it looks somewhat unorthodox. I cannot figure out how to achieve the following
The doubled parentheses are, indeed, rare outside Boost.Preprocessor type code.
int i = convert<int>::from(str, 0)(locale_ = new_locale, throw_ = true);
That is certainly preferable syntax.
(a list inside single quotes). I remember Andrey mentioning I could do that with a Boost.Parameter macro but I admit of not being bright enough to figure it out by myself. Any help would be greatly appreciated.
I have no information to offer on that point. _____ 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.

From: "Stewart, Robert" <Robert.Stewart@sig.com>
[I'm sorry it took so long to reply to this! I lost your post for a while.]
Hi, Rob, it's nice hearing from you.
[snip]
2. Both interfaces
#1 int i = convert<int>::from(str, 0) >> std::hex; #2 int i = convert<int>::from(str, 0)(radix_ = 16);
are currently supported. Should I move away from direct manipulator support and remove #1?
#2 provides an interesting, high level abstraction that seems attractive, but only applies to numeric conversions. The purpose is the same as >> std::hex so, I don't see the value of radix_ when that manipulator is commonly known, documented in the literature, and well understood on sight. I don't think users can add their own named parameters for convert(), so the library provided named parameters will occupy special status and shouldn't favor one sort of conversion above another. Consequently, use manipulators when available.
Yes, that is *exactly* the conclusion I've come to. #2 indeed looks cute and aestetically I much prefer #2... but it seems to be merely a sugar coating over #1. On the other hand, #1 hardly needs *any* explanations/support for the reasons you mention and that imho makes it a clear favorite. On top of it, I either do not see how named-parameters-based interface can be made user-extendable unless we pack all the parameters into ArgumentPacks and pass them to the user. Maybe that might be the way to pursue in the future but I feel we pretty much get all that right off the bat with ol' manipulators-based interface.
1. Both interfaces (for locale and dothrow) are currently supported:
#1 int i = convert<int>::from(str, 0)(locale_ = new_locale)(throw_ = true); #2 int i = convert<int>::from(str, 0) >> new_locale >> dothrow;
Should I remove #1?
You should remove #2, not #1.
Yes, that's what I *actually* meant to say back then. :-) Now though after having both around I am not sure. As I mentioned I do like named-parameter-based interface -- I find it pleasant aesthetically (I just happen to like op()) and friendly. However, while typing I find myself favoring the frugality and practicality of #2. It is probably because 1) #2 uses the same interface/notation as ">> std::hex" (I admit of being a consistency/uniformity freak); 2) #2 is less to type (#1 requires using namespace boost::conversion::parameter;) (surely a weak argument but it saves me a round trip to the top of the file to add 'using'); 3) I do not seem to find any other use for the named-parameter-based interface beyond locale_ and throw_ (so that the support base for #1 is somewhat small... although this interface clearly scales better and more uniformly). These are just my musings how I torn feel about both rather than serious arguments for or against. Best, V.

Vladimir Batov wrote:
From: "Stewart, Robert" <Robert.Stewart@sig.com>
1. Both interfaces (for locale and dothrow) are currently supported:
#1 int i = convert<int>::from(str, 0)(locale_ = new_locale)(throw_ = true); #2 int i = convert<int>::from(str, 0) >> new_locale >> dothrow;
Should I remove #1?
You should remove #2, not #1.
12345678901234567890123456789012345678901234567890123456789012345
Yes, that's what I *actually* meant to say back then. :-) Now though after having both around I am not sure. As I mentioned I do like named-parameter-based interface -- I find it pleasant aesthetically (I just happen to like op()) and friendly. However, while typing I find myself favoring the frugality and practicality of #2. It is probably because
1) #2 uses the same interface/notation as ">> std::hex" (I admit of being a consistency/uniformity freak);
I like consistency, too, but as Emerson said, "a foolish consistency is the hobgoblin of little minds," so let's not put too much stock in it.
2) #2 is less to type (#1 requires using namespace boost::conversion::parameter;) (surely a weak argument but it saves me a round trip to the top of the file to add 'using');
That's a good argument actually, especially when coupled with...
3) I do not seem to find any other use for the named-parameter- based interface beyond locale_ and throw_ (so that the support basefor #1 is somewhat small... although this interface clearly scales better and more uniformly).
Combining the need for a using directive with the lack of named parameters, I find the use of named parameters questionable. They add compile time overhead and some code complexity for relatively little value. I have forgotten now the arguments against the following, but I offer it again as a better interface because there's no using directive needed, yet there's a distinction between influencing the convert framework's behavior and the IOStreams conversion operation: int i(convert<int>::from(str, 0).locale(new_locale).throw_()); That is, rather than named parameters *or* (to my mind) misusing manipulators, simply use function calls to set those options. That even permits named functions for related options like classic_locale(), nothrow(), etc., easily providing additional flexibility and expressiveness. Manipulators added to that convert expression, then, are ordinary manipulators that affect the underlying stream. There are none that apply to the convert framework while the rest apply to the stream. Separating the interface to those two parts of the machinery is a wise course, don't you agree? _____ 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.

> From: "Stewart, Robert" <Robert.Stewart@sig.com> >> >> #1 int i = convert<int>::from(str, 0)(locale_ = new_locale)(throw_ = >> >> true); >> >> #2 int i = convert<int>::from(str, 0) >> new_locale >> dothrow; >> 1) #2 uses the same interface/notation as ">> std::hex" (I >> admit of being a consistency/uniformity freak); > > I like consistency, too, but as Emerson said, "a foolish consistency > is the hobgoblin of little minds," so let's not put too much stock in it. Yes, I've heard that quote. The problem with its applicability here as I see it is that 1) we have to define what constitutes a *foolish* consistency; 2) in his "Self-Reliance" Emerson's interpretation of consistency was not even close to our interpretation of the word within our s/w engineering domain. Emerson's interpretation (and dismissal) of consistency is clearer when he elaborates further -- "Speak what you think now in hard words, and to-morrow speak what to-morrow thinks in hard words again, though it contradict every thing you said to-day". So, I feel that the deployment/interface consistency might not be that easily dismissed with Emerson. Still, I do realize that for better or worse i/o streams somewhat usurped >> & << operators. That said, I personally do not feel it is such a hurdle (to me anyway) and I am guilty of using op>>() for manipulators and locales. > ... > Combining the need for a using directive with the lack of named > parameters, > I find the use of named parameters questionable. They add compile time > overhead and some code complexity for relatively little value. > I have forgotten now the arguments against the following, > but I offer it again as a better interface because there's no > using directive needed, yet there's a distinction between > influencing the convert framework's behavior and the > IOStreams conversion operation: > > int i(convert<int>::from(str, 0).locale(new_locale).throw_()); I do not feel (or remember) there might have been arguments against the interface above. It is an old and humble alternative to Boost.Parameter - another approach to the "Named Parameter Idiom" described in http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.18. Still, I personally like Boost.Parameter better as I feel it implements the idiom in a generic and more consistent manner. More so, Boost.Parameter-based interface is easier to deploy across different conversion specializations. That said, it'd be ideal to collect more input from the people using boost::convert. I admittedly have not had much (if any) input but still hoping that the situation might improve with time and closer to the actuall review. > That is, rather than named parameters *or* (to my mind) misusing > manipulators, > simply use function calls to set those options. That even permits named > functions for related options like classic_locale(), nothrow(), etc., > easily providing > additional flexibility and expressiveness. Yes, as I said I do not have anything major against this apart from the fact that I feel Boost.Parameter might fire better accomplishing that. > Manipulators added to that convert expression, then, are ordinary > manipulators that affect the underlying stream. There are none > that apply to the convert framework while the rest apply to the stream. > Separating the interface to those two parts of the machinery is a wise > course, don't you agree? Well, out of laziness or some other reason I am guilty of simply typing "convert::from >> std::hex >> locale". I know 'std::hex' and 'locale' affect different underlying parts. However, on the application level it does not seem that important to me -- in the end it shapes up the conversion. So, I am not that determined about "separating the interface to those two parts of the machinery". It's like my transmission stick and the steering wheel are two different interfaces to two different parts of machinery. However, I would not probably mind if I could handle steering and transmission with just one thing. V.

on Mon Jun 29 2009, "Stewart, Robert" <Robert.Stewart-AT-sig.com> wrote:
3. I only managed to figure out how to supply only one Boost.Parameter at a time like
int i = convert<int>::from(str, 0)(locale_ = new_locale)(throw_ = true);
I understand how to achieve
int i = convert<int>::from(str, 0)((locale_ = new_locale, throw_ = true));
(with double quotes) but do not want to go there as it looks somewhat unorthodox. I cannot figure out how to achieve the following
The doubled parentheses are, indeed, rare outside Boost.Preprocessor type code.
int i = convert<int>::from(str, 0)(locale_ = new_locale, throw_ = true);
That is certainly preferable syntax.
It's not hard to achieve, but I don't think you'd like the effect: convert<int>::from(str, 0) would have to return a function object rather than an int. What's wrong with int i = convert<int>::from(str, 0, locale_ = new_locale, throw_ = true); ? -- Dave Abrahams BoostPro Computing http://www.boostpro.com

From: "David Abrahams" <dave@boostpro.com>
... What's wrong with
int i = convert<int>::from(str, 0, locale_ = new_locale, throw_ = true);
I personally to not feel there is anything wrong per se with the above. The reason I did not pursue it was that it introduces a limit on the number of the parameters and I did not see it offering any major advantages to balance that shortcoming over the supported int i = convert<int>::from(str, 0)(locale_ = new_locale)(throw_ = true); On the other hand the supported interface seems to scale considerably better and is no longer to type: int i = convert<int>::from(str, 0)(locale_ = new_locale)(throw_ = true); int i = convert<int>::from(str, 0, locale_ = new_locale, throw_ = true); V.

on Wed Jul 01 2009, "Vladimir Batov" <batov-AT-people.net.au> wrote:
From: "David Abrahams" <dave@boostpro.com>
... What's wrong with
int i = convert<int>::from(str, 0, locale_ = new_locale, throw_ = true);
I personally to not feel there is anything wrong per se with the above. The reason I did not pursue it was that it introduces a limit on the number of the parameters and I did not see it offering any major advantages to balance that shortcoming over the supported
int i = convert<int>::from(str, 0)(locale_ = new_locale)(throw_ = true);
On the other hand the supported interface seems to scale considerably better and is no longer to type:
int i = convert<int>::from(str, 0)(locale_ = new_locale)(throw_ = true); int i = convert<int>::from(str, 0, locale_ = new_locale, throw_ = true);
But again, it has the downside that none of your calls above can return an actual int. That can have unexpected effects, e.g. in some_function_template( convert<int>::from(str, 0) ) when the deduced template argument is actually your proxy type. -- Dave Abrahams BoostPro Computing http://www.boostpro.com

And in addition to the unexpected effects, it just doesn't read or look right. from(str, 0)(more stuff); really makes me think 'more stuff' happens AFTER the conversion. This is one of the examples of why C programmers and many other language-ites just shake their heads at us. We can't seem to stick to writing code that even follows the rules of the language. (obviously the code, using a proxy, does follow the rules, but it doesn't look like it does). If you really want the multiple function call syntax, could we at least somehow put it first? ie (just one possible way), and note that 'convert' has become 'converter': converter<int>(locale_ = loc)(throw_ = true).from(str, 0); And as a bonus, I can reuse the 'converter' on the next line if I store it somewhere. And you can still use the simple 'convert' (without the er) if no extra params are needed: convert<int>::from(str, 0) And now it really does return an int. Tony On Wednesday, July 1, 2009, David Abrahams <dave@boostpro.com> wrote:
on Wed Jul 01 2009, "Vladimir Batov" <batov-AT-people.net.au <http://batov-AT-people.net.au>> wrote:
From: "David Abrahams" <dave@boostpro.com>
... What's wrong with
int i = convert<int>::from(str, 0, locale_ = new_locale, throw_ = true);
I personally to not feel there is anything wrong per se with the above. The reason I did not pursue it was that it introduces a limit on the number of the parameters and I did not see it offering any major advantages to balance that shortcoming over the supported
int i = convert<int>::from(str, 0)(locale_ = new_locale)(throw_ = true);
On the other hand the supported interface seems to scale considerably better and is no longer to type:
int i = convert<int>::from(str, 0)(locale_ = new_locale)(throw_ = true); int i = convert<int>::from(str, 0, locale_ = new_locale, throw_ = true);
But again, it has the downside that none of your calls above can return an actual int. That can have unexpected effects, e.g. in
some_function_template( convert<int>::from(str, 0) )
when the deduced template argument is actually your proxy type.
-- Dave Abrahams BoostPro Computing http://www.boostpro.com
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

From: "Gottlob Frege" <gottlobfrege@gmail.com> And in addition to the unexpected effects, it just doesn't read or look right.
from(str, 0)(more stuff);
really makes me think 'more stuff' happens AFTER the conversion.
Well, I am not sure I can agree that those mentioned "unexpected effects" is such a big deal. The "problem" with the deduced template parameters is the only "problem" I've come across so far. In fact, that issue is not unique to 'convert' and 'convert' addresses that is a fairly standard way. Granted, not having that 'hack' would be better.
This is one of the examples of why C programmers and many other language-ites just shake their heads at us. We can't seem to stick to writing code that even follows the rules of the language. (obviously the code, using a proxy, does follow the rules, but it doesn't look like it does).
I hear you. :-)
If you really want the multiple function call syntax, could we at least somehow put it first? ie (just one possible way), and note that 'convert' has become 'converter':
converter<int>(locale_ = loc)(throw_ = true).from(str, 0);
I am not sure why 'convert' had to become 'converter' so for the moment I'll stick with what exists already. That makes it int i = convert<int>(locale_=loc)(throw_=true).from(str,0); vs int i = convert<int>::from(str,0)(locale_=loc)(throw_=true); I am not sure. I'll have to sleep on it. Something is drawing me to the syntax you suggest but I am not sure what it is. What I like about the existing syntax though is its incremental flowing style: int i = convert<int>::from(str,0); // simple form int i = convert<int>::from(str,0)(locale_=loc); // with locale int i = convert<int>::from(str,0)(locale_=loc)(throw_=true); The above looks more predictable and flows better for me.
And as a bonus, I can reuse the 'converter' on the next line if I store it somewhere.
Yes, that functionality is already available (like one can use the same conversion with an algorithm) although maybe not in such an explicit form. That functionality was suggested, fairly straightforward to achieve within the existing framework, just did not get time to make it happen.
And you can still use the simple 'convert' (without the er) if no extra params are needed:
convert<int>::from(str, 0)
What I feel uncomfortable with is that "if this, use converter, if that, use convert, if this, use one syntax, if that some other syntax". To me that lacks predictability which is the first step towards familiarity that leads to confidence and acceptance (something any library writer wants).
And now it really does return an int.
After looking at the problem for a while I am not sure that 'convert' really returning a straight 'int' is achievable without denying many legitimate use-cases. Though I by no means can claim the last word in the domain. V.

Gottlob Frege wrote: [please don't top post]
And in addition to the unexpected effects, it just doesn't read or look right.
from(str, 0)(more stuff);
really makes me think 'more stuff' happens AFTER the conversion. This is one of the examples of why C programmers and many other language-ites just shake their heads at us. We can't seem to stick to writing code that even follows the rules of the language. (obviously the code, using a proxy, does follow the rules, but it doesn't look like it does).
That's probably why I've thought the named function approach clearer: from(str, 0).more().stuff(); It deviates less from the usual, though I do acknowledge your complaint about the sequence. Here's another way to make it more explicit: from(str, 0).with(more stuff);
If you really want the multiple function call syntax, could we at least somehow put it first? ie (just one possible way), and note that 'convert' has become 'converter':
converter<int>(locale_ = loc)(throw_ = true).from(str, 0);
Why not with the syntax Dave has been suggesting: converter<int>(locale_ = loc, throw_ = true).from(str, 0); _____ 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.

Sorry it is a limitation of the iPhone. It is very hard to edit emails. :( Tony (I'll try not to email from the phone too much) On Thursday, July 2, 2009, Stewart, Robert <Robert.Stewart@sig.com> wrote:
Gottlob Frege wrote:
[please don't top post]
And in addition to the unexpected effects, it just doesn't read or look right.
from(str, 0)(more stuff);
really makes me think 'more stuff' happens AFTER the conversion. This is one of the examples of why C programmers and many other language-ites just shake their heads at us. We can't seem to stick to writing code that even follows the rules of the language. (obviously the code, using a proxy, does follow the rules, but it doesn't look like it does).
That's probably why I've thought the named function approach clearer:
from(str, 0).more().stuff();
It deviates less from the usual, though I do acknowledge your complaint about the sequence. Here's another way to make it more explicit:
from(str, 0).with(more stuff);
If you really want the multiple function call syntax, could we at least somehow put it first? ie (just one possible way), and note that 'convert' has become 'converter':
converter<int>(locale_ = loc)(throw_ = true).from(str, 0);
Why not with the syntax Dave has been suggesting:
converter<int>(locale_ = loc, throw_ = true).from(str, 0);
_____ 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. _______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

2009/7/2 Stewart, Robert <Robert.Stewart@sig.com>:
Why not with the syntax Dave has been suggesting:
converter<int>(locale_ = loc, throw_ = true).from(str, 0);
To me, that's the nicest one yet proposed. And it'd be quite reasonable, instead of statics, to just use convert<int>().from(str, 0), since that's already common from function objects. And actually, why not spell from() as operator(), so it can be a proper function object? transform( istream_iterator<string>(cin), istream_iterator<string>(), back_inserter(v), convert<int>(locale_ = loc, base_ = 16, throw_ = false, default_ = 0));

On Thursday, July 2, 2009, Scott McMurray <me22.ca+boost@gmail.com> wrote:
2009/7/2 Stewart, Robert <Robert.Stewart@sig.com>:
Why not with the syntax Dave has been suggesting:
converter<int>(locale_ = loc, throw_ = true).from(str, 0);
To me, that's the nicest one yet proposed.
I think that the only complaint was that it limits the number of params, whereas the other version does not. But if you have more than 10 params, I think you have bigger problems.
And it'd be quite reasonable, instead of statics, to just use
I didn't see that as a static, but as a constructor call.
convert<int>().from(str, 0), since that's already common from function objects.
And actually, why not spell from() as operator(), so it can be a proper function object?
transform( istream_iterator<string>(cin), istream_iterator<string>(), back_inserter(v), convert<int>(locale_ = loc, base_ = 16, throw_ = false, default_ = 0)); _______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

2009/7/2 Gottlob Frege <gottlobfrege@gmail.com>:
And it'd be quite reasonable, instead of statics, to just use
I didn't see that as a static, but as a constructor call.
Sorry, I was referring to using convert<int>::from when you don't need to pass parameters to the constructor.

Hi, ----- Original Message ----- From: "Gottlob Frege" <gottlobfrege@gmail.com> To: <boost@lists.boost.org> Sent: Thursday, July 02, 2009 6:55 PM Subject: Re: [boost] [convert] Now with Boost.Parameter interface. On Thursday, July 2, 2009, Scott McMurray <me22.ca+boost@gmail.com> wrote:
2009/7/2 Stewart, Robert <Robert.Stewart@sig.com>:
Why not with the syntax Dave has been suggesting:
converter<int>(locale_ = loc, throw_ = true).from(str, 0);
To me, that's the nicest one yet proposed.
I think that the only complaint was that it limits the number of params, whereas the other version does not. But if you have more than 10 params, I think you have bigger problems.
And it'd be quite reasonable, instead of statics, to just use
I didn't see that as a static, but as a constructor call.
convert<int>().from(str, 0), since that's already common from function objects.
And actually, why not spell from() as operator(), so it can be a proper function object?
transform( istream_iterator<string>(cin), istream_iterator<string>(), back_inserter(v), convert<int>(locale_ = loc, base_ = 16, throw_ = false, default_ = 0));
I have followed this thread and I would like to share with you my view. IMO we need a converter function and a convert free function that returns the To type. With the interface below we should be able to * make a conversion using a default converter (which could throw if the conversion fails) int i = convert<int>(str); * make a conversion using a specific converter constructed form these coverter parameters int i=convert<int>(str, (locale_ = loc, base_ = 16, throw_ = false, default_ = 0)); Note that we can also have int i=convert<int>(str, (locale_ = loc)(base_ = 16)(throw_ = false)(default_ = 0)); if the converter_parameters defines the operator(). * transform using a converter functor transform( istream_iterator<string>(cin), istream_iterator<string>(), back_inserter(v), converter<int>((locale_ = loc, base_ = 16, throw_ = false, default_ = 0))); The class converter_parameters is used to store all the parameters related to the conversion. It should be close to the internal representation of the current converter convert_detail::implementation<TypeIn, TypeOut> but without the TypeIn template <typename To> class converter_parameters { ... } The converter class is the functor class realizing the conversion using the conversion parameters template <typename To> class converter { converter(); converter(converter_parameters<To> const&); template <typenam From> To operator()(From); }; the convert function now return the out type (To) and not a converter template <typename To, typename From> To convert(From f, converter<To> const&cv) { return cv(f); } template <typename To, typename From> To convert(From f, converter_parameters<To> const&cvp) { converter<To> cv(cvp); return convert(f, cv); } template <typename To, typename From> To convert(From f) { converter<To> cv; return convert(f, cv) } I dont' think the current implementation needs to much changes to be adapted to this proposal. HTH, Vicente

Scott McMurray <me22.ca+boost <at> gmail.com> writes:
converter<int>(locale_ = loc, throw_ = true).from(str, 0);
To me, that's the nicest one yet proposed.
And it'd be quite reasonable, instead of statics, to just use convert<int>().from(str, 0), since that's already common from function objects.
I am not sure I can immediately see the visual advantage of convert<int>().from(str, 0) over convert<int>::from(str, 0) Functionally, (with everything else equal) #1 has an additional overhead of creating an object.
And actually, why not spell from() as operator(), so it can be a proper function object?
If I remember correctly, one of the reasons for 'from' was the explicit directiveness of the conversion -- convert<int>::from(string). Functionally, the advantage of a static 'from' function over op() is that the former allows to delay implementation specialization (and, therefore, converter instantiation) until we know both -- TypeIn and TypeOut -- conversion types. It's turned out to be quite important as the actual conversion implementation equally depends on TypeIn as on TypeOut. For that reason I feel that constructing an instance of converter<TypeOut> (with only TypeOut known) is very limiting. V.

From: "Stewart, Robert" <Robert.Stewart@sig.com> Gottlob Frege wrote:
And in addition to the unexpected effects, it just doesn't read or look right.
from(str, 0)(more stuff); ... Here's another way to make it more explicit:
from(str, 0).with(more stuff);
Yes, I think I like Rob's 'with'. I feel this does rectify whatever "it just doesn't read or look right" there might be and it can be added quite easily to he existing interface. I personally like with() better than suggested before locale(), throw_(), etc. as it's only one new method to remember. My only concern is that spelling out everything has limited value -- only for an uninitiated person. For a seasoned user having to explicitly spell out everything is likely to be irritating. I am certainly talking about my own experiences. Is "str.append(other-string)" really better/clearer then "str += other-string"? I expect anyone who grew up with C to appreciate the latter syntax. To me it is pretty much the same with "from(str, 0)(more)" as it deploys a known C++ technique of chaining with op(). That's why (given choice) while learning/playing I might probably start with from(str, 0).with(more stuff); but once familiar with it, I'll likely switch to from(str, 0)(more stuff); Maybe provide both?
If you really want the multiple function call syntax, could we at least somehow put it first? ie (just one possible way), and note that 'convert' has become 'converter':
converter<int>(locale_ = loc)(throw_ = true).from(str, 0);
Why not with the syntax Dave has been suggesting:
converter<int>(locale_ = loc, throw_ = true).from(str, 0);
converter<int>(locale_ = loc)(throw_ = true) converter<int>(locale_ = loc, throw_ = true) To me the two above are only marginally different (and hardly visually different). Is it possible to identify what advantages of #2 there might be? As for #1, it is an instantly-recognizable and fairly common technique of chaining with op(). Unlike #2 it scales up with no issues. Providing a limited number of parameters is "cheating" as Alexantrescu puts it in his "Modern Design" book. :-) I understand that in practical terms it might not make any difference here... but why introduce a limitation when we do not have to? As for converter<TypeOut>, I suspect it won't be able to take us far enough as it does not take into account TypeIn which is important for implementation specialization. People following this thread might remember that at one stage the implementation/interface was "convert[er]<TypeIn, TypeOut>". Then, I feel it was an improvement when we kept internally "convert[er]<TypeIn, TypeOut>" but externally were able to deduce TypeIn via a function with convert<TypeOut>::from(TypeIn). That said, we certainly can re-visit that "convert[er]<TypeIn, TypeOut>". Then, we'll be able to do the suggested converter<TypeIn, TypeOut>(local_=loc).from(type-in-value) converter<TypeIn, TypeOut>(local_=loc)(type-in-value) converter<TypeIn, TypeOut>(type-in-value) To me it does have its own beauty and uniformity. V.

From: "David Abrahams" <dave@boostpro.com> ...
int i = convert<int>::from(str, 0)(locale_ = new_locale)(throw_ = true); int i = convert<int>::from(str, 0, locale_ = new_locale, throw_ = true);
But again, it has the downside that none of your calls above can return an actual int. That can have unexpected effects, e.g. in
some_function_template( convert<int>::from(str, 0) )
when the deduced template argument is actually your proxy type.
Yes, that is a fair point. The way around it is some_function_template( convert<int>::from(str, 0).value() ) Returning the value (rather than a proxy) was *very* tempting... but seemed *very* limiting. Like chaining of additional configuration parameters (notably i/o stream manipulatiors) would be impossible. Still, I'll take another look if I can see things differently this time around. V.

I've incorporated, uploaded to the Vault and started playing with the Boost.Parameter-based interface that Andrey Semashev was insisting from the set-go. And it is definitely growing on me and I like
int i = convert<int>::from(str, 0)(locale_ = new_locale)(throw_ = true);
instead of the original
int i = convert<int>::from(str, 0) >> new_locale >> dothrow;
I think people generally did not like locales and dothrow behaving like manipulators.
Sorry if I'm late here. Either of both ways just feels wrong as not all of the manipulators/parameters are of general use. The radix_ parameter described elsewhere is a good example for a parameter applicable to string <--> integer conversion only. It is highly confusing not to get a compile time error while specifying a wrong parameter (a parameter not being applicable for the type to convert to/from). For instance, what is supposed to happen if I do: convert<double>::from(str, radix_ = 16) ? The current approach in my view mixes things not belonging together. A conversion library needs to keep apart data and formatting information. This is not done here. Formatting and data type are spread over the whole expression, making it very difficult to get things right. I believe this is a strength of Spirit's approach, where data and formatting are clearly separated. Applying a similar approach to convert would result in: double d = convert::from(str, double_); and std::string s = convert::to(d, double_); where double_ is an example for a placeholder allowing to specify arbitrary formats (remember, Spirit allows to build more complex formats/grammars out of simpler ones, while providing all necessary primitive type conversions). For instance, applying a different locale to this results in: real_spec<double> locale_double(new_locale); double d = convert::from(str, locale_double); and std::string s = convert::to(d, locale_double); where real_spec (choose a different name, if you like) creates a new placeholder encapsulating all formatting information needed to do the conversion. This has the additional advantage of providing a nicely extensible framework. Any non-foreseeable formatting requirements are easily customizable. OTOH, general parameters applying directly to the conversion process (such as throwing behavior or default values) are fine to be passed as additional arguments. But care must be taken not to intermix this with formatting related parameters (and yes, believe me or not, locales _are_ formatting related). So I think the following would be ok: double d = convert::from(str, double_, default_ = 0.0); and std::string s = convert::to(d, double_, throw_ = true); BTW: this interface above is easily implementable on top of Spirit. It's just a thin wrapper around existing functionality. And I already expressed my opinion that any high level conversion library should be built on top of Spirit - a proven, fast, reliable, versatile, and mature parser and generator framework. Anything else leads to code duplication, inconsistencies, implementation flaws, you name it. Converting a string of digits into an integer might seem to be an easy task. But it's not. It's tricky, error prone, and difficult to get correct _and_ fast at the same time. But all that is certainly purely IMHO. Regards Hartmut

Hartmut Kaiser wrote:
BTW: this interface above is easily implementable on top of Spirit. It's just a thin wrapper around existing functionality. And I already expressed my opinion that any high level conversion library should be built on top of Spirit - a proven, fast, reliable, versatile, and mature parser and generator framework. Anything else leads to code duplication, inconsistencies, implementation flaws, you name it. Converting a string of digits into an integer might seem to be an easy task. But it's not. It's tricky, error prone, and difficult to get correct _and_ fast at the same time.
I think the barrier to adoption is the size of the Spirit library and its dependencies. For example, I'd be afraid to write a small library that forces my users to depend on Proto, MPL, and Fusion too. That, IMO, is too much to ask. People might not be aware though that Spirit is very modular. The numeric parsing/generating utilities are in fact just a couple of small *independent* header files. It might be a good idea to fully decouple them from Spirit into, say, Boost.Utilities. This is becoming more pertinent now that more people are needing them. Indeed, parsing and generating numbers is not easy. Make no mistake. Regards, -- Joel de Guzman http://www.boostpro.com http://spirit.sf.net

Hartmut Kaiser <hartmut.kaiser <at> gmail.com> writes:
int i = convert<int>::from(str, 0)(locale_ = new_locale)(throw_=true); int i = convert<int>::from(str, 0) >> new_locale >> dothrow;
Sorry if I'm late here. Either of both ways just feels wrong as not all of the manipulators/parameters are of general use. The radix_ parameter described elsewhere is a good example for a parameter applicable to string <--> integer conversion only. It is highly confusing not to get a compile time error while specifying a wrong parameter (a parameter not being applicable for the type to convert to/from).
For instance, what is supposed to happen if I do:
convert<double>::from(str, radix_ = 16)
Well, I agree that "it is highly confusing not to get a compile time error while specifying a wrong parameter". Fortunately, it does not have to be that way and is up to the conversion implementer. Say, convert<double>::from(str)(radix_ = 16) convert<double>::from(str) >> std::hex Both above internally deploy a specialization of some converter<TypeIn, TypeOut>. Then, additional formatting (like op() for 'radix_=16' or op>> for '>> std::hex') is passed to *that* converter. That is the #1 above can be seen as converter<std::string, double> cvt = convert<double>::from(str); cvt(radix_ = 16); double d = cvt.operator double(); It's all up to the implementer of converter<std::string, double> to accept or reject 'radix_=16' during *compilation*.
The current approach in my view mixes things not belonging together. A conversion library needs to keep apart data and formatting information. This is not done here. Formatting and data type are spread over the whole expression, making it very difficult to get things right.
Well, I probably cannot fully agree with "formatting and data type are spread" as it is always data first, then the rest. Say, my typical usage is convert<double>::from(str) >> locale >> std::scientific; the first line has the data and the rest is about formatting. Even in convert<double>::from(str)(locale_=locale) >> std::scientific; it seems clear that the data comes first with everything else following. As for "conversion library needs to keep apart data and formatting" I think in 'convert' data and formatting are apart due to the conversion specification *order*. Your preference (as I understand) is to take that separation further (as in Spirit) physically separating the formatting into a separate object.
I believe this is a strength of Spirit's approach, where data and formatting are clearly separated.
Well, I'd insist on just :-) "clearer separated" as I feel 'convert' does provide that separation although not to the degree Spirit does. And here I suspect we are talking about *visual* separation as under the hood 'convert' can be as strict (data-wise and formatting-wise) as the implementer likes.
Applying a similar approach to convert would result in:
double d = convert::from(str, double_); and std::string s = convert::to(d, double_);
where double_ is an example for a placeholder...
Please bear with me as I cannot claim familiarity with Spirit. I am not sure if/how that placeholder-based approach can be uniformly extended onto user types. I'd expect something like direction d = convert::from(str, direction_); That is, for a 'direction' user type to be integrated into the 'convert' framework, the user needs do provide a 'direction_' specification. If so, then I have no serious objections against that. It's (I think) in line with what Vicente was suggesting with convert_traits and it is *somewhat* similar to the current lexical_cast/convert approach. Although lexical_cast/convert limit that "specification" to only op>> and op<<.
Spirit allows to build more complex formats/grammars out of simpler ones, while providing all necessary primitive type conversions). For instance, applying a different locale to this results in:
real_spec<double> locale_double(new_locale); double d = convert::from(str, locale_double); and std::string s = convert::to(d, locale_double);
where real_spec (choose a different name, if you like) creates a new placeholder encapsulating all formatting information needed to do the conversion.
Well, I feel you are arguing the interface -- forcing the user to provide the formatting as a separate fmt object as the following could easily have the same implementation. real_spec<double> locale_double(new_locale); double d = convert::from(str, locale_double); or double d = convert::from(str, real_spec<double>(new_locale)); or double d = convert::from(str)(real_spec<double>(new_locale)); or double d = convert::from(str).locale(new_locale); or double d = convert::from(str)(locale_=new_locale); As for the interface, your approach might well be justified for Spirit applications. From *my* usage experience of 'convert' I feel for 'convert' it'd be an overkill. Please bear in mind that lexical_cast/convert never had Spirit-like ambitions and should be never treated as a serious converter of any sort. That does not mean, I do not feel 'convert' has no place. If you like 'convert' is a bike to get a short distance from A to B quickly and without much/any preparation. Spirit is for a longer trip. My usage pattern is to get a dozen or more config. parameters, convert them to their binary form, report conversion failures and get to my main domain-specific (non-parsing-related) business. Therefore, I am only prepared to allocate 12 lines of code to convert 12 parameters and to get to my main business ASAP.
This has the additional advantage of providing a nicely extensible framework. Any non-foreseeable formatting requirements are easily customizable.
I honestly see neither of 'convert' or Spirit approaches easier/harder to customize. That customization has to be implemented somewhere. Spirit (as I understand) does it via that "formatting specification" object. 'convert' has the converter. In fact, come to think of it now I not sure how real_spec<double> can help as TypeOut ('double' in the example) is only half of the story. Formatting equally depends on TypeIn. Say, I do not think we can allow/disallow (radix_=16) for real_spec<int> as we do not yet know if we are converting from a string.
OTOH, general parameters applying directly to the conversion process (such as throwing behavior or default values) are fine to be passed as additional arguments. But care must be taken not to intermix this with formatting related parameters (and yes, believe me or not, locales _are_ formatting related). So I think the following would be ok:
double d = convert::from(str, double_, default_ = 0.0); and std::string s = convert::to(d, double_, throw_ = true);
Don't you think I can apply the following *interface* transformation without undermining the actual implementation: double d = convert::from(s, double_formatter, default_ = 0.0); to double d = convert<double>::from(s, default_=0.0)(double_formatter); to double d = convert<double>::from(s,0)(double_formatter); to double d = convert<double>::from(s,0)(double_fmt_part1)(double_fmt_part2); which the current interface. ;-)
BTW: this interface above is easily implementable on top of Spirit. It's just a thin wrapper around existing functionality. And I already expressed my opinion that any high level conversion library should be built on top of Spirit...
I do not think anyone even argued against that. And I do not feel that hte existing interface stops us from dong that.
... - a proven, fast, reliable, versatile, and mature parser and generator framework.
:-)
Anything else leads to code duplication, inconsistencies, implementation flaws, you name it.
Well, I personally do not see code duplication, etc. what I see is different horses for different courses. With all due respect as of now I see Spirit as too big a hammer for my purposes.
Converting a string of digits into an integer might seem to be an easy task. But it's not. It's tricky, error prone, and difficult to get correct _and_ fast at the same time.
I surely agree. That said, in many many cases people do not need that complexity and processing power. lexical_cast (and convert -- a glorified lexical_cast) is far from perfect. However, its functionality (and often even speed) is sufficient (well, almost) and it's better known in average-user circles and easier to understand and deploy. I spend my day writing fuzzy logic related to train movements. I only care about conversions as much as reading from a config. file or reading inter-process XML. For that as of now I find Spirit too steep a ladder to claim for my current needs. It's not a criticism of any kind, just how I feel. V.
participants (15)
-
David Abrahams
-
Emre Turkay
-
Gottlob Frege
-
Hartmut Kaiser
-
Joel de Guzman
-
Marcus Lindblom
-
Max
-
Scott McMurray
-
Stewart, Robert
-
Vicente Botet Escriba
-
vicente.botet
-
Vladimir Batov
-
Vladimir Batov
-
Vladimir Prus
-
Vladimir.Batov@wrsa.com.au