PDA

View Full Version : regarding qgraphicsitem_cast



Gopala Krishna
23rd December 2006, 08:18
Hello guys,
I am facing some problems with qgraphicsitem_cast which casts in a wrong manner resulting in crashes.
Because of this now I am forced to check manually the value of type() function and then cast. :(
I am using Qt4.2.2 on linux.

The following working example illustrates my problem.
Is there something wrong in my code or is it a Qt bug ?? :eek:


#include <QtGui>

class Item : public QGraphicsItem
{
public:
enum ItemTypes {
ItemType = UserType+5,
ComponentType,
WireType,
};

Item() : QGraphicsItem(0,0) { }

QRectF boundingRect() const { return QRectF(-20,-20,40,40); }

void paint(QPainter *p,const QStyleOptionGraphicsItem *, QWidget *)
{
p->drawLine(-20,-20,20,20);
}
int type() const { return ItemType; }
};

class Component : public Item
{
public:
int type() const { return Item::ComponentType; }
};

class Resistor : public Component
{
};


class Wire : public Component
{
public:
int type() const { return Item::WireType; }
};

int main(int argc,char *argv[])
{
QApplication app(argc,argv);
QGraphicsScene sc(0,0,500,500);

Resistor * r= new Resistor();

sc.addItem(r);
r->setPos(50,50);
QGraphicsView v;
v.setScene(&sc);
v.show();

// The problematic part
Wire *w = qgraphicsitem_cast<Wire*>(r);
if(w != 0l) // this always happens
{
qDebug("WRONGLY CASTED");
Q_ASSERT(0);
}

return app.exec();
};


I checked the source of qgraphicsitem_cast but I couldn't understand how it works. Can somebody enligten me ?


template <class T> inline T qgraphicsitem_cast(QGraphicsItem *item)
{
return int(static_cast<T>(0)->Type) == int(QGraphicsItem::Type)
|| (item && int(static_cast<T>(0)->Type) == item->type()) ? static_cast<T>(item) : 0;
}

Gopala Krishna
23rd December 2006, 08:37
Sorry for the above post. I didn't read the documentation properly. Now i understand that "Type" enum should be declared in custom class to point to custom value.

Well anyway I still have doubt about the second question I asked.

wysota
23rd December 2006, 09:41
First you have:

Resistor * r= new Resistor();
and then:

Wire *w = qgraphicsitem_cast<Wire*>(r);
You're trying to cast a Resistor object to a Wire object, but Resistor doesn't inherit Wire, thus the cast returns 0 and you're dereferencing a null pointer, so the branch is triggered.

Gopala Krishna
23rd December 2006, 12:20
You're trying to cast a Resistor object to a Wire object, but Resistor doesn't inherit Wire, thus the cast returns 0 and you're dereferencing a null pointer, so the branch is triggered.

Thanks for the reply.
Sorry, i think you got it otherway. (it was if(w != 0l))
Actually I wanted the cast to fail but it didn't used to since I hadn't declared Type to point to return value of corresponding type() in my subclasses.
This resulted in cast from Resistor to Wire succesful causing crashes and I not having understood the doc well posted an example to show that there was bug in qt !! (how dumb of me)
Also I couldn't understand the source of qgraphicsitem_cast.

wysota
23rd December 2006, 13:23
Sorry, i think you got it otherway. (it was if(w != 0l))
The "l" mislead me. In C++ you don't have to cast nulls to long so the "l" is not necessary.


Actually I wanted the cast to fail but it didn't used to since I hadn't declared Type to point to return value of corresponding type() in my subclasses.
Yes, the Resistor class lacks the type() method and the Type is not set correctly.


Also I couldn't understand the source of qgraphicsitem_cast.
This is quite easy once you know what static_cast<T>(0)->Type means.

This is the same as T::Type (the static member Type of class T). So essentially what the cast does is that it checks whether you're not casting the pure abstract class QGraphicsItem and that the Type member of the target class matches the type value returned by the source object.

ggrinder
23rd December 2006, 13:27
I checked the source of qgraphicsitem_cast but I couldn't understand how it works. Can somebody enligten me ?


I'll give it a try.

At first one could rewrite the ternary conditional to an if-then-else clause, it would look like:


if( int(static_cast<T>(0)->Type) == int(QGraphicsItem::Type) )
return static_cast<T>(item);
else if( item && int(static_cast<T>(0)->Type) == item->type()
return static_cast<T>(item);
else
return 0;



I think the most interesting part of it is the


int(static_cast<T>(0)->Type)

expression. It blesses/casts a 0 to type T. This ensures that T is a pointer. Basically one can give any type to the template but if it is not a pointer this cast would fail. Than the member Type is requested from it. This is okay cause Type behaves like a static member of class *T.

Then again the value of Type is casted to int this is done so it can compared with the other Type vars, as you know normally members of different enum can't be compared.

So the first comparison


int(static_cast<T>(0)->Type) == int(QGraphicsItem::Type)

simply checks if the Type of T is equal to the Type of the basic QGraphicsItem.
If thats the case item will be casted to T and returned.

If thats not the case


(item && int(static_cast<T>(0)->Type) == item->type())

will be evaluated. It checks if item is a NULL pointer if thats the case the evaluation fails, else it checks if the type of item is equal to the type of the target for the cast operation, IOW if the cast is possible. If thats the case the cast operation is performed if not the whole cast fails.

Please correct me if I'm mistaken somewhere.

Gopala Krishna
23rd December 2006, 15:09
Thanks wysota and ggrinder for explaining me. Now I know how it works. This really helped learn an important c++ concept.
Going through qt source helps you fine tune your basic skills too. Thanks to trolls and all those who contributed to qt. :)