[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