Hartmut, Thank you for your input. It's much appreciated. From the critical tone of your post I'll take it you are voting "no". If you did not state it by accident, then I am happy to do that for you. If you did not state it intentionally, then I think you should as you should not be shying away from your community responsibilities. :-) Please find my replies below. On 05/26/2014 12:37 AM, Hartmut Kaiser wrote:
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
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
Jeroen Habraken wrote throw the better.
Well, no one argues that "the simpler a solution the better" and we all want "simple solutions to complex problems" and "more pay for less work", etc. Unfortunately, it is not always the case, is it? Surely, you are not saying that you won't accept a moderate solution to your complex problem if you can't find a simple one.
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.
Yes, your deployment case (of a library developer) makes sense to me. Unfortunately, my deployment cases (of an application developer) are quite different. In my neck of woods reading invalid configuration parameter and abandoning the ship with an exception is, well, an overkill (put very mildly). What I am driving at is that we have wildly differing deployment scenarios. And IMO a library (generic solution) should be able to cater for them all. The conundrum a conversion-related library in general is inevitably facing (and seemingly cannot solve... as we have none so far) is that every single user comes in and says -- my deployment use-case is dead-simple; why should I deploy that "complicated" API instead? IMO that "small-town" thinking has no place in commercial s/w development where modularization and generic solutions are quite important... even probably at the expense of efficiency, etc. Please do not take it as an insult. It's not directed at you or anyone in particular. It's merely my experience in managing considerable projects with finite budgets and rigid timeframes and revolving staff and real customers breathing down your neck for a reliably working solution.
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.
With all due respect this reminds me of the situation during review#1 when the Spirit team was very vocal and uniform (and did not mince their words) in their rejection of the proposal siting that Spirit was opening up unheard-of horizons in the conversion domain. Feel free to call me a dimwit but I personally find Spirit's syntax arcane and using your words "more verbose than absolutely needed" for my conversion purposes. Three years down the track and we are here with you expressing love for the library (Version: 0.2, Last upload: 2010 October 07) that I cannot possibly responsibly use in our commercial development environment due to lack of penetration, reliable support, active development and adequate (for my purposes) flexibility, extensibility and functional-completeness. And, curiously, it does not seem to handle *my* mundane deployment pattern -- non-throwing failure-detection: optional<T> res = convert<T>(from, cnv); if (!res) { log("Invalid ignored. Proceeding with the default"); res = some_default; }
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.
Yes, I understand. Unfortunately, my experience of deploying lexical_cast and my own conversions tells me that in my case the simple deployment interface does not cut it.
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.
Here I am not sure I immediately see what penalties std::tr2::optional introduces. My impression and analysis of the code tells me otherwise. Special requirements (like high performance) require special solutions. Still, that fact did not kill or diminish in any way the existence of generic libraries. More so, during this review I was pleasantly surprised to see more people with similar to mine needs -- they value std::sstream-based converter the most, i.e. functional richness even if not blindly fast. Is "coerce" is the same league?
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.
There is the "Performance" section in the docs. The current design though is different so that "convert" itself has no performance metrics. It's all defined by the plugged-in converter. So, "convert" can perform as fast as "coerce" :-) when coerce-based converter is plugged-in.
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.
I did. I do not believe it covers all deployment scenarios. I can be wrong though. That said, I have to qualms stepping back and letting Jeroen to bring "coerce" back to life. I'll take someone else's work any time (with commercial-development-specifics caveats). Less work and hassle for me. Honestly, it'll be a relief. I am already exhausted just going through this 2 weeks review.
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.
From what I see it should be fairly straightforward to eliminate "convert" api. But then again, one has to walk the walk to judge responsibly... and I am not in optimization business.