[Development] Improving toInt, toLong, etc.

Thiago Macieira thiago.macieira at intel.com
Tue Feb 4 21:07:22 CET 2014


Em ter 04 fev 2014, às 13:37:44, Matthew Woehlke escreveu:
> > 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).

I don't like the idea of a separate class for numeric conversions only. I also 
don't like the idea of returning the end pointer in a return argument. I 
prefer it as an output parameter: the end pointer will be used after the 
result is checked and potentially used.

> > 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?)

There are two possibilities:
1) operator T() asserts if it's disengaged
2) operator T() returns a default-constructed type if it's disengaged

Or we don't have an operator T() at all. A T &operator*() might be better, 
which must assert. I haven't looked at the std::optional proposal yet.

I'd like to keep value(const T &invalidAs) never asserting, like other methods 
work already.

> 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.

operator bool() implies T &operator*(). operator T() implies no operator 
bool().

So the two options are:
	QString s, t, u;
1) automatic casting:
	QOptional<int> value = s.toInt();
	if (value.isValid())
		use(value, t.toInt(), u.toInt().value(42));

2) pointer-like behaviour:
	QOptional<int> value = s.toInt();
	if (value)
		use(*value, *t.toInt(), u.toInt().value(42));
	// also implies:
	*value = 1;

> > 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

Unfortunately, upon reading (and updating) qstrtoll / qstrtoull, it looks like 
the conversion can fail but still move endptr. This is the comment in 
qlocale.cpp:

            // the only way qstrtoll can fail with *endptr != '\0' on a non-
empty
            // input string is overflow
            *overflow = *endptr != '\0';

Looking at the source code, it matches an error return of ERANGE. I'm inclined 
to make the Qt classes keep the current behaviour: failure to parse, so they 
don't consume *any* characters, even if they're all properly-formed digits.

> 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.

As I said above, I don't want to make it part of the return type for the same 
reason I don't want flags: I don't want to make a complex number-parser class. 
We're not in the business of supporting time spans ranging from 1e-18 to 1e18 
with one class. We just need to do what's reasonable.

> > 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?

Floating point doesn't have a base, so it's a different type. I can add a 
toNumber() whose integral specialisations call toIntegral(), though.

-- 
Thiago Macieira - thiago.macieira (AT) intel.com
  Software Architect - Intel Open Source Technology Center




More information about the Development mailing list