First of all, let me tip my hat to Jeroen for his very thorough and thoughtful review. I fully agree with all what he said. Let me also get out of the way that I have not looked at the library itself. 'm just following the discussions.
Jeroen, Thank you for your input. My apologies for snipping (a lot) -- I want to focus on the immediate. If we clear this one up, we'll get to the rest next. If we do not clear this one up, we won't get to the rest. :-)
...
as other have argued boost::lexical_cast is used without the try-catch in a lot of places. Simply expecting it not to
Jeroen Habraken wrote throw
and dropping everything on the ground when it does isn't the nicest way to go about things but it does get things done.
Jeroen, seriously, we are serious people writing serious s/w. Should we be discussing children playing with matches here? :-)
A large part of the software I write (which is no child's play, at least in my eyes) needs simple solutions to complex problems. The simpler a solution the better. I as a library writer often leave the handling of exceptions which are unrelated to the task at hand to the user of my library. Thus having a conversion throw an exception deep in the guts of my library is a perfect solution. This is particularly true if my library has no way of telling what would be a proper default value. I might be biased as I feel to have my share in the interface design of Jeroen's Coerce library. From that experience (and all the other library work I'm doing) I wouldn't like to have library interfaces which are more verbose than absolutely needed. That said, I very much like the interface design of Coerce, as it is as straight as it can get for the common case and allows to customize its behavior with little effort, without me having to remember shrouded syntax. Also, as Jeroen stated, library interfaces should not get in the way of performance. But more about this further down.
I guess the question is whether we should make it harder to use the library in such a way, and I think we shouldn't.
It is the wrong angle IMO. First, we should make it work properly. Then we make it as simple to use as possible within the constraints imposed by the first point.
Note that for this to be efficient I'd like to see the optional to be movable, which isn't yet the case with boost::optional if I'm not mistaken.
You might have noticed that I mention std::tr2::optional... which has what you are after. Andrzej is working on incorporating/backporting those features to boost::optional. "Life happened" to him also :-) but I am hoping, if we are extra nice, he might just might to get the interface we are after into 1.56. Andrzej, ple-e-e-ease, pretty pretty please!?
Honestly, when I first realized your convert() returns an optional I felt something like revulsion. This is for two reasons: a) While an optional exposes an operator*() and an operator->(), the resulting conversion syntax turns into something more complex than it has to be. b) Using optional means you need an additional allocation (at least sometimes if you have an improved version using small object optimization internally). I tend to say that this alone can turn into a knockout criteria for a supposedly high performance, low level conversion library. However I have not done any measurements - so all this is pure conjecture. There have been done plenty of conversion library comparisons by different people. It might be a good idea to show performance results of your solution comparing to the existing libraries.
I'm slowly warming up to the optional-based interface as it eliminates the throwing / non-throwing interface discussion, which is a good thing. Once again two comments, first of which is what's going to happen until the std::optional (whether in TR2 or elsewhere) is widely accepted. The optional provided by boost has a slightly different interface and isn't movable.
It is
std::tr2::optional<TypeOut> convert(TypeIn const&, Converter const&);
you are warming up to, right? If it is so, I am really glad to hear that. I am not married to it but all things considered it seems the most generic and flexible, functionally-complete, not too verbose... and sufficiently close to lexical_cast :-) for people not to feel alienated.
Given the immense variability of conversion-related deployments I see this interface as the only one that everyone can deploy -- exceptions? no problem; delayed exceptions? easy; no exceptions but failure-condition returned instead? piece of cake; can't be bothered processing the failure-condition and wanna proceed with the default? coming right up; want to process failure but still go with the default? anything to keep the user happy!
So far, no one (to my knowledge) has come up with another interface which simpler without sacrificing those mentioned important qualities (given we are talking about a library-grade interface).
See Jeroen's Coerce library for an example. It's as simple as lexical_cast or as complex as a full blown Spirit parser and everything in between.
The second is performance, with boost::coerce a lot of time was invested to prevent the interface from slowing down the conversion itself. I simply don't know how this interface will behave in that regard, when I have more time I'll try some benchmarking.
Ah, that should not be a problem. I don't think. I actually expect the convert() interface to be wiped out altogether when optimized.
I used to think that as well. Sadly, more often than not compilers are not able to optimize all of it. But there is hope that they will get better over time. At least I would like to see numbers confirming your statement. Regards Hartmut --------------- http://boost-spirit.com http://stellar.cct.lsu.edu