Boost logo

Boost :

Subject: Re: [boost] [convert] Default Syntax Idea
From: Hartmut Kaiser (hartmut.kaiser_at_[hidden])
Date: 2009-02-25 10:09:56


> > From: <Vladimir.Batov_at_[hidden]>
> >> From: "Stewart, Robert" <Robert.Stewart_at_[hidden]>
> >> I was thinking a bit about syntax for the default value for the
> >> notional boost::convert() and kin. How about the following:
> >>
> >> int const i(3);
> >> string const s("something");
> >> int const j(convert<int>(s) | 5);
> >
> > Yes, this idea is similar to
> >
> > int j = convert<int>(s).or_default(5);
> >
> > but does not introduce new vocabulary (my main hesitation). With the
> > suggested op|, as I am reading it, it makes perfect reading just like
> I'd
> > read English. And that is usually my criterion for an interface. So,
> I am
> > voting 'yes'... in principle.
>
> Now I am not so sure. In isolation it looks good:
>
> int j = convert_to<int>(s) | 5;
>

IMHO, operator|() just has the wrong precedence for this kind of syntax as,
most of the time, it needs to be put in parenthesis's.

> What happens when we start mixing with
>
> int j = convert_to<int>(s) | 5 >> std::hex;
> int j = convert_to<int>(s) | 5 >> boost::dothow >> std::hex;
> or even
> int j = convert_to<int>(s) >> boost::dothow >> std::hex | 5;

Some additional remarks.

1) Streams have proved to be horrible at formatting. Why stick with this
scheme? Why not invent something more flexible like:

    int j = convert_to<hex>(s);

?

2) What about more complex conversions like:

    int i, j;
    convert(int_ >> ", " >> int_, "1,2", tie(i, j));
    assert(i == 1 && j == 2);

which brings us directly into the domain of Spirit (and, if you change
convert for parse, you'll almost get a valid spirit expression, btw). So
here is my question again. What's the point in duplicating Spirit's
functionality?

I understand that you strive for a simpler syntax for primitive conversion
operations. So, let's think about how to put this simple API on top of
Spirit, covering the primitive conversion, but having a well defined
migration path towards more complex conversion needs.

Converting a simple integer in Spirit is a bit verbose:

    int i;
    if (parse(int_, "1", i))
        // ... succeeded

But OTOH, this nicely integrates with a convert() function to be put on top
of it:

    template <typename T>
    struct convert_to;

    template <>
    struct convert_to<int>
    {
        static int call(char const* p)
        {
            int i;
            if (spirit::qi::parse(int_, p, i))
               return i;
            boost::throw_exception(...);
        }
    };

    template <typename T>
    T convert(char const* p)
    {
        return convert_to<T>::call(p);
    }

    int i = convert<int>("1");

You get the idea.

3) What about leading/trailing whitespace in the strings to convert?

4) Default values should be directly associated with the conversion
operation in question, any of the syntax' above is misleading. Either have
default values as a separate parameter to convert(), or use the explicit
syntax proposed before: convert().default(...).

5) Throw/nothrow semantics are _not_ orthogonal to default values, those are
directly depending on each other. I.e. if I specify a default value, I don't
want to get an exception. The opposite is true as well, if I do want to get
an exception, I probably won't specify a default value.

But everything is just IMHO...
Regards Hartmut


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk