[Development] QOptional
Julien Blanc
julien.blanc at nmc-company.com
Thu Aug 21 14:41:14 CEST 2014
On 21/08/2014 13:01, Poenitz Andre wrote:
> Julien Blanc wrote:
>> There are two strong arguments in favor of optional :
>> - real life tends to show that the optional &ok parameter is often
>> missed in context it should not, leading to hard to find bugs.
>> - optional is much more friendly to static verification than &ok (dependancy between variables is a mess to check).
>>
> I am actually not quite sure what you mean with the second item.
I’m talking about what tools like coverity / polyspace can verify on the
code. Checking that an optional is not used when it is not tested for
nullness is doable (it is a lot like static checking against null
pointers dereference), whereas checking for another variable is beyond
what current static checkers can do afaik.
>
> Something like the following?
>
> struct S { int m_a, int m_b, int m_c);
>
> // "Tradtional, wrong result"
> bool S::parse(QString a, QString b, QString c)
> {
> bool ok;
> m_a = a.toInt(&ok);
> m_b = b.toInt(&ok); // Oops, overwrites first ok value.
> m_c = c.toInt(&ok);
> return ok;
> }
I wasn’t thinking about this one, but that’s an issue, yes.
>
> vs
>
> // "Tradtional, right result"
> bool S::parse(QString a, QString b, QString c)
> {
> bool oa, ob, oc;
> m_a = a.toInt(&oa);
> m_b = b.toInt(&ob);
> m_c = c.toInt(&oc);
> return oa && ob && oc;
> }
I would not recommend writing code like that (although i think it’s
widespread). I would expect that parse, when failing, does not alter the
object at all, so that it stays in a corerent state (aka
“strong-guarantee”). This is not the case with such implementation.
I would write it that way :
bool S::parse(QString a, QString b, QString c)
{
int tmpa, tmpb, tmpc;
bool oa, ob, oc;
tmpa = a.toInt(&oa);
tmpb = b.toInt(&ob);
tmpc = c.toInt(&oc);
if(oa && ob && oc)
{
m_a = tmpa;
m_b = tmpb;
m_c = tmpc;
return true;
}
return false;
}
>
> vs
>
> // "Optional"
> bool S::parse(QString a, QString b, QString c)
> {
> auto oa = a.toInt();
> auto ob = b.toInt();
> auto oc = c.toInt();
> m_a = *oa;
> m_b = *ob;
> m_c = *oc;
> return oa && ob && oc;
This one is obviously wrong and would trigger an undefined behaviour if
any string is not convertible to int. The good thing is that I expect
smart static verification tools to tell you this. To fix it, I would
write it that way :
bool S::parse(QString a, QString b, QString c)
{
auto oa = a.toInt();
auto ob = b.toInt();
auto oc = c.toInt();
if(oa && ob && oc)
{
m_a = *oa;
m_b = *ob;
m_c = *oc;
return true;
}
return false;
}
Note that we gained strong guarantee in the process without even aiming
for it, at no line of code cost.
>
> vs
>
> // "isInt"
> bool S::parse(QString a, QString b, QString c)
> {
> bool oa = a.isInt(&m_a);
> bool ob = b.isInt(&m_b);
> bool oc = c.isInt(&m_c);
> return oa && ob && oc;
> }
>
> vs
>
> // "something else"
> bool S::parse(QString a, QString b, QString c)
> {
> return a.isInt(&m_a) && b.isInt(&m_b) && c.isInt(&m_c);
> }
Those versions have the same “no strong-guarantee” problem.
> If so, the "Optional" variant doesn't look much simpler than any
> of the others, even less so when one doesn't accept the use of
> 'auto' there.
I never advocated that Optional makes for shorter code :) (yup, I know
others have, but i disagree with that statement). However, i think
Optional is a mean for *better* code. Your examples confort me in this
thinking : with Optional, strong-guarantee is achieved within the same
code amount. Without, it requires at least twice as more code than the
unsafe version.
Regards,
Julien
More information about the Development
mailing list