[Development] Improving toInt, toLong, etc.
Matthew Woehlke
mw_triad at users.sourceforge.net
Tue Feb 4 19:37:44 CET 2014
On 2014-02-03 19:26, Thiago Macieira wrote:
> Em seg 03 fev 2014, às 15:29:06, Matthew Woehlke escreveu:
>> If I were designing such an API from scratch, I'd probably these days go
>> with something like the std::expected approach. Bonus points for
>> implicitly converting to the value type.
>>
>> Then you could write e.g.:
>>
>> auto const result = s.toInt(); // Other params (e.g. base) elided
>> if (result.isOkay())
>> use(result.value()); // '.value()' optional
>>
>> - or -
>>
>> // Can use value directly, as before
>> use(s.toInt());
>>
>> - or -
>>
>> // Use default value on error
>> use(s.toInt().value(default));
>
> Sounds good, but should I then create a QExpected class? And how should I make
> it differ from QOptional?
I'm leaning more now toward having a separate (template) class for
numeric conversions. I would strongly look into having that class
*subclass* (or at least have as a member) QOptional, however. (But also
see rest of my reply, especially the last two paragraphs.)
You talked about returning information about how many characters were
processed; presumably the return class would contain that information as
well as the value. It may also contain extended error information (the
ISO proposed version definitely would, but I could see Qt omitting
that... up to you).
> Since we won't have exceptions in our QOptional, I don't think we need two
> classes. One would suffice. And then we can add it to our containers too, but
> probably only for Qt 6 due to BIC issues (QVector on MSVC).
Yeah, I'm not entirely on board with the whole "check the result or your
program dies" idea. Qt doesn't do that now; I definitely don't feel like
it needs to be added. (I guess QOptional will still assert though if you
call value() and it is disengaged? Just like a container would when
using operator[] on a non-existing index?)
>> - Should toInt() also take a default value?
>
> Either it takes a default value or it returns a QOptional. Not both.
Agreed. Probably I would go with the QOptional (subclass), and use
whatever is QOptional's equivalent of value_or() to implement default
values.
>> - operator bool could be ambiguous for toBool (though I think Qt doesn't
>> have this (currently)?), so I went with an explicit isOkay() to test the
>> result. Should there be an operator bool -> isOkay() for toX, x != Bool?
>
> We don't have toBool(). We'd probably name isOkay() instead isValid(). We
> should probably also adopt std::optional names and add an isEngaged().
Yes, for QOptional you wouldn't want to name it 'isOkay()'. But you
*will* have the problem of ambiguous implicit conversion with
QOptional<bool>, which I assume will be allowed even if there is no
QString::toBool(). Alternatively you could omit implicit value
conversion entirely and require to use either value() or operator*()
(hmm, now I wonder if the std::optional proposal had an operator*()...
it is almost as good as an implicit conversion to the value type, but
without ambiguity problems, and slightly more explicit while still being
much less typing than ".value()".)
Other names WFM.
>> Do we need that for e.g. 'if (auto const result = s.toInt())'?
>> Alternatively, the implicit conversion could be omitted and require the
>> user to use .value().
>
> In the above, result is a QOptional<int>. The question is what use you make of
> it.
The point was, the above expression only makes sense if there is an
operator bool() -> isValid().
I hadn't thought of it before; now that I have, I'm strongly leaning
toward operator bool() -> isValid() and operator*() -> value().
>> As an added benefit, isOkay() could take parameters or have alternate,
>> similar methods (isComplete()?) to clarify what "okay" means (e.g. did
>> it consume the entire input?). However it may be better to specify that
>> as an input parameter. (Unrelated: it's sure nice in this context to
>> have a flags class :-)...)
>
> That makes it too complex for QOptional or QExpected. Instead, toIntegral()
> should have extra parameters.
Yes, we agree :-).
> The task I have is to add the endposition output parameter. If that pointer is
> non-null, it will indicate the position (index) of where the conversion ended,
> so you can resume the parsing. If it is null, we fall back to the current
> behaviour: fail if endposition != length().
That could work...
Pros:
- makes implicit whether to allow trailing text
- return can be QOptional
Cons:
- it's an output parameter
I would at least consider, if you haven't, taking a QFlags parsing
options for e.g. if trailing text is allowed, if whitespace should be
ignored, and anything else that makes sense. The "endpos" could be a
member of the return type.
> That means the front-end function would be:
>
> template <typename T> QOptional<T> toIntegral(int base = 10, int *endpos = 0)
> // plus the enableif integral type
Consider 'toNumber' so that it can take either integral or floating
point types?
--
Matthew
More information about the Development
mailing list