PDA

View Full Version : QCanvasPolygonalItem delete and SegFault or pure virtual call



hayati
12th March 2008, 13:29
Hi to all,
I have list of QCanvasPolygonalItems which should be deleted when user wants to do so. As QT docs stated i used hide() in my derived class destructor then sometimes i started getting pure virtual func call errors or just segfaults. But after i dig into google about this problem, i found that it says that hide needs areaPoints() (i think it will be called sometime afterwards) which is pure virtual at base class. since virtual functions in c++ behave different than java and c#; using virtual functions is not recommended at constructors and destructors. But obviously i only used hide() in my destructor. And QT says hide() must be called. How can i delete preventing pure virtual func called or segfault. Exactly same problem was reported to trolltech at the below links:

http://lists.trolltech.com/qt-interest/2003-03/msg00692.html

as stated before problem appears when i try to delete qcanvaspolygonalitems.

Note: i also tried to call hide() before deleting the object. but this didn't solve the problem.

hayati
12th March 2008, 16:38
some coders faced to the same problem used a technique which is not to delete the object but using a recycle-bin system to handle these objects.
but obviously this is a "how to overcome this problem with an ugly way" and it also contains "memory leak".

wysota
12th March 2008, 18:02
Can you provide a minimal compilable example reproducing the problem?

hayati
12th March 2008, 20:32
a sample demostration is like below


class Cell : public QCanvasPolygonalItem {
//...
}


Cell::Cell(QCanvas* canvas)
: QCanvasPolygonalItem(canvas)
{
setBrush( gray );
}

Cell::~ Cell()
{
hide();
}

QPointArray Cell::areaPoints() const
{
QPointArray a(4);

//getCellWidth() and getCellHeight() returns an int value depended to width and height of the canvasitem and they are not virtual
a.setPoint(0, QPoint( (int)x(), (int)y() ) );
a.setPoint(1, QPoint( (int)x() + getCellWidth(), (int)y() ) );
a.setPoint(2, QPoint( (int)x() + getCellWidth(), (int)y() + getCellHeight() ) );
a.setPoint(3, QPoint( (int)x(), (int)y() + getCellHeight() ) );

return a;
}


// at the gui class deletion occurs as below
// m_cell is type of std::vector<Cell*>
void GUI::deleteCell(Cell* c)
{
for( vector<Cell*>::iterator it = m_cells.begin(); it != m_cells.end(); it++){
if ((*it) == c) { // check if it's what we seek
m_cells.erase(it);
break;
}
}
delete c;
c = NULL;

m_canvas->setAllChanged();
m_canvas->update();
}


when i run the program in gdb or valgrind pure virtual func call or segfault caught in:

#0 0x4044132c in QCanvasItemList::drawUnique(QPainter&) () from /usr/lib/libqt.so.3
#1 0x40443dfb in QCanvas::drawCanvasArea(QRect const&, QPainter*, bool) ()
from /usr/lib/libqt.so.3
#2 0x4044368b in QCanvas::drawChanges(QRect const&) () from /usr/lib/libqt.so.3
#3 0x40443196 in QCanvas::update() () from /usr/lib/libqt.so.3
#4 0x404426cc in QCanvas::advance() () from /usr/lib/libqt.so.3
#5 0x40527cf4 in QCanvas::qt_invoke(int, QUObject*) () from /usr/lib/libqt.so.3
#6 0x402a302a in QObject::activate_signal(QConnectionList*, QUObject*) () from /usr/lib/libqt.so.3
#7 0x402a2ee7 in QObject::activate_signal(int) () from /usr/lib/libqt.so.3
#8 0x40505314 in QTimer::timeout() () from /usr/lib/libqt.so.3
#9 0x402bf3bf in QTimer::event(QEvent*) () from /usr/lib/libqt.so.3
#10 0x4024f2c6 in QApplication::internalNotify(QObject*, QEvent*) () from /usr/lib/libqt.so.3
#11 0x4024ef43 in QApplication::notify(QObject*, QEvent*) () from /usr/lib/libqt.so.3
#12 0x4022e2f3 in QEventLoop::activateTimers() () from /usr/lib/libqt.so.3
#13 0x4020f27d in QEventLoop::processEvents(unsigned) () from /usr/lib/libqt.so.3
#14 0x402606cb in QEventLoop::enterLoop() () from /usr/lib/libqt.so.3
#15 0x40260588 in QEventLoop::exec() () from /usr/lib/libqt.so.3
#16 0x4024f4c0 in QApplication::exec() () from /usr/lib/libqt.so.3

so if i understand correct, it crashes after

m_canvas->update()
but same post clearly points that it's because a pure virtual func call. (which is done in QCanvas::update() i think, but i'm not sure off course)

wysota
12th March 2008, 20:35
How about something compilable? :)

hayati
12th March 2008, 23:01
opps i misunderstood you, sorry..
here is what you want.

i managed to generate same error :)
but i realized that this happens (segfault or pure virtual func call) when size of QCanvasPolygonalItem is decreased. Otherwise it work flawlessly.
i used 3 very little class to build a user interface and the error engine :)

i had the same gdb output shown below:

#1 0xb7c2b26d in QCanvasItemList::drawUnique () from /usr/lib/libqt-mt.so.3
#2 0xb7c3179c in QCanvas::drawCanvasArea () from /usr/lib/libqt-mt.so.3
#3 0xb7c3296a in QCanvas::drawChanges () from /usr/lib/libqt-mt.so.3
#4 0xb7c33876 in QCanvas::update () from /usr/lib/libqt-mt.so.3
#5 0x0804e825 in GUI::deleteCell ()
#6 0x0804fea8 in GUI::qt_invoke ()
#7 0xb79f9893 in QObject::activate_signal () from /usr/lib/libqt-mt.so.3
#8 0xb79fa338 in QObject::activate_signal () from /usr/lib/libqt-mt.so.3
#9 0xb7d8e907 in QButton::clicked () from /usr/lib/libqt-mt.so.3
#10 0xb7a97f8c in QButton::mouseReleaseEvent () from /usr/lib/libqt-mt.so.3
#11 0xb7a30681 in QWidget::event () from /usr/lib/libqt-mt.so.3
#12 0xb7990af0 in QApplication::internalNotify () from /usr/lib/libqt-mt.so.3
#13 0xb7992cae in QApplication::notify () from /usr/lib/libqt-mt.so.3
#14 0xb792327d in QApplication::sendSpontaneousEvent ()
from /usr/lib/libqt-mt.so.3
#15 0xb7921ee2 in QETWidget::translateMouseEvent () from /usr/lib/libqt-mt.so.3
#16 0xb791ffcc in QApplication::x11ProcessEvent () from /usr/lib/libqt-mt.so.3
#17 0xb79371a4 in QEventLoop::processEvents () from /usr/lib/libqt-mt.so.3
#18 0xb79ab1ce in QEventLoop::enterLoop () from /usr/lib/libqt-mt.so.3
#19 0xb79aafde in QEventLoop::exec () from /usr/lib/libqt-mt.so.3
#20 0xb7992699 in QApplication::exec () from /usr/lib/libqt-mt.so.3
#21 0x0804e7af in main ()

wysota
12th March 2008, 23:53
The problem is not related to deleting the item. Pure virtual calls have nothing to do with it. The problem is with refreshing the canvas after the item got deleted.

hayati
13th March 2008, 06:12
yes i also think so; but thread at trolltech, which i gave the link at first post, states that it is because of pure virtual funcs. Another fact supporting this is sometimes i really get pure virtual func errors. but both points to the same place = QCanvas::update()


QCanvaspolygonalItem::hide() accesses areaPoints(), which is virtual.
hide() is called from the QCanvasItem destructor if the item is still
visible by the time that destructor fires. Calling a virtual in a
destructor is not legal (more precisely, it is not late bound, so the
pure-virtual is called and "undefined" behavior results), so the runtime
explodes. This behaves very differently depending on the
compiler/platform. Sometimes you get a relatively clear "pure virtual
method call" message (with no mention of *which* method, of course). Other
times, the app segfaults. This is actually mentioned in the docs, but is
easy to miss/forget.

As a result what are we supposed to do next?

wysota
13th March 2008, 08:35
yes i also think so; but thread at trolltech, which i gave the link at first post, states that it is because of pure virtual funcs.
But it doesn't state you have the same probalem.


Another fact supporting this is sometimes i really get pure virtual func errors. but both points to the same place = QCanvas::update()

Run the application under a debugger and when it crashes, see the backtrace.


As a result what are we supposed to do next?

Correct your code :)

hayati
13th March 2008, 11:05
ok, thanks for reply;
but i can't figure out what is wrong with my little tiny code. (and this code is absolutely written to simulate the error)
i don't mean find my errors; i mean why qt doesn't generate pure virtual call errors or segfault when i increase the Cell objects size (width or height) but if i decrease its width or height then it crushes. please check this out in provided source file, i stated the situation in comments.
so what is wrong with this code (that leads to this error) could anybody explain me?
moreover; i have nothing to do with QCanvas::update()
procedure is quite simple shrink(decrease the size of) the object, delete the object and QCanvas::update() func call and it crushes.
remember when you increase its size there is no problem! only when you decrease this happens.

wysota
13th March 2008, 14:12
i don't mean find my errors;
I won't - I haven't used Qt3 for about two years now.


i mean why qt doesn't generate pure virtual call errors or segfault when i increase the Cell objects size (width or height) but if i decrease its width or height then it crushes.
For me it generates regular segmentation faults on updating the canvas view... I can delete the item without a segfault and then when I force a refresh it crashes. So obviously it's not a problem with an item that got deleted a few seconds earlier.


i have nothing to do with QCanvas::update()

Really? And what is that?

void GUI::deleteCell()
{
// just delete first item and don't mind others
Cell* c;
for( std::vector<Cell*>::iterator it = cells.begin(); it != cells.end(); it++ ){
c = *it;
cells.erase(it);
break;
}
delete c;
c = NULL;

canvas->setAllChanged();
canvas->update(); // <======== What is that?
}


remember when you increase its size there is no problem! only when you decrease this happens.

And that proves what? That something is calling pure virtual methods in the destructor? Somehow I don't see that and the debugger seems to be taking my side and not yours. Did you have a look at the backtrace? Remember to remove the "hide()" call from the destructor (although it's probably perfectly valid there) and move it to GUI::deleteCell() "just in case".

hayati
13th March 2008, 15:03
First of all;
you may be right.
but this doesn't solves the problem just changes the title of the thread :)
in either way program crushes.

and QCanvas::update() HAS TO BE CALLED in order to reflect changes, what can be done?
we both have the same code and both are crushing.
as stated before, crush scnerio is simple;
decrease the size, delete and update the canvas
is it really a bug? or what is wrong with it?

Note: i tried to remove hide() before delete item. but this didn't help.

wysota
13th March 2008, 15:14
First of all;
you may be right.
but this doesn't solves the problem just changes the title of the thread :)
It does, because you'll be looking in the right direction instead of chasing a ghost.


and QCanvas::update() HAS TO BE CALLED in order to reflect changes, what can be done?
The proper question is "why does it crash?".


Is it really a bug? or what is wrong with it?
If I were to guess, I'd say your item class is invalid. It's quite easy to check it out - simply substitute it with QCanvasRectangle and see if it still crashes.

hayati
13th March 2008, 16:18
yes if i use QCanvasRectangle then it works great. but it doesn't meet my requirements ofcourse. (since my drawing is more complicated. that's why i used QCanvasPolygonalItem as base class)
what is being done wrong? hmm i'll check QCanvasRectangle implementation i guess.

i'll back to inform if i fail again.

thank you very much.

wysota
13th March 2008, 16:36
If I were to guess again, I'd say your areaPoints() implementation was incorrect.

hayati
13th March 2008, 17:06
no areaPoints() is pretty good.
it's inner undocumented problem :(
i've just checked source code of qcanvas.cpp

and surprise appears.
but thanks to qtcentre.org which leads to me to correct answer.

at qtcentre.org similar (maybe exactly same) problem was posted before.
post ends like below:



Hi

It has been fixed. Horray. A nice chap at Qt support saw the problem and luckily my example had made the problem worst. Anyway here it is:

// Note: if you reimplement the boundingRect() function
//of your QCanvasRectangle subclass and change the values this function
//returns while the item is visible, sure to call QCanvasPolygonalItem::invalidate().
// If you do not the QCanvas will use one bounding rectangle when adding the
// item to the chunks and a different one
//when removing, causing the internal list of items to still contain your
//item after it has been hidden. When the item is deleted, QCanvas still
//believes that the item is visible and uses the pointer and thus your
//program crashes. You will need to call
//invalidate() on the item if you do change it.

I did not realise from the Qt documentation overriding the boundRect() and changing the size would have this effect.

Cya Illya

I have seen the same problems about removing properly the QCanvasItem from the QCanvas. But I did not see the satisfying answer.

I have faced with the same problem. After two days, we found and solved the result as in the following.

When we try to delete the any items from the QCanvas by using the delete statement, we are facing the problem with resize or some thing like that which causes to call the update function of the QCanvas. In detail, it call the drawUnique() function. Anyway, althoug we delete the QCanvasItem from QCanvas, it stores another list which is a memeber of QCanvasChunk. That is, QCanvas uses another list in order to improve the its performance. It uses chunks which displays the changed items in the QCanvas. Therfore, we have to clear this list as well.

In order to do that, First, we can put setVisible(false) or hide() or invalidate() functions at the begining of the overloaded areaPoints() function. Then, we can add setVisible(true) or show() or update() functions at the end of the areaPoints() function.


setVisible() (or the others listed above) call to removeFromChunks() or addChunks() function. So any action which causes to call update function can not reason any conflists between the removed QCanvasItems and the internal list ( the list member of QCanvasChunk ) .

I think it should be a function of QCanvas which clear the chunks as well and it should be used in the destructor like hide as documented.

Best regards
Serhat


and QCanvasRectangle source code was same to my Cell class except below:


void QCanvasRectangle::setSize(int width, int height)
{
if ( w != width || h != height ) {
removeFromChunks(); // ATTENTION HERE
w = width;
h = height;
addToChunks(); // ATTENTION HERE
}
}


which supports post of Serhat.

So now, there is no way to reach these functions (removeFromChunks() and addToChunks()) but as post says there is way to accomplish this via other functions.

Addition to post there is no way to trigger these two functions in areaPoints() because areaPoints() is const.

i'll check and report test results to you.
Guess what?
now it makes sense about pure virtual func call. because it has reference to base class(via chunk list). but real class (derived obj) is deleted. so pure virtual call happens here.


thanks for interest

Hayati
Best Regards

hayati
13th March 2008, 17:46
problem solved.
invalide() was like below in "segfault caught code"


void Cell::setCellWidth(int w)
{
m_width = w;
invalidate();
update();
}

void Cell::setCellHeight(int h)
{
m_height = h;
invalidate();
update();
}

but at the working one;


void Cell::setCellWidth(int w)
{
invalidate();
m_width = w;
update();
}

void Cell::setCellHeight(int h)
{
invalidate();
m_height = h;
update();
}

invalidate() clears out the chunck and update() adds item the chunck. but they should be at the correct order. ie. invalidate() should be first statement of size changing function and update() should be very last (in case a return) statement.

It's qt's undocumented issue.

Hayati
Best Regards