Re: [boost] Formal Review Request: Boost.String.Convert

I understand we'll never manage to please everyone. Apologies. For now I am settling on the boost::convert interface as I very much agree with the arguments Robert's put forward for it and I feel there is the least disagreement around this interface. Surely boost::convert() has issues of its own. However, I feel the path (convert::to_string->convert::to->convert) how I myself arrived to this logical (IMHO) conclusion seemed reasonable. More so, despite its subtle (to some) directionality the interface has proven to work (as it is essentially the same interface as for lexical_cast). You cannot deny that I made considerable sacrifices (my pets "string" namespace and "to/from" are gone, something I was ready to die :-) for yesterday...). I am hoping the others could make some concessions as well and we would settle on boost::convert()... for now. If some other issues pop up that we missed so far, we'll revisit the issue. It is because I would very much like to move forward and start looking at the functionality and implementation. Do you think it is possible or I am rushing things again? I somewhat cleaned up the implementation (removed to/from, etc.). However, before going forward I'd like to discuss one important thing that's been bugging me since Scott brought it up. Namely, all conversions are now implemented as discriminated functions like // string-to-type conversion template<class TypeOut, class StringIn> typename boost::enable_if_c< !is_string<TypeOut>::value && is_string<StringIn>::value, detail::from_string<StringIn, TypeOut>
::type convert(StringIn const& from_str, TypeOut const& default_value =TypeOut()) { return detail::from_string<StringIn, TypeOut>(from_str, default_value); }
However, Scott mentioned that and I feel that might be cleaner if we could implement it as namespace boost { template<class TypeOut, class TypeIn, class Enable =void> class convert; } Then for every conversion we'll be able to encapsulate *complete* implementation in the respective specialization class. Like for string-to-type template< class TypeOut, class StringIn, typename boost::enable_if_c< !is_string<TypeOut>::value && is_string<StringIn>::value, void>::type> class convert { convert(StringIn const& from_str, TypeOut const& default_value =TypeOut()); }; The reason I like it is that it eliminates other support/intermediate classes (like something used-to-called boost::string::value<...> and detail::from_string) and we can do int i = convert(str, -1); // Nothing changed. a 'convert' instance created on the stack and converted to 'int'. convert<int, string> checked(str, -1); // named 'convert' is created. convert<int, string> checked = convert(str, -1); // the same but some might like this syntax better. if (checked.good()) success // we can check the success as ussed to with boost::string::value It feels that approach gives us considerably more room for expansion as some special conversions will be able to have methods only specific to that particular conversion. For this approach to work with algorithms we'll need class convert { static TypeOut apply(TypeIn const&); }; and then std::transform( ints.begin(), ints.end(), std::back_inserter(strings), convert<string, int>::apply); Thanks, Vladimir.

Correction. I meant to say the following namespace boost { template<class TypeIn, class TypeOut, class Enable =void> class convert; } // string-to-type specialization template<class StringIn, class TypeOut> class convert< StringIn, TypeOut, typename boost::enable_if_c< is_string<StringIn>::value && !is_string<TypeOut>::value, void>::type> > { convert(StringIn const&, TypeOut const& =TypeOut()); operator TypeOut const& () const {...} static TypeOut apply(StringIn const&) { ...} }; int string_to_int = convert<string, int>(str, -1); int string_to_int = convert<string, int>(str); convert<string, int> string_to_int(str, -1); convert<string, int> string_to_int = convert<string, int>(str, -1); std::transform( integers.begin(), integer.end(), std::back_inserter(strings), convert<string, int>::apply); As you can see there is *no* type-deducing. That is, with that approach we'll have to always indicate 'to' and 'from' types. That seems verbose... but might be a plus as it takes *any* ambiguity out. That gives us a lot of freedom. Like 1. using boost::convert; 2. typedef boost::convert<string, int> StringToInt; 3. optimization can be done on individual type-to-type or category-to-category basis 4. any specific exotic conversion can be added on when needed basis and locally by the user. 5. conversion-specific methods can be added to individual conversions or constructors extended (as in one of Scott's examples: struct convert< string, int, enable_if> { convert(string, int) convert(string, int, rounding) // Added special constructor } V.

On Thursday, February 19, 2009 2:57 AM Vladimir Batov wrote:
namespace boost { template<class TypeIn, class TypeOut, class Enable =void> class convert;
s/convert/converter/ // leave default value computation to converter template <class TypeOut, class TypeIn> TypeOut const convert(TypeIn _input) { return converter<TypeIn,TypeOut>(_input); } template <class TypeOut, class TypeIn> TypeOut const convert(TypeIn _input, TypeOut const & _default) { return converter<TypeIn,TypeOut>(_input, _default); }
} [snip class template declaration]
int string_to_int = convert<string, int>(str, -1);
int s2i(convert<int>(str, -1));
int string_to_int = convert<string, int>(str);
s2i = convert<int>(str);
convert<string, int> string_to_int(str, -1);
converter<string,int> c(str, -1);
convert<string, int> string_to_int = convert<string, int>(str, -1);
c = convert<int>(str, -1);
std::transform( integers.begin(), integer.end(), std::back_inserter(strings), convert<string, int>::apply);
s/convert/converter/
As you can see there is *no* type-deducing. That is, with that approach we'll have to always indicate 'to' and 'from' types. That seems verbose... but might be a plus as it takes *any* ambiguity out.
As you can see, there is type deduction. That is, with this approach, we won't need to indicate the source and destination types in all contexts, but can when needed to remove ambiguity. _____ Rob Stewart robert.stewart@sig.com Software Engineer, Core Software using std::disclaimer; Susquehanna International Group, LLP http://www.sig.com IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

Stewart, Robert wrote:
On Thursday, February 19, 2009 2:57 AM Vladimir Batov wrote:
namespace boost { template<class TypeIn, class TypeOut, class Enable =void> class convert;
s/convert/converter/
int string_to_int = convert<string, int>(str, -1);
int s2i(convert<int>(str, -1));
int string_to_int = convert<string, int>(str);
s2i = convert<int>(str);
convert<string, int> string_to_int(str, -1);
converter<string,int> c(str, -1);
convert<string, int> string_to_int = convert<string, int>(str, -1);
c = convert<int>(str, -1);
I like it. Looks nice and clean.
std::transform( integers.begin(), integer.end(), std::back_inserter(strings), convert<string, int>::apply);
s/convert/converter/
...and, I guess, converter< int, string > is more correct in this example. It looks like the static "apply" will not allow to pass additional conversion parameters, except by resorting to Boost.Bind or something similar. Why not simply provide operator() in the converter? Then having support of Boost.Parameter, we could pass some customization to the converter constructor: std::transform( integers.begin(), integers.end(), std::back_inserter(strings), converter<int, string>(radix_ = 16, uppercase_ = true));

It looks like the static "apply" will not allow to pass additional conversion parameters, except by resorting to Boost.Bind or something similar. Why not simply provide operator() in the converter?
Yes, I think it's a good idea. Will do. the 'apply' was a quick hack anyway.
Then having support of Boost.Parameter, we could pass some customization to the converter constructor:
std::transform( integers.begin(), integers.end(), std::back_inserter(strings), converter<int, string>(radix_ = 16, uppercase_ = true));
I looked a Boost.Parameter and will certainly try using it somewhere. My hesitation with it for boost::convert is I am under impression that the set of acceptable parameters (radix_, etc.) has to be predefined and therefore cannot be extended by the user when formatting like "convert(str) >> std::hex" seems more flexible. V.

On Fri, Feb 20, 2009 at 6:48 PM, Vladimir Batov <batov@people.net.au> wrote:
It looks like the static "apply" will not allow to pass additional conversion parameters, except by resorting to Boost.Bind or something similar. Why not simply provide operator() in the converter?
Yes, I think it's a good idea. Will do. the 'apply' was a quick hack anyway.
Then having support of Boost.Parameter, we could pass some customization to the converter constructor:
std::transform( integers.begin(), integers.end(), std::back_inserter(strings), converter<int, string>(radix_ = 16, uppercase_ = true));
I looked a Boost.Parameter and will certainly try using it somewhere. My hesitation with it for boost::convert is I am under impression that the set of acceptable parameters (radix_, etc.) has to be predefined and therefore cannot be extended by the user when formatting like "convert(str) >> std::hex" seems more flexible.
You could simply take a formatting string. Emil Dotchevski Reverge Studios, Inc. http://www.revergestudios.com/reblog/index.php?n=ReCode

cannot be extended by the user when formatting like "convert(str) >> std::hex" seems more flexible.
You could simply take a formatting string.
Could you be more specific? An example? What format you'd suggest? How is it fed to the convert class? It's just that I am a tactile kind of person. I find it hard to keep up without seeing, touching and tasting. Thanks, V.

Vladimir Batov wrote:
Then having support of Boost.Parameter, we could pass some customization to the converter constructor:
std::transform( integers.begin(), integers.end(), std::back_inserter(strings), converter<int, string>(radix_ = 16, uppercase_ = true));
I looked a Boost.Parameter and will certainly try using it somewhere. My hesitation with it for boost::convert is I am under impression that the set of acceptable parameters (radix_, etc.) has to be predefined and therefore cannot be extended by the user when formatting like "convert(str) >> std::hex" seems more flexible.
You can add new keywords as needed. The point is that the underlying conversion mechanism can support a subset of these keywords, specific to the conversion domain. This way it will be a non-intrusive extension. On the other hand, using manipulators, like std::hex, is clearly more limiting, as they may be meaningless for the particular conversion requested. For example, how would it be extended to support convert< short >(0xFFFFFFFF, throw_on_out_of_range_ = true)?

On the other hand, using manipulators, like std::hex, is clearly more limiting, as they may be meaningless for the particular conversion requested. For example, how would it be extended to support convert< short
(0xFFFFFFFF, throw_on_out_of_range_ = true)?
I am very open to whatever comes out of it. I am not married to those manipulators but you'll have to tell me more about using Boost.Parameter. To solve that particular problem that you describe the user would write his own manipulator and nothing needs to be done in boost::convert. That user manip. would do boundary checks and stuff. Then, short i = convert_to<short>("0xFFFFFFFF") >> my_short_manip; I personally do not like manipulators. I do not even really know how to write them properly. However, they are standard. So, we can say to someone -- you wanna do your own formatting? Bring in your manipulator and plug it in. Boost.Convert does not need to be changed to accommodate that specific request. How do you do that with Boost.Parameter? How Boost.Convert will know about "throw_on_out_of_range_" or any fancy parameter? How Boost.Convert will apply/interpret that parameter? If you tell me that the user can make Boost.Convert understand that new parameter without modifying Boost.Convert, I'll be first to abandon manipulators in their favor. V.

Vladimir Batov wrote:
I personally do not like manipulators. I do not even really know how to write them properly. However, they are standard. So, we can say to someone -- you wanna do your own formatting? Bring in your manipulator and plug it in. Boost.Convert does not need to be changed to accommodate that specific request.
How do you do that with Boost.Parameter? How Boost.Convert will know about "throw_on_out_of_range_" or any fancy parameter? How Boost.Convert will apply/interpret that parameter? If you tell me that the user can make Boost.Convert understand that new parameter without modifying Boost.Convert, I'll be first to abandon manipulators in their favor.
I haven't followed this thread, so I'm not familiar with this particular interface, but in general you could compose an ArgumentPack in the interface function, and pass that down to whatever implementation you choose. Something like: BOOST_PARAMETER_NAME(value) typedef parameters<required<tag::value> > convert_parameters; template <class U, class T, class A0, ...> U convert_to(T const& value, A0 const& a0, ...) { return convert_impl<U>(convert_parameters(value, a0, ...)); } The implementation would then just declare a keyword and fetch whatever parameters they are interested in from the ArgumentPack: BOOST_PARAMETER_NAME(width) template <class U, class ArgumentPack> U convert_impl(ArgumentPack const& args) { do_work( args[_value] , args[_width | 5] ); } -- Daniel Wallin BoostPro Computing http://www.boostpro.com

Daniel Wallin BoostPro Computing http://www.boostpro.com
Daniel, Thanks for chiming in. Much appreciated. I am not sure Boost.Parameter can be applied for the Boost.Convert task in question (although Andrey is really pushing for it :-)). However, I must say it is a very nifty thing you've come up with. I am thinking really hard where I might use it. :-) V.

on Sun Feb 22 2009, "Vladimir Batov" <batov-AT-people.net.au> wrote:
I am not sure Boost.Parameter can be applied for the Boost.Convert task in question (although Andrey is really pushing for it :-)). However, I must say it is a very nifty thing you've come up with. I am thinking really hard where I might use it. :-)
This seems to evade your earlier statement a bit:
Vladimir Batov wrote:
If you tell me that the user can make Boost.Convert understand that new parameter without modifying Boost.Convert, I'll be first to abandon manipulators in their favor.
-- Dave Abrahams BoostPro Computing http://www.boostpro.com

I am not sure Boost.Parameter can be applied for the Boost.Convert task in question (although Andrey is really pushing for it :-)). However, I must say it is a very nifty thing you've come up with. I am thinking really hard where I might use it. :-)
This seems to evade your earlier statement a bit:
If you tell me that the user can make Boost.Convert understand that new parameter without modifying Boost.Convert, I'll be first to abandon manipulators in their favor.
Dave, it probably might well appear this way... but I am not really trying to evade anything. The thing is that Boost.Parameter is a new thing to me and I have not seen much code using it. So, I am still not sure how to plug it into Boost.Convert (sorry for using the name I know it's not part of Boost, I just need to refer to it somehow quickly). Now I understand that Boost.Parameter allows me to call convert<string, int>(str, some_user_defined_param = ...); As I understand Boost.Convert does not know and does not make use of "some_user_defined_param" but takes it in using ArgumentPack. How then does the parameter make it to some user-defined formatter? Maybe it is because I am still thinking in terms of manipulators -- a user writes his own and plugs it into Boost.Convert as convert_to<int>(str) >> std::hex; How do we do that with Boost.Parameter? Should we write/specify some kind of Boost.Parameter-aware manipulator? What the signature then might be? Is there some other way? convert<string, int>(str, default_ = -1, throw_ = false); indeed looks trendy and I probably could extend the convert() interface that far. However, I understand Andrey's interest is in the user providing his own keyword (like throw_on_out_of_range_) so that it could be used somewhere for formatting. Where and how that's what I am having difficulty with (due to no experience with Boost.Parameter). V.

on Sun Feb 22 2009, "Vladimir Batov" <batov-AT-people.net.au> wrote:
I am not sure Boost.Parameter can be applied for the Boost.Convert task in question (although Andrey is really pushing for it :-)). However, I must say it is a very nifty thing you've come up with. I am thinking really hard where I might use it. :-)
This seems to evade your earlier statement a bit:
If you tell me that the user can make Boost.Convert understand that new parameter without modifying Boost.Convert, I'll be first to abandon manipulators in their favor.
Dave, it probably might well appear this way... but I am not really trying to evade anything. The thing is that Boost.Parameter is a new thing to me and I have not seen much code using it. So, I am still not sure how to plug it into Boost.Convert (sorry for using the name I know it's not part of Boost, I just need to refer to it somehow quickly).
Now I understand that Boost.Parameter allows me to call
convert<string, int>(str, some_user_defined_param = ...);
As I understand Boost.Convert does not know and does not make use of "some_user_defined_param" but takes it in using ArgumentPack. How then does the parameter make it to some user-defined formatter? Maybe it is because I am still thinking in terms of manipulators -- a user writes his own and plugs it into Boost.Convert as
convert_to<int>(str) >> std::hex;
Well, if the user-defined formatter is not part of the type of the converter (e.g. encoded in a template parameter) then you will indeed have some issues because there is a type-erasure boundary to cross. You could do it (Daniel did it when he extended Boost.Parameter to interoperate with Python), but I doubt you'd find Boost.Parameter to be a trivial drop-in solution in that case. Maybe it could be easily extended to do what you need, though. I don't know how close your use case comes to the Python use-case. Certainly, the interface you show above is easy enough to achieve. -- Dave Abrahams BoostPro Computing http://www.boostpro.com

On Sunday, February 22, 2009 11:31 PM Vladimir Batov wrote:
Now I understand that Boost.Parameter allows me to call
convert<string, int>(str, some_user_defined_param = ...);
As I understand Boost.Convert does not know and does not make use of "some_user_defined_param" but takes it in using ArgumentPack. How then does the parameter make it to some user-defined formatter? Maybe it is because I am still thinking in terms of manipulators -- a user writes his own and plugs it into Boost.Convert as
convert_to<int>(str) >> std::hex;
How do we do that with Boost.Parameter? Should we write/specify some kind of Boost.Parameter-aware manipulator? What the signature then might be? Is there some other way?
std::hex assumes some state in the stream for storing that hexadecimal formatting should be used in a subsequent conversion step. IOStreams provides a means to tack on extra state not built in (iword(), pword(), and xalloc()), so that's how user-defined formatting could save state to be used later, but it still requires the cooperation of the manipulators to set the state and the formatting logic invoked later. That same, save state and use it later when converting is needed for something like convert.
convert<string, int>(str, default_ = -1, throw_ = false);
indeed looks trendy and I probably could extend the convert() interface that far. However, I understand Andrey's interest is in the user providing his own keyword (like throw_on_out_of_range_) so that it could be used somewhere for formatting. Where and how that's what I am having difficulty with (due to no experience with Boost.Parameter).
I'd still like to see a coherent set of requirements. There are too many forces tugging on the concept from what appear to be competing directions. _____ 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.

std::hex assumes some state in the stream for storing that hexadecimal formatting ... but it still requires the cooperation of the manipulators to set the state and the formatting logic invoked later.
Yes, and my expectation was/is that convert() (as lexical_cast) piggy-backs on that std::stream functionality. And I think we've achieved very decent results in that direction that many people would prefer to raw std::streams and lexical_cast.
That same, save state and use it later when converting is needed for something like convert.
Discarding std::streams for formatting and building something similar from scratch based on Boost.Parameter is quite a task. I am not well qualified to take on such a task. Especially in a short timeframe. I am not even sure there is truly pressing need for that. I could look into it without hurry next if convert() gets through the review.
I'd still like to see a coherent set of requirements. There are too many forces tugging on the concept from what appear to be competing directions.
I personally feel we've achieved all our immediate objections that I felt were important to people -- 1. the default value, 2. no DefaultConstructibility, 3. throw/no-throw behavior, 4. simple/advanced failure checks, 5. formatting (even though only "uninteresting" std::stream-based), 6. locale. In fact, I do not feel there are many forces pulling in different directions. The issues remaining are 1. if the existing interface could be improved and 2. one major issue of deploying Boost.Parameter. As for #1 there always room for improvement. Having said that I feel something very laconic and unambiguous has come out of the discussion (even though it has left no stoned unturned of my original "vision"). As for #2, it could be looked at (and I am planning to) in due course. V.

On Tuesday, February 24, 2009 4:26 AM Vladimir Batov wrote:
Rob Stewart wrote:
(Please get in the habit of attributing your quotations. It makes things easier for the rest of us to follow.)
std::hex assumes some state in the stream for storing that hexadecimal formatting ... but it still requires the cooperation of the manipulators to set the state and the formatting logic invoked later.
Yes, and my expectation was/is that convert() (as lexical_cast) piggy-backs on that std::stream functionality. And I think we've achieved very decent results in that direction that many people would prefer to raw std::streams and lexical_cast.
I hadn't seen any commitment to doing so, but I favor it. That offers the best reuse of functionality and knowledge.
Discarding std::streams for formatting and building something similar from scratch based on Boost.Parameter is quite a task. I am not well qualified to take on such a task. Especially in a short timeframe. I am
I'm not sure why you're in such a rush.
not even sure there is truly pressing need for that. I could look into it
I'm inclined to think there isn't, but I'm not sure what Andrey had in mind.
without hurry next if convert() gets through the review.
I know you already requested a review, but that seemed woefully premature.
I'd still like to see a coherent set of requirements. There are too many forces tugging on the concept from what appear to be competing directions.
I personally feel we've achieved all our immediate objections that I felt were important to people --
1. the default value, 2. no DefaultConstructibility, 3. throw/no-throw behavior, 4. simple/advanced failure checks, 5. formatting (even though only "uninteresting" std::stream-based), 6. locale.
This is the closest thing to a set of requirements I've seen. What about the concern I raised early on about implicit conversions? The ideal interface has the compiler deducing both the source and destination types to avoid implicit conversions of the source object to the source parameter and the conversion result to the destination object. 4) and 5) are incompletely specified. What are the use cases? What sort of formatting control is needed? Does the current interface support them? Without use cases specifying the desired usage pattern, it's hard to know whether the current interface is sufficient. (The use cases, of course, lead to test cases.) Was there a link to documentation on these requirements that I missed?
In fact, I do not feel there are many forces pulling in different directions. The issues remaining are
1. if the existing interface could be improved and
What about Emil Dotchevski's desire for from_string() and to_string()? Have those been dropped entirely? I think it was Emil's intention that those functions be concerned with providing optimized conversions to/from string with only limited formatting support, but they required type-specific overloads. I'm not sure those were to be, or could be, based upon IOStreams. (I'm not sure the two can be reconciled, but I didn't want Emil's concerns to be dropped altogether.)
2. one major issue of deploying Boost.Parameter.
As for #1 there always room for improvement. Having said that I feel something very laconic and unambiguous has come out of the
FYI, "laconic" is rarely used in modern American English. (I can't speak for other variants.) We use "concise" instead.
discussion (even though it has left no stoned unturned of my original "vision").
That is typically the case with Boost! _____ 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>
Yes, and my expectation was/is that convert() (as lexical_cast) piggy-backs on that std::stream functionality.
I hadn't seen any commitment to doing so, but I favor it. That offers the best reuse of functionality and knowledge.
Thank you. The commitment was implicit (due to IOStream-based implementation, lexical_cast origins, manipulators) and I am still planning to stick to it as it gets me where I'd like to be the quickest. Still, hearing that I am not all alone with the view certainly helps. Thanks.
I know you already requested a review, but that seemed woefully premature.
Yes, I see your point... and looking back I think I have to agree. :-) In my defence I might say that functionality-wise not that much changed from the first "Formal Review Request" as it provided the list below: 1. the default value, 2. no DefaultConstructibility, 3. throw/no-throw behavior, 4. simple/advanced failure checks, 5. formatting (even though only "uninteresting" std::stream-based), 6. locale. Surely, the 'string' namespace and some stuff are gone, implementation looks nothing as before. Although I'd expect "adjustments" like that to be an expected result of a review. Granted, my first "Review Request" posting was not that much of a review (as Scott correctly noted) as I wanted to request/review/flash out what people actually needed from convert. Still, your point taken. Do you think we are any closer to "formal formal review request"?
This is the closest thing to a set of requirements I've seen.
I think the list is pretty much as I listed it initially when requested the "Formal Review Request". I could be wrong though.
What about the concern I raised early on about implicit conversions? The ideal interface has the compiler deducing both the source and destination types to avoid implicit conversions of the source object to the source parameter and the conversion result to the destination object.
Unless I misunderstand, don't we achieve that with template<class TypeIn, class TypeOut> TypeOut convert_from(TypeIn const&, class TypeOut const&) as it is templated there is no implicit conversion. Still, when we actually want one we can do double d = convert_from<string, double>(str, -1/*implicitly converted to double*/)
4) and 5) are incompletely specified. What are the use cases? What sort of formatting control is needed? Does the current interface support them? Without use cases specifying the desired usage pattern, it's hard to know whether the current interface is sufficient.
As for #4 I tried covering that in the doc. and in a nutshell it is as follows: int i = convert_from(str, -1); if (i == -1) // simple check convert<string, int> i = convert_from(str, -1); if (i.good()) // "advanced" check I cannot see anything beyond that and unless I missed that there was no much debate around it (or I was too busy defending my "string" namespace :-) ). As for #5 then, it is as much or as little as std::stream provides. That, in fact, covers all my formatting need and I think could be a good basis. I am very reluctant getting more adventurous as I suspect the process will drag for long time and will fizzle out with no result in the end.
Was there a link to documentation on these requirements that I missed?
There is a hastily written "document" together with the implementation in the Vault (the file called boost-string-convert.zip).
What about Emil Dotchevski's desire for from_string() and to_string()? Have those been dropped entirely?
Indeed, Emil was very vocal about those to/from_string. I followed the discussion and believe you provided very weighty arguments in favor of a more generic scope. That's the direction I took.
I think it was Emil's intention that those functions be concerned with providing optimized conversions to/from string with only limited formatting support, ... I didn't want Emil's concerns to be dropped altogether.)
I do not think Emil's concerns have been dropped. Yes, there is probably no support for his specific needs in the current IOstream-based implementations. However, the "framework"-style approach supports specializations. I.e. Emil could write his own specific conversion (to/from any type) and have it deployed via the "framework" interface. That's what I've been doing with lexical_cast -- specializing it for my types to allow handling them via common (lexical_cast) interface.
FYI, "laconic" is rarely used in modern American English. (I can't speak for other variants.) We use "concise" instead.
Thanks. Appreciated (truly). Will avoid it then. It was not exactly a success with the Australians I know. :-)
discussion (even though it has left no stoned unturned of my original "vision").
That is typically the case with Boost!
I am glad I went through that (well, not through yet :-) ). And many thanks for taking me for the ride -- a ride worth taking on a few levels. V.

On Wednesday, February 25, 2009 2:29 AM Vladimir Batov wrote:
From: "Stewart, Robert" <Robert.Stewart@sig.com>
Surely, the 'string' namespace and some stuff are gone, implementation looks nothing as before. Although I'd expect "adjustments" like that to be an expected result of a review. Granted, my first "Review
The review should not be the means to find out what the finished library should be. Ideally, you want nothing but praise and affirmation at that time. There will usually be some late to the party who will find something new to disturb what had been thought so well in hand, but you shouldn't come to the review expecting to get lots of good ideas to completely reshape the library.
Request" posting was not that much of a review (as Scott correctly noted) as I wanted to request/review/flash out what people actually needed from convert. Still, your point taken. Do you think we are any closer to "formal formal review request"?
I don't think there's any reason to pursue a review until things begin to quiesce which doesn't appear likely any time soon, if the current discussions are any indication. I appreciate your excitement, but it would be far better to have a well conceived, broadly acceptable idea before heading for review. Otherwise, you're quite likely to find your library rejected with comments like you're seeing now.
What about the concern I raised early on about implicit conversions? The ideal interface has the compiler deducing both the source and destination types to avoid implicit conversions of the source object to the source parameter and the conversion result to the destination object.
Unless I misunderstand, don't we achieve that with
template<class TypeIn, class TypeOut> TypeOut convert_from(TypeIn const&, class TypeOut const&)
The output value can still be implicitly converted to the callers destination object. To avoid input and output conversions, you need the likes of this: template <class Out, class In> void convert(In const &, Out &); // or convert(Out &, In const &) Without that, the following might surprise someone: struct foo { foo(int); }; foo f(convert<double>("1234.5"));
double d = convert_from<string, double>(str, -1/*implicitly converted to double*/)
That is always an issue, but the possibility of implicit conversion is clear when the caller identifies the exact specialization to use.
4) and 5) are incompletely specified. What are the use cases? What sort of formatting control is needed? Does the current interface support them? Without use cases specifying the desired usage pattern, it's hard to know whether the current interface is sufficient.
As for #4 I tried covering that in the doc. and in a nutshell it is as follows:
int i = convert_from(str, -1); if (i == -1) // simple check
convert<string, int> i = convert_from(str, -1); if (i.good()) // "advanced" check
I had no idea that testing for the default value or querying a success flag was what you meant by simple and advanced "failure checks." I think there should be two or three distinct signatures for using the conversion facilities: 1. A call that throws on failure. 2. A call that takes a default and always succeeds. 3. A call that returns false on failure. The third one is questionable. Certainly the first could be built atop the third, but the second provides a means to approximate the third (assuming there's a default that can always be considered a failure value). With that set of signatures, there's no implied need for a special return type that converts implicitly to the destination type and can be queried for success, or a throw/nothrow flag. There may yet be a special return type to account for specifying the default value as I suggested elsewhere, or for some other peculiarity, but it isn't a given. So, the questions are, whether all three signatures are needed and how they should differ. I suggested a way that the first and second can be handled in the "[convert] Default Syntax Idea" thread, of course. If we support the third, how should it look and work? (I'll pursue that separately.)
As for #5 then, it is as much or as little as std::stream provides. That, in fact, covers all my formatting need and I think could be a good basis. I am very reluctant getting more adventurous as I suspect the process will drag for long time and will fizzle out with no result in the end.
I had no idea that you were referring to the formatting facilities inherent in the underlying IOStreams implementation. Still, there must be a large number of use cases of various kinds of UDT and builtin conversions, complete with a variety of formatting tasks, to determine if the interface will cope well in this context.
What about Emil Dotchevski's desire for from_string() and to_string()? Have those been dropped entirely?
Indeed, Emil was very vocal about those to/from_string. I followed the discussion and believe you provided very weighty arguments in favor of a more generic scope. That's the direction I took.
That's fine, but I wasn't sure you had made a conscious choice to pursue the generic approach at the expense of Emil's arguments. On balance, I like the generic approach because it isn't string focused, it builds upon the IOStreams facilities, and it is extensible. However, there remains merit in what Emil wanted because of the optimizations possible. I think his functions can still be used under the facade, so he might yet be happy with that part.
I think it was Emil's intention that those functions be concerned with providing optimized conversions to/from string with only limited formatting support, ... I didn't want Emil's concerns to be dropped altogether.)
I do not think Emil's concerns have been dropped. Yes, there is probably no support for his specific needs in the current IOstream-based implementations. However, the "framework"-style approach supports specializations. I.e. Emil could write his own specific conversion (to/from any type) and have it deployed via the "framework" interface. That's what I've been doing with lexical_cast -- specializing it for my types to allow handling them via common (lexical_cast) interface.
I'm not sure whether the two can cooperate because one supports formatting the other isn't likely to support. _____ Rob Stewart robert.stewart@sig.com Software Engineer, Core Software using std::disclaimer; Susquehanna International Group, LLP http://www.sig.com IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

From: "Stewart, Robert" <Robert.Stewart@sig.com> ... I don't think there's any reason to pursue a review until things begin to quiesce which doesn't appear likely any time soon, if the current discussions are any indication.
Well, that's IMHO too bad. I obviously cannot carry on forever and I do not see a consensus getting any closer. The crux of the matter (in my view) is that the convert() functionality is available and IMHO way better than lexical_cast has to offer. Rejecting it because Spirit can do formatting better or because convert() does not use Boost.Parameter would be shortsighted IMHO as in the end people will be still left with nothing -- a huge hole between lexical_cast and Spirit. Is it realistic/practical approach? I do not think so. When/If something better comes up, this functionality will die out by itself.
I appreciate your excitement, but it would be far better to have a well conceived, broadly acceptable idea before heading for review. Otherwise, you're quite likely to find your library rejected with comments like you're seeing now.
Again, I am not convinced that what I have on offer is badly conceived. Surely, there's been quite a discussion about names and the 'string' namespace. However, I hope that well-conceived means quite a bit more than just names... which we'll never come to an agreement about. So, if it is really the case that the naming (rather than functionality) will be the deciding factor for an acceptance or a rejection, maybe it is better to drop the whole thing altogether.
What about the concern I raised early on about implicit conversions? The output value can still be implicitly converted to the callers destination object. To avoid input and output conversions, you need the likes of this:
template <class Out, class In> void convert(In const &, Out &); // or convert(Out &, In const &)
Aha, now I understand. Passing a parameter by a non-const reference. Yes, as above we indeed avoid the implicit conversions. That's nice. Never occured to me. I admit I am allergic :-) to returning results this Pascal-style. Never grew on me. Sorry. Just not my cup of tea. Will probably do *anything* just not to have it. :-) Truly sorry.
Without that, the following might surprise someone:
foo f(convert<double>("1234.5"));
Yes, that's typical of C/C++. Although I admit that after reviewing/looking at so much of somebody else's code it has never been my major concern. And isn't a compiler warns us of things like that?
I had no idea that testing for the default value or querying a success flag was what you meant by simple and advanced "failure checks."
Wel, calling it "advanced" is probably a bit rich. How about simple and "better" failure checks. They *are* failure checks, aren't they?
I think there should be two or three distinct signatures for using the conversion facilities:
1. A call that throws on failure. 2. A call that takes a default and always succeeds. 3. A call that returns false on failure.
As I indicated, I am not convinced there is such a clear cut as I might be forced to provide a default (due to no def.ctor) but still want to throw. My thinking behind convet_to, convert_from is that the latter *is* the interface. I'd be happy to get rid of #1 (which is convert_to) but I think it'd better be there for better transition from lexical_cast. As you indicated in your very first reply to this thread (in relation to string::is()) #3 will have to actually try the conversion. If I remember correctly you did not like it much (or did you object to the name only?).
... With that set of signatures, there's no implied need for a special return type that converts implicitly to the destination type and can be queried for success, or a throw/nothrow flag. There may yet be a special return type to account for specifying the default value as I suggested elsewhere, or for some other peculiarity, but it isn't a given.
Again, my real example: direction dir = convert_from(str, direction::up); It takes the default because it has to (no def.ctor). It does not mean I do not want to throw on failure... In real life, I don't. However, then I need a means to distinguish the failure. With convert::result<direction> dir = convert_from(str, direction::up); if (dir.good()) I can achieve that without resorting to your #3 to actually try the conversion twice.
I had no idea that you were referring to the formatting facilities inherent in the underlying IOStreams implementation. Still, there must be a large number of use cases of various kinds of UDT and builtin conversions, complete with a variety of formatting tasks, to determine if the interface will cope well in this context.
I feel there is too much emphasise on formatting. I do not believe convert() is the tool for it. For good formatting we need good formatting tools (Spirit?). However, for basic formatting IOStream-based approach works for me -- I do not need to bring a cannon (Spirit) to shoot a few little birds (basic formatting). V.

[I found this reply in my "drafts" folder. I must have been forced to log off or reboot and forgot about it before finishing it and sending it. Here's hoping there's still value in the content, if only to reawaken thoughts on the proposed library.] On Thursday, February 26, 2009 1:35 AM Vladimir Batov wrote:
From: "Stewart, Robert" <Robert.Stewart@sig.com> ... I don't think there's any reason to pursue a review until things begin to quiesce which doesn't appear likely any time soon, if the current discussions are any indication.
Well, that's IMHO too bad. I obviously cannot carry on forever and I do not
I wasn't suggesting anything so extreme as carrying on forever.
see a consensus getting any closer. The crux of the matter (in my view) is that the convert() functionality is available and IMHO way better than lexical_cast has to offer. Rejecting it because Spirit can do formatting better or because convert() does not use Boost.Parameter would be shortsighted IMHO as in the end people will be still left with nothing -- a huge hole between lexical_cast and Spirit. Is it realistic/practical approach? I do not think so. When/If something better comes up, this functionality will die out by itself.
It is decidedly not better to have _anything_ because _something_ is needed. There have been many ideas, some better than others (and I don't mean mine were better and the rest weren't!), that have shaped the current notions. There may yet be a brilliant idea that will reconcile things even better than now or a simplification that better focuses the role of your proposal.
I appreciate your excitement, but it would be far better to have a well conceived, broadly acceptable idea before heading for review. Otherwise, you're quite likely to find your library rejected with comments like you're seeing now.
Again, I am not convinced that what I have on offer is badly conceived.
I'm sorry that my phrasing led you to think that, but I didn't mean to suggest that what you have now is badly conceived. I meant that we may be shortsighted or overreaching now and that some compelling alternative or argument may yet be raised that makes what we now have pale by comparison.
Surely, there's been quite a discussion about names and the 'string' namespace. However, I hope that well-conceived means quite a bit more than just names... which we'll never come to an agreement about.
There has been a lot of discussion and about more than just names. It has all been useful as the arguments and suggested alternatives raised thus far have either altered what was or have permitted a better defense of what is.
So, if it is really the case that the naming (rather than functionality) will be the deciding factor for an acceptance or a rejection, maybe it is better to drop the whole thing altogether.
This is too precipitous. My suggestion was to have some patience. Let ideas settle. See if others have more thoughts. Produce use cases and see how well the various interfaces work and show the results on the list. Those harboring doubts may warm to the idea or finally conceive what they'd rather have and win the rest.
What about the concern I raised early on about implicit conversions? The output value can still be implicitly converted to the caller's destination object. To avoid input and output conversions, you need the likes of this:
template <class Out, class In> void convert(In const &, Out &); // or convert(Out &, In const &)
Aha, now I understand. Passing a parameter by a non-const reference. Yes, as above we indeed avoid the implicit conversions. That's nice. Never occured to me. I admit I am allergic :-) to returning results this Pascal-style. Never grew on me. Sorry. Just not my cup of tea. Will probably do
*anything* just not to have it. :-) Truly sorry.
That interface provides support for types without a default constructor and increases safety. It can, of course, be built atop the other interfaces, so there's no reason the library couldn't supply it as a safer alternative for those not allergic to output parameters.
Without that, the following might surprise someone:
foo f(convert<double>("1234.5"));
Yes, that's typical of C/C++. Although I admit that after reviewing/looking at so much of somebody else's code it has never been my major concern. And isn't a compiler warns us of things like that?
That's a legitimate use of the converting constructor so the conversion will be silent. That's the danger of such constructors generally, so it isn't just convert's problem, but altering or extending convert's interface can overcome it readily.
I had no idea that testing for the default value or querying a success flag was what you meant by simple and advanced "failure checks."
Wel, calling it "advanced" is probably a bit rich. How about simple and "better" failure checks. They *are* failure checks, aren't they?
Those adjectives don't help. Just note the use cases as testing for the default or for a success flag.
I think there should be two or three distinct signatures for using the conversion facilities:
1. A call that throws on failure. 2. A call that takes a default and always succeeds. 3. A call that returns false on failure.
As I indicated, I am not convinced there is such a clear cut as I might be forced to provide a default (due to no def.ctor) but still want to throw. My thinking behind convet_to, convert_from is that the latter *is* the interface. I'd be happy to get rid of #1 (which is convert_to) but I think it'd better be there for better transition from lexical_cast.
I'm lost on the application of "convert_to" and "convert_from" to my list.
As you indicated in your very first reply to this thread (in relation to string::is()) #3 will have to actually try the conversion. If I remember correctly you did not like it much (or did you object to the name only?).
I'm not sure what reply you're referring to nor what the discussion was about. However, in order to return false on a failure to convert, of course the conversion must be tried. What am I missing?
With that set of signatures, there's no implied need for a special return type that converts implicitly to the destination type and can be queried for success, or a throw/nothrow flag. There may yet be a special return type to account for specifying the default value as I suggested elsewhere, or for some other peculiarity, but it isn't a given.
Again, my real example:
direction dir = convert_from(str, direction::up);
It takes the default because it has to (no def.ctor). It does not mean I do not want to throw on failure... In real life, I don't. However, then I need a means to distinguish the failure. With
convert::result<direction> dir = convert_from(str, direction::up); if (dir.good())
I can achieve that without resorting to your #3 to actually try the conversion twice.
Why twice?
I had no idea that you were referring to the formatting facilities inherent in the underlying IOStreams implementation. Still, there must be a large number of use cases of various kinds of UDT and builtin conversions, complete with a variety of formatting tasks, to determine if the interface will cope well in this context.
I feel there is too much emphasise on formatting. I do not believe convert() is the tool for it. For good formatting we need good formatting tools (Spirit?). However, for basic formatting IOStream-based approach works for me -- I do not need to bring a cannon (Spirit) to shoot a few little birds (basic formatting).
That's a legitimate point, but then is the facility really to handle conversions among multiple types or, as Emil Dotchevski asserted, is it really just for conversion to and from string? If the latter, then another interface is probably better since the current interface implies arbitrary inter-type conversions. _____ 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.

I looked a Boost.Parameter and will certainly try using it somewhere. My hesitation with it for boost::convert is I am under impression that the set of acceptable parameters (radix_, etc.) has to be predefined and therefore cannot be extended by the user when formatting like "convert(str) >> std::hex" seems more flexible.
You can add new keywords as needed.
Where can I add those "new keywords" to? If it is into convert(), then I do not feel it is appropriate as for every user "needed" will be different and I cannot see convert() collecting *all* those "needed" keywords from every user on the globe.
The point is that the underlying conversion mechanism can support a subset of these keywords, specific to the conversion domain. This way it will be a non-intrusive extension.
I cannot understand how it can be a "non-intrusive extension" if every new keyword needs to be stored/registered *inside* convert<string, int>... unless I misunderstand something. With manipulators (however clumsy they are) convert<string, int> knows nothing about std::hex. Still, when std::hex is plugged in, we get 0xFF. How do you do that with "radix_"? The keyword has to be registeded with convert<string, int> and then convert<> needs to apply that somehow. Secondly, I do not feel there is much conceptual difference between convert<string, int>(str, radix_ = 16) and convert<string, int>(str).radix(16) // 16 passed in differently or convert<string, int>(str) >> radix(16) // different op>> or convert<string, int>(str) >> std::hex The difference is that in #1 convert() has to handle "radix_" itself and in #4 convert() knows nothing about std::hex. Am I missing something? RE: algorithms I've extended convert to be used with algorithms as std::vector<string> strings; std::vector<int> integers; ... std::transform( strings.begin(), strings.end(), std::back_inserter(integers), convert<string, int>(string(), -1)); No formatting. I feel it is the best I can do (well, so far).
convert<string, short >(0xFFFFFFFF, throw_on_out_of_range_ = true)?
Looks certainly tempting. However, I do not believe it is achievable. For that to happen every parameter (like throw_on_out_of_range_) needs to be integrated into, say, convert<string, int> and then its value stored to be re-applied for every entry during traversal. Far too expensive. convert<string, type> has already grown beyond anything I ever imagined. I might well not understand what you are advocating. Can you put together some kind of proof-of-concept code for me to better understand how you want to do that. V.

on Sun Feb 22 2009, "Vladimir Batov" <batov-AT-people.net.au> wrote:
I looked a Boost.Parameter and will certainly try using it somewhere. My hesitation with it for boost::convert is I am under impression that the set of acceptable parameters (radix_, etc.) has to be predefined and therefore cannot be extended by the user when formatting like "convert(str) >> std::hex" seems more flexible.
You can add new keywords as needed.
Where can I add those "new keywords" to? If it is into convert(), then I do not feel it is appropriate as for every user "needed" will be different and I cannot see convert() collecting *all* those "needed" keywords from every user on the globe.
The keywords are decoupled.
The point is that the underlying conversion mechanism can support a subset of these keywords, specific to the conversion domain. This way it will be a non-intrusive extension.
I cannot understand how it can be a "non-intrusive extension" if every new keyword needs to be stored/registered *inside* convert<string, int>...
Did you read through the Boost.Parameter tutorial? -- Dave Abrahams BoostPro Computing http://www.boostpro.com

Sorry for the late reply, it's been a busy weekend. I'm glad to see that others answered some of your questions. Vladimir Batov wrote:
You can add new keywords as needed.
Where can I add those "new keywords" to? If it is into convert(), then I do not feel it is appropriate as for every user "needed" will be different and I cannot see convert() collecting *all* those "needed" keywords from every user on the globe.
The point is that the underlying conversion mechanism can support a subset of these keywords, specific to the conversion domain. This way it will be a non-intrusive extension.
I cannot understand how it can be a "non-intrusive extension" if every new keyword needs to be stored/registered *inside* convert<string, int>... unless I misunderstand something. With manipulators (however clumsy they are) convert<string, int> knows nothing about std::hex. Still, when std::hex is plugged in, we get 0xFF. How do you do that with "radix_"? The keyword has to be registeded with convert<string, int> and then convert<> needs to apply that somehow.
My initial post wasn't specifically about string-related conversions, however, the main plot is still applicable. Assume we want to make a numeric conversion, but we also want to detect truncation and treat it as an error: int i = 0xFFFFFFFF; short j = convert< short >(i, throw_on_overflow_ = true); Now, that kind of conversion could be handled by Boost.NumericConversion, something like this (pseudocode): BOOST_PARAMETER_KEYWORD(throw_on_overflow_) template< typename ToT, typename ArgsT > ToT convert_impl(ArgsT const& args) { bool throw_on_overflow = args[throw_on_overflow_ | false]; return numeric_cast< ToT >(args[source_], throw_on_overflow); } You can see that the convert_impl accepts a single arguments pack that accommodates all named arguments passed to the "convert" function, including the source value i, which has the implicit name "source_". You can also see that the "throw_on_overflow_" keyword belongs to the Boost.NumericConversion library, and Boost.Convert does not need to know anything about it. As for the "string<->numeric" conversions, the approach is the same with one exception: such conversions can be made by Boost.Convert itself. This means that Boost.Convert may introduce its own keywords to customize the conversion (such as radix_, bool_alpha_, etc.) One dark corner that needs to be settled in this scheme is to figure out how Boost.Convert will decide which convert_impl should be called. This needs to be thought about, however, I think, using a number of traits on the source and target types, yielding some tag type to dispatch between convert_impl functions would suffice.
Secondly, I do not feel there is much conceptual difference between
convert<string, int>(str, radix_ = 16) and convert<string, int>(str).radix(16) // 16 passed in differently or convert<string, int>(str) >> radix(16) // different op>> or convert<string, int>(str) >> std::hex
The difference is that in #1 convert() has to handle "radix_" itself and in #4 convert() knows nothing about std::hex. Am I missing something?
Like I said, using manipulators is sufficient for string-related conversions, but is pretty meaningless for other types of conversion. You could implement your own manipulator-like modifiers even for other conversions, but it would look unnatural and more complex than using keywords. For example, here's how wstring->string conversion could look like: string s = convert< string >(ws) >> replace_non_convertible_with('?'); or: string s = convert< string >(ws, replace_non_convertible_with_ = '?'); Personally, the latter looks more appealing to me.
RE: algorithms
I've extended convert to be used with algorithms as
std::vector<string> strings; std::vector<int> integers; ... std::transform( strings.begin(), strings.end(), std::back_inserter(integers), convert<string, int>(string(), -1));
No formatting. I feel it is the best I can do (well, so far).
Do you intentionally use the "convert" verb for the functor name? Traditionally, functors and other classes use nouns in their names, like "converter".

Vladimir Batov wrote:
Can you put together some kind of proof-of-concept code for me to better understand how you want to do that.
I have put together a rough example of what I have in mind. Please, find the code attached. It works on my GCC 4.3. It converts integral types to strings and back, it also supports the "converter" functor that can be used with algorithms. Both of them support a couple of named parameters for customization. The implementation also has a fundamental support for further extension by adding new conversion tags and specializing the "converter_impl" template on them. In order to inject new conversion tags into the library one has to overload "get_conversion_tag" function. I admit that it doesn't look very pretty, but it's a start, and it's non-intrusive. Maybe someone will suggest something better. PS: I had to extend Boost.Parameter a little, but that's not too much and can be moved to Boost.Parameter if needed.

On Tue, Feb 24, 2009 at 1:32 PM, Andrey Semashev <andrey.semashev@gmail.com> wrote:
I have put together a rough example of what I have in mind. Please, find the code attached. It works on my GCC 4.3.
Andrey, a question: let's say I am the author of a uuid type, something like: struct uuid { unsigned char value_[16]; }; How do I integrate that type in the proposed convert framework, so it can be converted to std::string? I have a basic understanding, but I wanted to see a clear picture of only the things relevant to defining a conversion (as opposed to using it.) Emil Dotchevski Reverge Studios, Inc. http://www.revergestudios.com/reblog/index.php?n=ReCode

Emil Dotchevski wrote:
Andrey, a question: let's say I am the author of a uuid type, something like:
struct uuid { unsigned char value_[16]; };
How do I integrate that type in the proposed convert framework, so it can be converted to std::string? I have a basic understanding, but I wanted to see a clear picture of only the things relevant to defining a conversion (as opposed to using it.)
With the code snippet I attached it should look something like this: struct uuid_to_string_tag {}; template< typename ToT > typename enable_if< is_string< ToT >, uuid_to_string_tag
::type get_conversion_tag(uuid*, ToT*);
template< > struct converter_impl< uuid, uuid_to_string_tag > { typedef uuid result_type; template< typename ArgsT > result_type operator() (ArgsT const& args) const; }; The operator() will contain the conversion code.

On Tue, Feb 24, 2009 at 2:09 PM, Andrey Semashev <andrey.semashev@gmail.com> wrote:
Emil Dotchevski wrote:
Andrey, a question: let's say I am the author of a uuid type, something like:
struct uuid { unsigned char value_[16]; }; <snipped>
With the code snippet I attached it should look something like this: <snipped>
I'm trying to get a working program to see what it's going to look like. I got your code and added what I think would be necessary to implement conversion for a type user::uuid (just as an example.) So far I have this: http://codepad.org/zBOc3s25, but it has compile errors. I'm sure with some patience I could get it to work but I'm hoping this will be easier for you to finish (you can edit your/mine code directly on that page and hit submit to compile/run, I find this rather convenient.) Thanks, Emil Dotchevski Reverge Studios, Inc. http://www.revergestudios.com/reblog/index.php?n=ReCode

Emil Dotchevski wrote:
Andrey, a question: let's say I am the author of a uuid type, something like:
struct uuid { unsigned char value_[16]; }; <snipped> With the code snippet I attached it should look something like this: <snipped>
I'm trying to get a working program to see what it's going to look like. I got your code and added what I think would be necessary to implement conversion for a type user::uuid (just as an example.)
So far I have this: http://codepad.org/zBOc3s25, but it has compile errors. I'm sure with some patience I could get it to work but I'm hoping this will be easier for you to finish (you can edit your/mine code directly on that page and hit submit to compile/run, I find this rather convenient.)
The get_conversion_tag function is not found via ADL. Also, your compiler seems to have troubles with boost::array initialization with the initializers list. I've posted the fixed code. Aside from the aforementioned fixes I also optimized away Boost.Bind and Boost.Function usage.

On Wed, Feb 25, 2009 at 11:38 AM, Andrey Semashev <andrey.semashev@gmail.com> wrote:
Emil Dotchevski wrote:
Andrey, a question: let's say I am the author of a uuid type, something like:
struct uuid { unsigned char value_[16]; }; <snipped>
With the code snippet I attached it should look something like this: <snipped>
I'm trying to get a working program to see what it's going to look like. I got your code and added what I think would be necessary to implement conversion for a type user::uuid (just as an example.)
So far I have this: http://codepad.org/zBOc3s25, but it has compile errors. I'm sure with some patience I could get it to work but I'm hoping this will be easier for you to finish (you can edit your/mine code directly on that page and hit submit to compile/run, I find this rather convenient.) I've posted the fixed code. Aside from the aforementioned fixes I also optimized away Boost.Bind and Boost.Function usage.
Posted - where? Emil Dotchevski Reverge Studios, Inc. http://www.revergestudios.com/reblog/index.php?n=ReCode

Emil Dotchevski wrote:
On Wed, Feb 25, 2009 at 11:38 AM, Andrey Semashev <andrey.semashev@gmail.com> wrote:
Emil Dotchevski wrote:
Andrey, a question: let's say I am the author of a uuid type, something like:
struct uuid { unsigned char value_[16]; }; <snipped> With the code snippet I attached it should look something like this: <snipped> I'm trying to get a working program to see what it's going to look like. I got your code and added what I think would be necessary to implement conversion for a type user::uuid (just as an example.)
So far I have this: http://codepad.org/zBOc3s25, but it has compile errors. I'm sure with some patience I could get it to work but I'm hoping this will be easier for you to finish (you can edit your/mine code directly on that page and hit submit to compile/run, I find this rather convenient.) I've posted the fixed code. Aside from the aforementioned fixes I also optimized away Boost.Bind and Boost.Function usage.
Posted - where?
http://codepad.org/WJNCaicE Sorry, I've never dealt before with this way of software development. :)

On Wed, Feb 25, 2009 at 12:00 PM, Andrey Semashev <andrey.semashev@gmail.com> wrote:
Emil Dotchevski wrote:
Andrey, a question: let's say I am the author of a uuid type, something like:
struct uuid { unsigned char value_[16]; }; <snipped>
I've posted the fixed code. Aside from the aforementioned fixes I also optimized away Boost.Bind and Boost.Function usage. Posted - where? http://codepad.org/WJNCaicE
Sorry, I've never dealt before with this way of software development. :)
Me neither but in my experience codepad.org is easier to deal with for small snippets like this, compared to having to compile and link on my own system. :) Anyway, another question: is there a way to make operator() not be a template? Emil Dotchevski Reverge Studios, Inc. http://www.revergestudios.com/reblog/index.php?n=ReCode

Emil Dotchevski wrote:
Anyway, another question: is there a way to make operator() not be a template?
It should be doable if you only need to receive the "source" argument to convert from. But if you want other arguments as well, I can't figure out how to do it without a template, unless to resort to type erasure. If you're trying to extract the conversion code into a separate TU, then you can leave the operator() a template, so it is able to transform the named arguments pack into a call of a non-template function with positional arguments only.

On Thu, Feb 26, 2009 at 8:18 AM, Andrey Semashev <andrey.semashev@gmail.com> wrote:
Emil Dotchevski wrote:
Anyway, another question: is there a way to make operator() not be a template? It should be doable if you only need to receive the "source" argument to convert from. But if you want other arguments as well, I can't figure out how to do it without a template, unless to resort to type erasure.
Well, one way to take arguments is to tell the compiler that your function or operator() takes arguments, right? std::string operator()( uuid const & value, int arg1, float arg2 ); C++ was type-safe even before templates were introduced. :)
If you're trying to extract the conversion code into a separate TU, then you can leave the operator() a template, so it is able to transform the named arguments pack into a call of a non-template function with positional arguments only.
I'm not trying to extract the conversion code into a separate TU, I'm trying to justify putting it in the header. :) If I'm the maintainer of class uuid and I am asked to provide a to-string conversion, you'll find it very difficult to convince me that it makes sense to replace this: #include <string> namespace user { class uuid; std::string to_string( uuid const & ); } with all of this: #include <boost/mpl/and.hpp> #include <boost/mpl/bool.hpp> #include <boost/typeof/typeof.hpp> #include <boost/utility/enable_if.hpp> #include <boost/type_traits/is_integral.hpp> #include <boost/parameter/keyword.hpp> #include <kitchen_sink.hpp> namespace user { struct uuid { unsigned char value_[16]; }; struct uuid_to_string_tag {}; template< typename ToT > typename boost::enable_if< boost::conversion::is_string< ToT >, uuid_to_string_tag
::type get_conversion_tag(uuid*, ToT*);
} // namespace user namespace boost { namespace conversion { template< typename ToT > struct converter_impl< ToT, user::uuid_to_string_tag > { typedef ToT result_type; template< typename ArgsT > ToT operator() (ArgsT const& args) const { .... } }; } // namespace conversion } // namespace boost Emil Dotchevski Reverge Studios, Inc. http://www.revergestudios.com/reblog/index.php?n=ReCode

On Thursday, February 26, 2009 3:03 PM Emil Dotchevski wrote:
If I'm the maintainer of class uuid and I am asked to provide a to-string conversion, you'll find it very difficult to convince me that it makes sense to replace this:
#include <string>
namespace user { class uuid; std::string to_string( uuid const & ); }
with all of this:
#include <boost/mpl/and.hpp> #include <boost/mpl/bool.hpp> #include <boost/typeof/typeof.hpp> #include <boost/utility/enable_if.hpp> #include <boost/type_traits/is_integral.hpp> #include <boost/parameter/keyword.hpp>
#include <kitchen_sink.hpp>
namespace user {
struct uuid { unsigned char value_[16]; };
struct uuid_to_string_tag {};
template< typename ToT > typename boost::enable_if< boost::conversion::is_string< ToT >, uuid_to_string_tag
::type get_conversion_tag(uuid*, ToT*);
} // namespace user
namespace boost {
namespace conversion {
template< typename ToT > struct converter_impl< ToT, user::uuid_to_string_tag > { typedef ToT result_type;
template< typename ArgsT > ToT operator() (ArgsT const& args) const { .... } };
} // namespace conversion
} // namespace boost
That's a bit unfair, don't you think? I know that you only wish to support std::string conversions, but the more complex machinery shown in the latter variant supports open ended formatting options and various target types. _____ 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, Feb 26, 2009 at 12:07 PM, Stewart, Robert <Robert.Stewart@sig.com> wrote:
On Thursday, February 26, 2009 3:03 PM Emil Dotchevski wrote:
If I'm the maintainer of class uuid and I am asked to provide a to-string conversion, you'll find it very difficult to convince me that it makes sense to replace this:
#include <string>
namespace user { class uuid; std::string to_string( uuid const & ); }
with all of this:
#include <boost/mpl/and.hpp> #include <boost/mpl/bool.hpp> #include <boost/typeof/typeof.hpp> #include <boost/utility/enable_if.hpp> #include <boost/type_traits/is_integral.hpp> #include <boost/parameter/keyword.hpp>
#include <kitchen_sink.hpp>
namespace user {
struct uuid { unsigned char value_[16]; };
struct uuid_to_string_tag {};
template< typename ToT > typename boost::enable_if< boost::conversion::is_string< ToT >, uuid_to_string_tag
::type get_conversion_tag(uuid*, ToT*);
} // namespace user
namespace boost {
namespace conversion {
template< typename ToT > struct converter_impl< ToT, user::uuid_to_string_tag > { typedef ToT result_type;
template< typename ArgsT > ToT operator() (ArgsT const& args) const { .... } };
} // namespace conversion
} // namespace boost
That's a bit unfair, don't you think? I know that you only wish to support std::string conversions, but the more complex machinery shown in the latter variant supports open ended formatting options and various target types.
I understand that the design direction I would take for the framework is different, but I think you're missing the point of my previous message. Let's assume that converting to all kinds of target types with open-ended formatting options in a single convert<> function is desirable. Within that assumption, we need to provide reasonable support for the case when the conversion of an uuid object to std::string requires no parameters. You can do this by requiring uuid.hpp to include all kinds of headers and template goodness, learn your complex argument forwarding framework -or- you can design your framework in a way that allows it to bind simple function declarations or even member functions and expose them through the generic convert<> interface. I prefer the latter. Do you really find this unfair? Emil Dotchevski Reverge Studios, Inc. http://www.revergestudios.com/reblog/index.php?n=ReCode

On Thursday, February 26, 2009 4:44 PM Emil Dotchevski wrote:
On Thu, Feb 26, 2009 at 12:07 PM, Stewart, Robert <Robert.Stewart@sig.com> wrote:
On Thursday, February 26, 2009 3:03 PM Emil Dotchevski wrote:
If I'm the maintainer of class uuid and I am asked to provide a to-string conversion, you'll find it very difficult to convince me that it makes sense to replace this:
#include <string>
namespace user { class uuid; std::string to_string( uuid const & ); }
with all of this:
[snip long alternative]
That's a bit unfair, don't you think? I know that you only wish to support std::string conversions, but the more complex machinery shown in the latter variant supports open ended formatting options and various target types.
Let's assume that converting to all kinds of target types with open-ended formatting options in a single convert<> function is desirable. Within that assumption, we need to provide reasonable support for the case when the conversion of an uuid object to std::string requires no parameters.
I see your point and I think it is reasonable.
You can do this by requiring uuid.hpp to include all kinds of headers and template goodness, learn your complex argument forwarding framework -or- you can design your framework in a way that allows it to bind simple function declarations or even member functions and expose them through the generic convert<> interface.
The framework uses IOStreams to do its work. I believe the conversion functionality for the uuid type is captured via: namespace user { class uuid; template <class Stream> Stream & operator <<(Stream &, uuid const &); } For interoperability with IOStreams, uuid will probably offer that function anyway, so there's no additional burden.
I prefer the latter. Do you really find this unfair?
I prefer the IOStreams version. If the conversion can support formatting, then it must look in the Stream object's iword/pword facility to find the additional information to guide it. It might also be that there could be support for a specialized convert() overload or function template that takes a Boost.Parameter argument pack to deliver the additional formatting information. If that's the case, the uuid author only does the heavy lifting when the additional formatting is desirable. _____ 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 Fri, Feb 27, 2009 at 5:30 AM, Stewart, Robert <Robert.Stewart@sig.com> wrote:
On Thursday, February 26, 2009 4:44 PM Emil Dotchevski wrote:
You can do this by requiring uuid.hpp to include all kinds of headers and template goodness, learn your complex argument forwarding framework -or- you can design your framework in a way that allows it to bind simple function declarations or even member functions and expose them through the generic convert<> interface.
The framework uses IOStreams to do its work. I believe the conversion functionality for the uuid type is captured via:
namespace user { class uuid;
template <class Stream> Stream & operator <<(Stream &, uuid const &); }
If operator<< was a reasonable to-string alternative, we wouldn't be needing a library for it. It is relevant, of course, and the framework should be able to automatically bind suitable operator<< overloads if they exist. It should also provide a generic operator<< overload that calls convert, so all types that integrate in the convert framework are automatically compatible with streams. Emil Dotchevski Reverge Studios, Inc. http://www.revergestudios.com/reblog/index.php?n=ReCode

On Friday, February 27, 2009 1:48 PM Emil Dotchevski wrote:
On Fri, Feb 27, 2009 at 5:30 AM, Stewart, Robert <Robert.Stewart@sig.com> wrote:
The framework uses IOStreams to do its work. I believe the conversion functionality for the uuid type is captured via:
namespace user { class uuid;
template <class Stream> Stream & operator <<(Stream &, uuid const &); }
If operator<< was a reasonable to-string alternative, we wouldn't be needing a library for it.
That is the purpose of the insertion operator: it produces a character sequence from the right hand argument.
It is relevant, of course, and the framework should be able to automatically bind suitable operator<< overloads if they exist. It should also provide a generic operator<< overload that calls convert, so all types that integrate in the convert framework are automatically compatible with streams.
The library's convert() should use a provided operator <<() when available and the library should provide an operator <<() that uses a provided convert() when available? How will that work? _____ 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 Fri, Feb 27, 2009 at 10:52 AM, Stewart, Robert <Robert.Stewart@sig.com> wrote:
On Friday, February 27, 2009 1:48 PM Emil Dotchevski wrote:
On Fri, Feb 27, 2009 at 5:30 AM, Stewart, Robert <Robert.Stewart@sig.com> wrote:
The framework uses IOStreams to do its work. I believe the conversion functionality for the uuid type is captured via:
namespace user { class uuid;
template <class Stream> Stream & operator <<(Stream &, uuid const &); }
If operator<< was a reasonable to-string alternative, we wouldn't be needing a library for it.
That is the purpose of the insertion operator: it produces a character sequence from the right hand argument.
No, its purpose is to implement dumping of objects in streams, in terms of other existing operator<< overloads. This can also be used to convert to string, but the streams framework is rather clumsy to use when you need a std::string.
It is relevant, of course, and the framework should be able to automatically bind suitable operator<< overloads if they exist. It should also provide a generic operator<< overload that calls convert, so all types that integrate in the convert framework are automatically compatible with streams.
The library's convert() should use a provided operator <<() when available and the library should provide an operator <<() that uses a provided convert() when available? How will that work?
The former requires a bit more template kungfu but isn't a problem. The latter is easy, well, sort of, you need to use the correct target type for convert, but you get the idea: template <class Stream,class T> Stream & operator<<( Stream & s, T const & x ) { return s << convert<std::string>(x); } Emil Dotchevski Reverge Studios, Inc. http://www.revergestudios.com/reblog/index.php?n=ReCode

On Friday, February 27, 2009 2:06 PM Emil Dotchevski wrote:
On Fri, Feb 27, 2009 at 10:52 AM, Stewart, Robert <Robert.Stewart@sig.com> wrote:
On Friday, February 27, 2009 1:48 PM Emil Dotchevski wrote:
On Fri, Feb 27, 2009 at 5:30 AM, Stewart, Robert <Robert.Stewart@sig.com> wrote:
The framework uses IOStreams to do its work. I believe the conversion functionality for the uuid type is captured via:
namespace user { class uuid;
template <class Stream> Stream & operator <<(Stream &, uuid const &); }
If operator<< was a reasonable to-string alternative, we wouldn't be needing a library for it.
That is the purpose of the insertion operator: it produces a character sequence from the right hand argument.
No, its purpose is to implement dumping of objects in streams, in terms of other existing operator<< overloads. This can also be used to convert to string, but the streams framework is rather clumsy to use when you need a std::string.
I hope you're not being purposely obtuse. I raised the insertion operator as the spelling of the hook for the library because the library will use a std::ostringstream to produce the (formatted) output. Since UDTs frequently provide such an operator, the convert code will piggy back on those operators easily, thus turning them into a handy means of converting to a string.
It is relevant, of course, and the framework should be able to automatically bind suitable operator<< overloads if they exist. It should also provide a generic operator<< overload that calls convert, so all types that integrate in the convert framework are automatically compatible with streams.
The library's convert() should use a provided operator <<() when available and the library should provide an operator <<() that uses a provided convert() when available? How will that work?
The former requires a bit more template kungfu but isn't a problem.
The latter is easy, well, sort of, you need to use the correct target type for convert, but you get the idea:
template <class Stream,class T> Stream & operator<<( Stream & s, T const & x ) { return s << convert<std::string>(x); }
My point was it was a loop. How do you resolve it? _____ 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 Fri, Feb 27, 2009 at 11:14 AM, Stewart, Robert <Robert.Stewart@sig.com> wrote:
On Friday, February 27, 2009 2:06 PM Emil Dotchevski wrote:
On Fri, Feb 27, 2009 at 10:52 AM, Stewart, Robert <Robert.Stewart@sig.com> wrote:
On Friday, February 27, 2009 1:48 PM Emil Dotchevski wrote:
On Fri, Feb 27, 2009 at 5:30 AM, Stewart, Robert <Robert.Stewart@sig.com> wrote:
The framework uses IOStreams to do its work. I believe the conversion functionality for the uuid type is captured via:
namespace user { class uuid;
template <class Stream> Stream & operator <<(Stream &, uuid const &); }
If operator<< was a reasonable to-string alternative, we wouldn't be needing a library for it.
That is the purpose of the insertion operator: it produces a character sequence from the right hand argument.
No, its purpose is to implement dumping of objects in streams, in terms of other existing operator<< overloads. This can also be used to convert to string, but the streams framework is rather clumsy to use when you need a std::string.
I hope you're not being purposely obtuse. I raised the insertion operator as the spelling of the hook for the library because the library will use a std::ostringstream to produce the (formatted) output. Since UDTs frequently provide such an operator, the convert code will piggy back on those operators easily, thus turning them into a handy means of converting to a string.
Operator<< overloads don't solve the problem of converting to string well. Boost::lexical_cast isn't either. I was assuming that anyone who considers yet another to-string conversion interface was on board with that. :)
My point was it was a loop. How do you resolve it?
The loop case is when you call convert. Basically you need to detect if the overload resolution for operator<< will bind the generic operator<< overload provided by convert, and in that case induce a compile error. Emil Dotchevski Reverge Studios, Inc. http://www.revergestudios.com/reblog/index.php?n=ReCode

Emil Dotchevski wrote:
I hope you're not being purposely obtuse. I raised the insertion operator as the spelling of the hook for the library because the library will use a std::ostringstream to produce the (formatted) output. Since UDTs frequently provide such an operator, the convert code will piggy back on those operators easily, thus turning them into a handy means of converting to a string.
Operator<< overloads don't solve the problem of converting to string well. Boost::lexical_cast isn't either. I was assuming that anyone who considers yet another to-string conversion interface was on board with that. :)
The operator<< does its job very well as a simple and widely-used fall back method of to-string conversions. I agree with Robert that the Boost.Convert library (oh, well, the one that I have in mind, at least) should support user-defined operator<<. However, there should also be a way to provide a more fine-grained conversion implementation, for example, with these Boost.Parameter customization options.
My point was it was a loop. How do you resolve it?
The loop case is when you call convert. Basically you need to detect if the overload resolution for operator<< will bind the generic operator<< overload provided by convert, and in that case induce a compile error.
I guess that is only possible when instantiating the operator<<, isn't it? Is that really desirable? IMO, the library should not jump over its head. It uses the user-provided operator<<, but it doesn't have to provide it.

Emil Dotchevski wrote:
Anyway, another question: is there a way to make operator() not be a template? It should be doable if you only need to receive the "source" argument to convert from. But if you want other arguments as well, I can't figure out how to do it without a template, unless to resort to type erasure.
Well, one way to take arguments is to tell the compiler that your function or operator() takes arguments, right?
std::string operator()( uuid const & value, int arg1, float arg2 );
C++ was type-safe even before templates were introduced. :)
It's not about type safety, it's about making the compiler to deduce your intentions. How do you instruct the compiler to pass the argument with tag "arg1" into the second argument of your operator, and not in the first or third? I can only see one way to do so: template< typename ArgsT > std::string operator()( ArgsT const& args ) const { return operator() (args[source], args[arg1], args[arg2]); } Thus, yielding the template I was suggesting to leave in my previous post.
If I'm the maintainer of class uuid and I am asked to provide a to-string conversion, you'll find it very difficult to convince me that it makes sense to replace this:
I believe, Robert has already answered this. You get more flexibility and the common interface for this functionality. If you don't need all this stuff, you can stay with the simple and restricted interface you suggest, and I won't even try to reconvince you. :) I'll just say that I often prefer flexibility if it doesn't compromise user-side usability too much.

On Thu, Feb 26, 2009 at 12:42 PM, Andrey Semashev <andrey.semashev@gmail.com> wrote:
Emil Dotchevski wrote:
Anyway, another question: is there a way to make operator() not be a template?
It should be doable if you only need to receive the "source" argument to convert from. But if you want other arguments as well, I can't figure out how to do it without a template, unless to resort to type erasure.
Well, one way to take arguments is to tell the compiler that your function or operator() takes arguments, right?
std::string operator()( uuid const & value, int arg1, float arg2 );
C++ was type-safe even before templates were introduced. :)
It's not about type safety, it's about making the compiler to deduce your intentions. How do you instruct the compiler to pass the argument with tag "arg1" into the second argument of your operator, and not in the first or third? I can only see one way to do so:
template< typename ArgsT > std::string operator()( ArgsT const& args ) const { return operator() (args[source], args[arg1], args[arg2]); }
I understand the problem very well, and I think the solution is not to write a function that takes a single args argument, but instead to provide overloads. This would not only solve your problem, it would also make convert() compatible with boost::bind/function/lambda etc. What's wrong with the following call syntax: boost::convert<target>( source const & [,arg1,arg2,...] ) that is, the caller provides the target type for the conversion, then the first argument is considered the source (deduced automatically), and the rest of the arguments configure the conversion. Going back to my uuid example, uuid.hpp could simply say: #include <string> namespace user { class uuid; template <class Target> Target convert( uuid const & ); template <> std::string convert<std::string>( uuid const & ); } (no need to include anything.) Now, the user of uuid.hpp can also #include "boost/convert.hpp" when making calls to convert. This could provide generic overloads that support common conversion options, which would bind only if uuid.hpp doesn't provide overloads itself (which would obviously be more specialized and the overload resolution will prefer them over the convert.hpp generics.) Emil Dotchevski Reverge Studios, Inc. http://www.revergestudios.com/reblog/index.php?n=ReCode

On Thursday, February 26, 2009 5:09 PM Emil Dotchevski wrote:
On Thu, Feb 26, 2009 at 12:42 PM, Andrey Semashev <andrey.semashev@gmail.com> wrote:
template< typename ArgsT > std::string operator()( ArgsT const& args ) const { return operator() (args[source], args[arg1], args[arg2]); }
I understand the problem very well, and I think the solution is not to write a function that takes a single args argument, but instead to provide overloads. This would not only solve your problem, it would also make convert() compatible with boost::bind/function/lambda etc.
What's wrong with the following call syntax:
boost::convert<target>( source const & [,arg1,arg2,...] )
that is, the caller provides the target type for the conversion, then the first argument is considered the source (deduced automatically), and the rest of the arguments configure the conversion.
That only works if the argument types and number are known ahead of time. The Boost.Parameter approach allows for open ended extension -- for good or ill.
Going back to my uuid example, uuid.hpp could simply say:
#include <string>
namespace user { class uuid; template <class Target> Target convert( uuid const & ); template <> std::string convert<std::string>( uuid const & ); }
(no need to include anything.)
That's nice and clean. To support a variable number of formatting arguments, Boost.Parameter is helpful, so it would necessitate an overload to support those arguments: namespace user { template <class Target, class Args> Target convert(uuid const &, Args const &); } Applying Boost.ConceptCheck can ensure that Args is a Boost.Parameter arguments pack. Since that is, of necessity a function template, it isn't as clean as your overload and since we don't yet have partial specialization of function templates, you can't create just the Target = std::string variant.
Now, the user of uuid.hpp can also #include "boost/convert.hpp" when making calls to convert. This could provide generic overloads that support common conversion options, which would bind only if uuid.hpp doesn't provide overloads itself (which would obviously be more specialized and the overload resolution will prefer them over the convert.hpp generics.)
I like the idea of the framework providing default recognition of any supplied formatting information while deferring to customizations when available. _____ 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 Fri, Feb 27, 2009 at 5:40 AM, Stewart, Robert <Robert.Stewart@sig.com> wrote:
On Thursday, February 26, 2009 5:09 PM Emil Dotchevski wrote:
On Thu, Feb 26, 2009 at 12:42 PM, Andrey Semashev <andrey.semashev@gmail.com> wrote:
template< typename ArgsT > std::string operator()( ArgsT const& args ) const { return operator() (args[source], args[arg1], args[arg2]); }
I understand the problem very well, and I think the solution is not to write a function that takes a single args argument, but instead to provide overloads. This would not only solve your problem, it would also make convert() compatible with boost::bind/function/lambda etc.
What's wrong with the following call syntax:
boost::convert<target>( source const & [,arg1,arg2,...] )
that is, the caller provides the target type for the conversion, then the first argument is considered the source (deduced automatically), and the rest of the arguments configure the conversion.
That only works if the argument types and number are known ahead of time. The Boost.Parameter approach allows for open ended extension -- for good or ill.
I don't understand. The overload-based approach can also be extended -- by providing more overloads, user-defined or generic overloads given by the convert framework itself. Emil Dotchevski Reverge Studios, Inc. http://www.revergestudios.com/reblog/index.php?n=ReCode

On Friday, February 27, 2009 1:55 PM Emil Dotchevski wrote:
On Fri, Feb 27, 2009 at 5:40 AM, Stewart, Robert <Robert.Stewart@sig.com> wrote:
What's wrong with the following call syntax:
boost::convert<target>( source const & [,arg1,arg2,...] )
that is, the caller provides the target type for the conversion, then the first argument is considered the source (deduced automatically), and the rest of the arguments configure the conversion.
That only works if the argument types and number are known ahead of time. The Boost.Parameter approach allows for open ended extension -- for good or ill.
I don't understand. The overload-based approach can also be extended -- by providing more overloads, user-defined or generic overloads given by the convert framework itself.
Each of your arg1, arg2, ...argN must have a type or be deduced by the compiler in a function template. In order to use them as formatting options, the function [template] must understand which parameters(s) map to the needed formatting option(s), while ignoring the rest. Thus, the order, purpose, etc. must be known beforehand. Each new, custom formatting option extends the set introduced by *all* other custom formatting options. That is untenable. With the Boost.Parameters approach, the formatting options are gathered into, conceptually, a dictionary which is accessed by keys. A given convert function template must know about the keys it deems important and can use them to access the desired options. _____ 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 Fri, Feb 27, 2009 at 11:02 AM, Stewart, Robert <Robert.Stewart@sig.com> wrote:
On Friday, February 27, 2009 1:55 PM Emil Dotchevski wrote:
On Fri, Feb 27, 2009 at 5:40 AM, Stewart, Robert <Robert.Stewart@sig.com> wrote:
What's wrong with the following call syntax:
boost::convert<target>( source const & [,arg1,arg2,...] )
that is, the caller provides the target type for the conversion, then the first argument is considered the source (deduced automatically), and the rest of the arguments configure the conversion.
That only works if the argument types and number are known ahead of time. The Boost.Parameter approach allows for open ended extension -- for good or ill.
I don't understand. The overload-based approach can also be extended -- by providing more overloads, user-defined or generic overloads given by the convert framework itself.
Each of your arg1, arg2, ...argN must have a type or be deduced by the compiler in a function template. In order to use them as formatting options, the function [template] must understand which parameters(s) map to the needed formatting option(s), while ignoring the rest. Thus, the order, purpose, etc. must be known beforehand. Each new, custom formatting option extends the set introduced by *all* other custom formatting options. That is untenable.
With the Boost.Parameters approach, the formatting options are gathered into, conceptually, a dictionary which is accessed by keys. A given convert function template must know about the keys it deems important and can use them to access the desired options.
In fact in many languages that's exactly how arguments get passed. But obviously it isn't appropriate in C++ in general. For sure, there are cases when you have to resort to dictionaries of parameters. I don't see why this is necessary in the case of convert. Whenever the caller uses a formatting parameter, the function the call binds to has to understand that parameter. In our case, there are two contexts that can deal with formatting parameters: the user-defined overloads, and the generic framework overloads. If the formatting parameter is generic, then it's reasonable for the framework to handle it. If not, then it is irrelevant to the framework itself. It can't do anything with it, well, other than pack it into a dictionary to send it to the user. :) Emil Dotchevski Reverge Studios, Inc. http://www.revergestudios.com/reblog/index.php?n=ReCode

On Friday, February 27, 2009 2:20 PM Emil Dotchevski wrote:
On Fri, Feb 27, 2009 at 11:02 AM, Stewart, Robert <Robert.Stewart@sig.com> wrote:
With the Boost.Parameters approach, the formatting options are gathered into, conceptually, a dictionary which is accessed by keys. A given convert function template must know about the keys it deems important and can use them to access the desired options.
I don't see why this is necessary in the case of convert. Whenever the caller uses a formatting parameter, the function the call binds to has to understand that parameter. In our case, there are two contexts that can deal with formatting parameters: the user-defined overloads, and the generic framework overloads. If the formatting parameter is generic, then it's reasonable for the framework to handle it. If not, then it is irrelevant to the framework itself. It can't do anything with it, well, other than pack it into a dictionary to send it to the user. :)
If the framework uses a formatting option to provide default formatting support, then should that option be passed to the UDT's conversion function? If some of those arguments are consumed, in what order should the rest be passed to the UDT's conversion function? How will the framework know that one of the formatting options is supported but not the others in order to know to provide the default formatting support? I don't understand how you think the framework can manage all of this magic. Are we talking past one another somehow? _____ 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.

Emil Dotchevski wrote:
What's wrong with the following call syntax:
boost::convert<target>( source const & [,arg1,arg2,...] )
that is, the caller provides the target type for the conversion, then the first argument is considered the source (deduced automatically), and the rest of the arguments configure the conversion.
Going back to my uuid example, uuid.hpp could simply say:
#include <string>
namespace user { class uuid; template <class Target> Target convert( uuid const & ); template <> std::string convert<std::string>( uuid const & ); }
(no need to include anything.)
Now, the user of uuid.hpp can also #include "boost/convert.hpp" when making calls to convert. This could provide generic overloads that support common conversion options, which would bind only if uuid.hpp doesn't provide overloads itself (which would obviously be more specialized and the overload resolution will prefer them over the convert.hpp generics.)
Sorry, I can't imagine how the generic convert.hpp could provide support for additional conversion options for user::uuid. It is my understanding that such options should be supported by the user::uuid implementer and should not be part of Boost.Convert. I may not understand your point well, though. Could you produce an illustrating code sample?

On Sat, Feb 28, 2009 at 2:55 AM, Andrey Semashev <andrey.semashev@gmail.com> wrote:
Emil Dotchevski wrote:
What's wrong with the following call syntax:
boost::convert<target>( source const & [,arg1,arg2,...] )
that is, the caller provides the target type for the conversion, then the first argument is considered the source (deduced automatically), and the rest of the arguments configure the conversion.
Going back to my uuid example, uuid.hpp could simply say:
#include <string>
namespace user { class uuid; template <class Target> Target convert( uuid const & ); template <> std::string convert<std::string>( uuid const & ); }
(no need to include anything.)
Now, the user of uuid.hpp can also #include "boost/convert.hpp" when making calls to convert. This could provide generic overloads that support common conversion options, which would bind only if uuid.hpp doesn't provide overloads itself (which would obviously be more specialized and the overload resolution will prefer them over the convert.hpp generics.)
I can't imagine how the generic convert.hpp could provide support for additional conversion options for user::uuid.
Additional conversion options that are specific to converting user::uuid to string shouldn't be a concern for the generic convert framework. The caller can pass such additional arguments if user::uuid provides overloads that take them; if not then the caller will get a compile error, which is also appropriate. Emil Dotchevski Reverge Studios, Inc. http://www.revergestudios.com/reblog/index.php?n=ReCode

Emil Dotchevski wrote:
Now, the user of uuid.hpp can also #include "boost/convert.hpp" when making calls to convert. This could provide generic overloads that support common conversion options, which would bind only if uuid.hpp doesn't provide overloads itself (which would obviously be more specialized and the overload resolution will prefer them over the convert.hpp generics.) I can't imagine how the generic convert.hpp could provide support for additional conversion options for user::uuid.
Additional conversion options that are specific to converting user::uuid to string shouldn't be a concern for the generic convert framework. The caller can pass such additional arguments if user::uuid provides overloads that take them; if not then the caller will get a compile error, which is also appropriate.
I think that'll make too many of these overloads, with an added confusion on the user's side, who has to remember the order of these options in the overloads. I certainly like named arguments more.

AMDG Emil Dotchevski wrote:
What's wrong with the following call syntax:
boost::convert<target>( source const & [,arg1,arg2,...] )
that is, the caller provides the target type for the conversion, then the first argument is considered the source (deduced automatically), and the rest of the arguments configure the conversion.
Going back to my uuid example, uuid.hpp could simply say:
#include <string>
namespace user { class uuid; template <class Target> Target convert( uuid const & ); template <> std::string convert<std::string>( uuid const & ); }
This overload of convert cannot reliably be found as the syntax convert<std::string>(...) doesn't invoke ADL. (At least, not according to the standard. Some compilers perform ADL in this case anyway) In Christ, Steven Watanabe

On Sat, Feb 28, 2009 at 8:48 AM, Steven Watanabe <watanabesj@gmail.com> wrote:
AMDG
Emil Dotchevski wrote:
What's wrong with the following call syntax:
boost::convert<target>( source const & [,arg1,arg2,...] )
that is, the caller provides the target type for the conversion, then the first argument is considered the source (deduced automatically), and the rest of the arguments configure the conversion.
Going back to my uuid example, uuid.hpp could simply say:
#include <string>
namespace user { class uuid; template <class Target> Target convert( uuid const & ); template <> std::string convert<std::string>( uuid const & ); }
This overload of convert cannot reliably be found as the syntax convert<std::string>(...) doesn't invoke ADL. (At least, not according to the standard. Some compilers perform ADL in this case anyway)
True, so how about to_string, instead of convert :)) Emil Dotchevski Reverge Studios, Inc. http://www.revergestudios.com/reblog/index.php?n=ReCode

From: "Andrey Semashev" <andrey.semashev@gmail.com>
I have put together a rough example of what I have in mind. Please, find the code attached. It works on my GCC 4.3.
Andrey, Thank you for the code. Very much appreciated. I'll certainly use it as a starting point for any further Boost.Parameter-related work. Having said that I won't be trying to incorporate this interface into convert() that I am currently putting forward. It still looks to me that the IOStream-based interface does all you demonstrate in your example and will address the needs of many people without Boost.Parameter. Thanks again for finding the time to come up with the example, I promise, it won't be wasted. Best, V.

It seems like there's still a lot of discussion going on about the interface even when Vladimir is plodding on, so I thought I might offer another candidate that I haven't seen put forth just yet. template<class Target, class Source> *unspecified-type* convert_to(const Source& value); This would give us the flexibility to easily implement an interface that could be used like so: int i = convert_to<int>("5").get(); // get "5" converted to int (throw on error) unsigned u = convert_to<unsigned>("-1").get_or(0); // get "-1" converted to unsigned or else 0 int i = convert_to<int>("0xff").get_special(as_hex()); // get "0xff" converted to int using as_hex functor And we could easily add another function to support STL algorithm composition while maintaining consistency in our public interface. template<class Target, class Source> *unspecified-type* convert_to(); Which could be used like this: vector<string> strs; strs.push_back("1"); strs.push_back("2"); strs.push_back("3"); vector<int> ints; transform (strs.begin (), strs.end(), back_inserter (ints), convert_to<int, string>().get()); // throw on error vector<string> strs; strs.push_back("1"); strs.push_back("-2"); strs.push_back("3"); vector<unsigned> uints; transform (strs.begin (), strs.end(), back_inserter (uints), convert_to<unsigned, string>().get_or(0)); // default to 0 vector<string> strs; strs.push_back("0xa"); strs.push_back("0xb"); strs.push_back("0xc"); vector<int> ints; transform (strs.begin (), strs.end(), back_inserter (ints), convert_to<unsigned, string>().get_special(as_hex())); // as hexadecimal With this approach, the template argument is still required even in the defaulting-to-fallback form, making the interface more consistent. It is also much clearer and explicit what kind of behavior you want the conversion to exhibit. For example through the use of get() that you risk throwing an exception, with get_or() you get a default on error, or with get_special() you can use whatever special formatting conversion you feel like providing. The interface on the unspecified- type could be easily extended later, through the addition of new methods. And it's easy to return a different type when convert_to() is called without any parameter that could work intuitively with STL algorithms and maintain a consistent interface. -- Andrew Troschinetz Applied Research Laboratories

On Thu, Feb 19, 2009 at 12:57 PM, Andrew Troschinetz <ast@arlut.utexas.edu> wrote:
It seems like there's still a lot of discussion going on about the interface even when Vladimir is plodding on, so I thought I might offer another candidate that I haven't seen put forth just yet.
template<class Target, class Source> *unspecified-type* convert_to(const Source& value);
This would give us the flexibility to easily implement an interface that could be used like so:
int i = convert_to<int>("5").get(); // get "5" converted to int (throw on error)
My problem with the convert_to<int> syntax is that it demands that the function it binds to is a template. For me it is crucial for any to- and from- string conversion framework to be able to bind simple user-defined overloads like: class foo; char const * to_string( foo const & ); or class bar; std::string to_string( bar const & ); or template <class T> class foobar; template <class T> std::string to_string( foobar<T> const & ); The interface specification, I think, has to be that converting to string is done through unqualified call (so ADL kicks in) using automatic argument deduction if templates are involved. Emil Dotchevski Reverge Studios, Inc. http://www.revergestudios.com/reblog/index.php?n=ReCode

It seems like there's still a lot of discussion going on about the interface even when Vladimir is plodding on, so I thought I might offer another candidate that I haven't seen put forth just yet.
I personally do not mind wotking in such a mode. I am merely implemeting what we are seemingly settling on (as far as my interpretation goes) like 'convert' as a class, etc. to see how it works out in reality. And, therefore, my "plodding on" should not be an ideas' deterrent of any kind.
template<class Target, class Source> *unspecified-type* convert_to(const Source& value);
This would give us the flexibility to easily implement an interface that could be used like so:
int i = convert_to<int>("5").get(); // get "5" converted to int (throw on error)
unsigned u = convert_to<unsigned>("-1").get_or(0); // get "-1" converted to unsigned or else 0
int i = convert_to<int>("0xff").get_special(as_hex()); // get "0xff" converted to int using as_hex functor
My immediate concern is a lot of new vocabulary introduced. That can provide tons of useful stuff... that the majority of people might never use. Secondly, I am not sure what are the advantages of the alternative interface above. We seem to achieve all the above with the existing interface and only 1 (!) new keyword ("convert_to") in stad of 5 (convert_to, get, get_or, get_special, as_hex). // get "5" converted to int (throw on error) int i = convert_to<int>("5")(throw_t()); // get "-1" converted to unsigned or else 0 unsigned u = convert_to<unsigned>("-1", 0); // get "0xff" converted to int using as_hex functor int i = convert_to<int>("0xff") >> std::hex; With all due respect the interface immediately above looks (to me) more consistent and easier extendable. [snip] Still, I am keeping an open mind on that. If something else comes up, I may well flip onto your side (the current implementation already looks nothing as what I started with showing what a "flipper" I am). :-) V.

On Feb 20, 2009, at 9:08 PM, Vladimir Batov wrote:
It seems like there's still a lot of discussion going on about the interface even when Vladimir is plodding on, so I thought I might offer another candidate that I haven't seen put forth just yet.
I personally do not mind wotking in such a mode. I am merely implemeting what we are seemingly settling on (as far as my interpretation goes) like 'convert' as a class, etc. to see how it works out in reality. And, therefore, my "plodding on" should not be an ideas' deterrent of any kind.
Thanks, just wanted to make sure I was being polite :)
template<class Target, class Source> *unspecified-type* convert_to(const Source& value);
This would give us the flexibility to easily implement an interface that could be used like so:
int i = convert_to<int>("5").get(); // get "5" converted to int (throw on error)
unsigned u = convert_to<unsigned>("-1").get_or(0); // get "-1" converted to unsigned or else 0
int i = convert_to<int>("0xff").get_special(as_hex()); // get "0xff" converted to int using as_hex functor
My immediate concern is a lot of new vocabulary introduced. That can provide tons of useful stuff... that the majority of people might never use.
I had this concern too, though as_hex was just an example of something a user might write if they wanted some special kind of conversion, not something to include in the library.
Secondly, I am not sure what are the advantages of the alternative interface above. We seem to achieve all the above with the existing interface and only 1 (!) new keyword ("convert_to") in stad of 5 (convert_to, get, get_or, get_special, as_hex).
Fair enough. The goals of the interface were: 1. Be able to provide a default value w/o type deducing being a problem. (ie: making the interface unclear) 2. Have the same interface when used as a functor. 3. Be very explicit and clear about if you're getting throwing behavior or defaulting behavior.
// get "5" converted to int (throw on error) int i = convert_to<int>("5")(throw_t());
So could I do the following? vector<string> s; vector<int> i; transform (s.begin(), s.end(), back_inserter(i), convert_to<int, string>(throw_t()));
// get "-1" converted to unsigned or else 0 unsigned u = convert_to<unsigned>("-1", 0);
In this case <unsigned> isn't necessary, type deducing kicks in and we could just as well write: unsigned u = convert_to("-1", 0); Which reads to me as "convert to -1 from 0". And if we change "convert_to" to "convert" it becomes even more confusing.
// get "0xff" converted to int using as_hex functor int i = convert_to<int>("0xff") >> std::hex;
I thought operator>>() returns an istream? If there's precedent somewhere where it doesn't return an istream, forgive my ignorance please. But given that interface, what does the following mean? int a, b, c; string d; int i = convert_to<int>("0xff") >> std::hex >> a >> b >> c >> d;
With all due respect the interface immediately above looks (to me) more consistent and easier extendable.
That's where the get_special() method comes in. It could take any user defined function/functor for a conversion that required additional attention. However I realize that's a different sort of extensibility than what you're talking about.
Still, I am keeping an open mind on that. If something else comes up, I may well flip onto your side (the current implementation already looks nothing as what I started with showing what a "flipper" I am). :-)
I'm not married to my interface either, I was mainly just using this discussion as a sounding board. I'd like to see a really nice interface come out of it that addresses the points I made: 1. Be able to provide a default value w/o type deducing being a problem. (ie: making the interface unclear) 2. Have the same interface when used as a functor. 3. Be very explicit and clear about if you're getting throwing behavior or defaulting behavior. -- Andrew Troschinetz Applied Research Laboratories

1. Be able to provide a default value w/o type deducing being a problem. (ie: making the interface unclear) 2. Have the same interface when used as a functor. 3. Be very explicit and clear about if you're getting throwing behavior or defaulting behavior.
I believe we achieved thses goals with int i = convert<string, int>(str); int i = convert_to<int>(str); int i = convert_from(str, -1); See below.
// get "5" converted to int (throw on error) int i = convert_to<int>("5")(throw_t());
So could I do the following?
vector<string> s; vector<int> i; transform (s.begin(), s.end(), back_inserter(i), convert_to<int, string>(throw_t()));
Pretty much. You can do the following transform ( s.begin(), s.end(), back_inserter(i), convert<int, string>(string(), -1) >> throw_t() >> std::hex); I.e. get throwing hex-formatting behavior.
// get "-1" converted to unsigned or else 0 unsigned u = convert_to<unsigned>("-1", 0);
In this case <unsigned> isn't necessary, type deducing kicks in and we could just as well write: unsigned u = convert_to("-1", 0);
No, it won't work. 'convert_to' olny takes one argument and requires explicit TypeTo: unsigned i = convert_to(str). 'convert_from' does deduce both but I do not think there is confusion here: unsigned u = convert_from("-1", 0);
Which reads to me as "convert to -1 from 0".
As you can see it reads convert *from* "-1" string.
And if we change "convert_to" to "convert" it becomes even more confusing.
I am not sure I follow this. Which part of the below do you find confusing? unsigned u = convert<string, unsigned>("-1", 0);
// get "0xff" converted to int using as_hex functor int i = convert_to<int>("0xff") >> std::hex;
I thought operator>>() returns an istream? If there's precedent somewhere where it doesn't return an istream, forgive my ignorance please.
As far as I am concerned, op>>() returns whatever we'd like it to return. IMHO it certainly is not the property of std::streams. You could have a look at std::bitset. I am sure we can find many more examples.
But given that interface, what does the following mean?
int a, b, c; string d; int i = convert_to<int>("0xff") >> std::hex >> a >> b >> c >> d;
It surely looks gibberish to me. I am not sure about older version but with the latest version I do not believe it will compile.
I'm not married to my interface either, I was mainly just using this discussion as a sounding board. I'd like to see a really nice interface come out of it that addresses the points I made:
1. Be able to provide a default value w/o type deducing being a problem. (ie: making the interface unclear)
Do you find the following satisfactory or still have concerns: int i = convert<string, int>(str); int i = convert_to<int>(str); int i = convert_from(str, -1);
2. Have the same interface when used as a functor.
How about this: transform ( s.begin(), s.end(), back_inserter(i), convert<int, string>(string(), -1) >> throw_t() >> std::hex);
3. Be very explicit and clear about if you're getting throwing behavior or defaulting behavior.
Currently it is like this -- you provide a default, you get the defaulting behavior; you do *not* provide a default, you get the throwing behavior. Do you think it is sufficient? Still, we could do int i = convert<string, int, throw_t::yes>(str); It'd make the implementation leaner as well. But I suspect people won't like it. Best, V.

I missed your link to the source code before, but I went back and found it. It has cleared up much confusion on my part. Though some confusion yet remains. On Feb 23, 2009, at 10:36 PM, Vladimir Batov wrote:
You can do the following
transform ( s.begin(), s.end(), back_inserter(i), convert<int, string>(string(), -1) >> throw_t() >> std::hex);
I believe that says "convert to int from string, throw on error, format as hex." But what does string() and -1 signify in that case? And if you'll allow me to nitpick, it seems you've mixed up the order of the function parameters here. Should it read as follows? convert<int, string>(-1, string()) And since it seems that -1 and string() serve no purpose here, can we possible get a refinement to the following? transform ( s.begin(), s.end(), back_inserter(i), convert<int, string>() >> throw_t() >> std::hex);
'convert_to' olny takes one argument and requires explicit TypeTo:
unsigned i = convert_to(str).
Yes, sorry I mischaracterized the convert_to interface as you currently have it. I'm sure you meant to say here: unsigned i = convert_to<unsigned>(str); Correct? That makes sense.
'convert_from' does deduce both but I do not think there is confusion here:
unsigned u = convert_from("-1", 0);
I agree, somehow that syntax is clearer in my mind.
And if we change "convert_to" to "convert" it becomes even more confusing.
I am not sure I follow this. Which part of the below do you find confusing?
unsigned u = convert<string, unsigned>("-1", 0);
Ah, see. Even within you're own post things have been confused. Above we have: convert<int, string>(string(), -1) // <A, B> (B, A) And now we have: convert<string, unsigned>("-1", 0); // <A, B> (A, B) From looking at the source code, it seems like the first one is just a typo. But you see how easily things can get mixed up. I'd be happier picking one interface, either convert or convert_to or convert_from to keep things clear. Though that might make others unhappy, I don't know.
// get "0xff" converted to int using as_hex functor int i = convert_to<int>("0xff") >> std::hex;
I thought operator>>() returns an istream? If there's precedent somewhere where it doesn't return an istream, forgive my ignorance please.
As far as I am concerned, op>>() returns whatever we'd like it to return. IMHO it certainly is not the property of std::streams. You could have a look at std::bitset. I am sure we can find many more examples.
You're correct, of course. Sorry I phrased the question badly. Operator >> can be made to do anything. It could be a bitshift operation as you say. But when I see >> and std::hex on one line, my mind immediately goes to streams because std::hex is a stream manipulator. And in this case, the stream that's being manipulated isn't exposed to the user, it's an implementation detail. Using stream manipulators here may be nice, but it's a bit of a case of implementation leaking into the interface. I'm not saying that's entirely bad in this case because using stream manipulators does seem to solve the problem nicely, but it is a bit weird (at least to me anyway) seeing them used this way. I could get used to this interface though, nice job so far. Thank you for the effort. -- Andrew Troschinetz Applied Research Laboratories

transform ( s.begin(), s.end(), back_inserter(i), convert<int, string>(string(), -1) >> throw_t() >> std::hex);
I believe that says "convert to int from string, throw on error, format as hex." But what does string() and -1 signify in that case?
And if you'll allow me to nitpick, it seems you've mixed up the order of the function parameters here. Should it read as follows?
convert<int, string>(-1, string())
Yes, I typed it in a hurry and messed it up. Sorry. It meant to be transform ( s.begin(), s.end(), back_inserter(i), convert<string, int>(string(), -1) >> throw_t() >> std::hex); I.e. we have strings. We want to convert them to ints. So, we say, convert<string, int>.
And since it seems that -1 and string() serve no purpose here, can we possible get a refinement to the following?
In fact, string() and -1 do serve a purpose. Namely, -1 is the default/failure parameter to be used if conversion fails. string() is a nuisance, I agree, but it has to be in front of -1 as the signature is convert(TypeIn const&, TypeOut const&). But you are correct, if we do not need the default value (as with convert_to()) we should be able to do as you indicated below. Will add this.
transform ( s.begin(), s.end(), back_inserter(i), convert<int, string>() >> throw_t() >> std::hex);
I'm sure you meant to say here:
unsigned i = convert_to<unsigned>(str);
Correct? That makes sense.
I did mean to say that. Thank you. convert_to always requires the type we are converting to.
From looking at the source code, it seems like the first one is just a typo.
Yes, it was. I hate when that happens. Trying to explain and instead confuse people. Frustrating.
But you see how easily things can get mixed up. I'd be happier picking one interface, either convert or convert_to or convert_from to keep things clear. Though that might make others unhappy, I don't know.
I understand. I am of an opinion that we should not be deciding for people what they are to use... especially for such a diverse group as Boosters. We'll provide them with convert, convert_to, convert_from which I hope will cover all the preferences.
... when I see >> and std::hex on one line, my mind immediately goes to streams because std::hex is a stream manipulator. And in this case, the stream that's being manipulated isn't exposed to the user, it's an implementation detail. Using stream manipulators here may be nice, but it's a bit of a case of implementation leaking into the interface.
Hmm, I am not sure I see it that way. To me those manipulators are merely building blocks/components. If they fit some other purpose, I'll use them without hesitation. However, I tend to agree that it somewhat gives away the implementation detail. Although come to think of it I am not sure it's such a bad thing as I'd expect that to make the user feel more comfortable as he'll know what's ticking inside.
I'm not saying that's entirely bad in this case because using stream manipulators does seem to solve the problem nicely, but it is a bit weird (at least to me anyway) seeing them used this way.
Yes, I think I know what you mean.. and, gosh, haven't I felt that way while looking at many Boost libraries. I am sure this little piece won't even get into the first ten in this category. What do you think? ;-)
I could get used to this interface though, nice job so far. Thank you for the effort.
Actually it was very little of my own effort. I merely followed the path of the least resistance and implemented what people were suggesting/discussing. What's come out so far has nothing to do what I foolishly stepped with. :-) But still thanks for the kind words anyway. :-) Best, V.

From: "Andrew Troschinetz" <ast@arlut.utexas.edu> But you see how easily things can get mixed up. I'd be happier picking one interface, either convert or convert_to or convert_from to keep things clear. Though that might make others unhappy, I don't know.
I understand. I am of an opinion that we should not be deciding for people what they are to use... especially for such a diverse group as Boosters. We'll provide them with convert, convert_to, convert_from which I hope will cover all the preferences.
I thought some more about you mentioning "one interface... to keep things clear" and I think I agree with you. I'll be only leaving "convert_to or convert_from".
... when I see >> and std::hex on one line, my mind immediately goes to streams because std::hex is a stream manipulator.
We currently have convert_to<int>(str) >> boost::nothrow >> std::hex; I've been thinking of other operators and I am not sure anyone stands out as a better candidate. Like convert_to<int>(str) & boost::nothrow; might be good but then convert_to<int>(str) & boost::nothrow & std::hex; does not look that hot. So, I personally feel better with op>> as it indeed exposes the underlying IOStreams and tells the user I am the one you've known for many years. Do you have anything particular in mind? There is an alternative to accommodate throwing like convert_to<int, boost::nothrow>(str) >> std::hex; but it feels too long, isn't it? Best, V.
participants (9)
-
Andrew Troschinetz
-
Andrey Semashev
-
Daniel Wallin
-
David Abrahams
-
Emil Dotchevski
-
Steven Watanabe
-
Stewart, Robert
-
Vladimir Batov
-
Vladimir.Batov@wrsa.com.au