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