I'm currently working on a project, more precisely a tangram game.
I have a problem with a segfault and I don't understand why.
Given that I have a whole project, I will try to simplify the problem :
I have a GameManager class that contains in particular a Menu object (and other things, but I don't think that is important. The gameManager is used to inititialize this object and manage it.
The Menu contains a vector of Button(each button has a lambda to perform an action when the user click on it).
std::vector<std::unique_ptr<Button>> buttons;
To illustrate how it works I will take an example : if the user clicks on the "Load" button, the gameManager delete the current Buttons contained in the menu and add new buttons in that menu.
void GameManager::initMainMenuButtons() {
...
menu -> addButton(std::unique_ptr<Button>(new Button(x1, y1, x2, y2, "Create",
[this]{
std::cout << "Create level" << std::endl;
menu->clear()
initCreateLevelButtons();
actionManager->setMenu(menu);
}
)));
...
}
In that code sample, I have a method initMainMenuButtons that adds several buttons in the menu , like "Load" or "Quit".
When the user clicks on "Create", I want to change the interface (adding and deleting buttons). So, to delete the buttons, i call the method clear()
void Menu::clear() {
buttons.clear();
decorationPieces.clear(); // not interesting
}
I'm using unique_ptr, thus, I don't have to delete the buttons mannualy.
So far, no problem : the vector of buttons seems to be empty (size is 0).
Next, the method initCreateLevelButtons() is called. This method is very similar to initMainMenu : it adds buttons in the menu, nothing else. During this call, the buttons seems to be correctly added in the vector, I printed the content of the vector at the end and the vector contains the correct buttons.
And there, the problem appears : after the call of initCreateLevelButtons(), there is a segfault when i want to use the menu, so, actionManager->setMenu(menu); doesn't work. I tried to print the menu std::cout << menu << std::endl, and test if this pointer is nullptr, but it doesn't work either. I don't understand why the menu seems to be correct at the last line of initCreateLevelButtons() and becomes invalid just after.
If I doesn't clear the vector of buttons (the menu->clear instruction), the program works, but, the last buttons are still here).
I tried to use raw pointers and I notices that the program is able to clear the vector as long as the buttons are not deleted (If I add a loop to delete the buttons, the problem arises), so, I conclued that the probleme is the buttons deleting. I don't understanf why, I'm stuck.
I don't know if I explained it weel, because, as I have already said, the code is part of a whole project, it's hard to introduce classes without introduce other things.
if you want details or the complete code of methods, I can provide them.
menu sustains lifetime of some button
button sustain lifetime of lambda
when you click button lambda clears menu
menu destructor clears button, button clears lambda
lambda continues execution when it in fact has been already destroyed -> undefined behavior ends with a crash
Now question is: Do you own Button class?
If yes then the easiest way to fix it, is to invoke copy of lambda in the button.
When you call menu->clear() it calls buttons.clear().
When you call buttons.clear() it destroys all the elements of buttons.
When you destroy the unique_ptr for the "Create" button, it destroys the "Create" button.
I assume button's callback is a std::function. When the button is destroyed, so is the std::function.
When the std::function is destroyed, your callback lambda object ([this]{...}) is destroyed.
The this pointer inside a lambda is stored in the lambda object. So now the memory that held the this pointer has been deallocated.
Since actionManager is a member variable of GameManager, actionManager->setMenu(menu) is really this->actionManager->setMenu(menu) which crashes because it uses a dangling pointer.
One workaround is to put the button code in a function of GameManager (since the GameManager is not destroyed), and call that from the lambda. Then, it's okay if you destroy the button while inside that function. It's okay to destroy an object whose code is currently running, as long as you are careful to not access the object after it's destroyed! This is also okay with std::function. I.e.:
[this]{
// move the rest of the code to the CreateLevel function
this->CreateLevel();
// At this point the lambda has been destroyed, but it's not a problem
// because we don't do anything.
}
Related
I'm working on a diagram visualisation tool and I ran into an issue where my QGraphicsScene does not display a shared_ptr<DiagramItem> when a raw pointer obtained via .get() is passed to scene->addItem().
Subsequent check via scene->items() shows that my DiagramItem is not a part of the scene. My guess is that it got freed as the refcounter on the shared_ptr will be zero after leaving the scope of the testing function...
But that was the testing case. In my actual code I'm using a shared_ptr that I got from elsewhere and is definitely present in memory with a non-zero refcounter. I get the raw pointer of that and pass it to scene->addItem(). It is also not displayed, but this time it is present in scene->items(). So why is it not being drawn?
If I switch from using shared_ptr<DiagramItem> to DiagramItem* then the issue disappears and everything is displayed properly. But due to limitations from the rest of the project, I cannot easily abandon smart pointers here, nor do I want to.
Did I run into some kind of memory limitation or am I doing something wrong?
I already tried calling show() and update() on the item and increasing the scene size in case the item doesn't fit (it does). I also tried breakpointing the paint() method, but that one doesn't get called at all.
I found a possibly related question here where similar behaviour occurs due to the object going out of scope and being deallocated, but that doesn't seem to be the case with my actual DiagramItem.
class DiagramItem : public QGraphicsItem
{
...
}
//Create scene
auto scene = new QGraphicsScene(nullptr);
//Item is created OR obtained from elsewhere
auto item1 = std::make_shared<DiagramItem>(nullptr, QString("aaa"), true);
auto item2 = GetDiagramItem(...);
//Raw pointers get passed to addItem
scene->addItem(item1.get());
scene->addItem(item2.get());
//Item1 is not present at all (directly created DiagramItem)
//Item2 is present but invisible (DiagramItem passed from elsewhere)
//myItem gets Item2
auto myItem = scene->items()[0];
...
As per this stackoverflow question:
What is the correct way to programmatically quit an MFC application?
I am using AfxGetMainWnd()->PostMessage(WM_CLOSE,0,0); to exit an MFC program. (SDI, CFrameWnd containing a CSplitterWnd with two CFormViews)
As expected, this calls DestroyWindow().
The problem I am facing is that after the derived CFormView destruction, as per MSDN:
After calling DestroyWindow on a non-auto-cleanup object, the C++ object will still be around, but m_hWnd will be NULL. [MSDN]
Now the CView destructor is called and at the point it does the
CDocument::RemoveView()...
CDocument::UpdateFrameCounts()
it fails on the following assert: ASSERT(::IsWindow(pView->m_hWnd));
I checked and the m_hWnd is already set to NULL in the derived CView destructor called just before.
What am I doing wrong ?
EDIT:
Here is a chart illustrating why I want to send a WM_CLOSE message and not a WM_QUIT.
I think the answer lays in this MSDN Technical Note, but I can't figure it out.
EDIT 2:
The order that things get called:
1- AfxGetMainWnd()->PostMessage(WM_CLOSE,0,0);
2- Derived CFrameWnd::OnClose()
3- CFrameWnd::OnClose()
which calls CWinApp::CloseAllDocuments(BOOL bEndSession);
which calls CDocManager::CloseAllDocuments(BOOL bEndSession)
which calls CDocTemplate::CloseAllDocuments(BOOL)
which calls CDocument::OnCloseDocument()
Now, in this function
while (!m_viewList.IsEmpty())
{
// get frame attached to the view
CView* pView = (CView*)m_viewList.GetHead();
ASSERT_VALID(pView);
CFrameWnd* pFrame = pView->EnsureParentFrame();
// and close it
PreCloseFrame(pFrame);
pFrame->DestroyWindow();
// will destroy the view as well
}
So we see that CWnd::DestroyWindow() is called, so:
4- Derived CFormView destructor
5- CScrollView::~CScrollView()
6- CView::~CView()
which calls CDocument::RemoveView(CView* pView)
which calls CDocument::OnChangedViewList()
which calls CDocument::UpdateFrameCounts()
Which crashes here: ASSERT(::IsWindow(pView->m_hWnd));
because pView->m_hWnd is NULL...
EDIT 3:
I figured out what the problem was:
The destructor of the first view was deleting an uninitialized pointer, which is UB. This was making the destructor hang and never complete.
Usually, the destructor of the second view is only called upon completion of the first one. But in this case it was still being executed although the first one never completed.
Since the first view base class destructors were never called, this function was never called for the first view:
void CDocument::RemoveView(CView* pView)
{
ASSERT_VALID(pView);
ASSERT(pView->m_pDocument == this); // must be attached to us
m_viewList.RemoveAt(m_viewList.Find(pView));
pView->m_pDocument = NULL;
OnChangedViewList(); // must be the last thing done to the document
}
Where we can see that the view is removed from the m_viewList.
This means that when the second view destructor completes, in:
void CDocument::UpdateFrameCounts()
// assumes 1 doc per frame
{
// walk all frames of views (mark and sweep approach)
POSITION pos = GetFirstViewPosition();
while (pos != NULL)
{
...
The pos is supposed to be NULL, but it is not. Which lead to the crash.
I think the way you are closing the frame is not the issue there.
My guess is that you destroy one of the views by hand whereas you should let MFC delete them (you probably called DestroyWindow on one of them)
Call ::PostQuitMessage(0); to close the app.
The problem was resolved, see EDIT 3 in the question for the solution.
This may seem like a trivial question, or I may have misunderstood previous information/the research I've done so far.
But is it possible to have a object with a function (in C++) that can access all instances of its own type?
In the context of my usage. I wanted to have a Button class, whereby I could simply instantiate multiple Buttons but call to a function could call reference all buttons.
ButtonInstance.isMouseTargetting(cursorCoordinates);
Is this possible? If so is it efficient?
Or should I have the class which owns the Button instances call each instance to check if the mouse coordinates match up?
I'm under the impression you are looking for advice on how to design this.
In the context of my usage. I wanted to have a Button class, whereby I
could simply instantiate multiple Buttons but call to a function could
call reference all buttons.
You want to do this in a button container. A button is not a button container and in a GUI context you already have an established hirerarchy.
Or should I have the class which owns the Button instances call each
instance to check if the mouse coordinates match up?
Yes. You probably already have a window/container class for this.
Your question is more of about Design pattern than C++ itself. Take a look at the Gang of Four book;you will find an appropriate implementation.
You can, for example, make a list of all objects created for a given class,
class Button {
public:
Button() {
_buttonList.push_back( this );
// assign index/handler to this button
}
~Button() {
_buttonList.erase( _handle );
}
static bool isMouseTargeting( float x, float y ) {
for ( auto button : _buttonList ) {
// check if inside
}
return false;
}
private:
static std::list< Button* > _buttonList;
// Handler _handle;
}
This is only a very general example of what you could do. You can use any other container besides a list (entirely up to you), and you have to find a way to index each button (or create a handle) so that you can later erase it in the destructor.
Beware of the default constructors (copy or move). If you don't explicitly create your constructors then some of your buttons will not enter the list, so either make them yourself or delete them.
Button( const Button& button ) = delete;
This is one way to do what you asked, but not necessarily the best solution. It may be simpler to just add the buttons to a non-static container by yourself and search from there.
The short answer is yes. But i will not recommend to put this functionality on the Button class since this will add extra (maybe not expected) responsibility to it. You can achieve the desired behavior by storing your Button objects on some collection and then call a function to check which button is targeted by the mouse.
Another solution would be to store the buttons collection as a member of a higher level class that represents your user-interface. This way you can call a method of this class and check if the mouse cursor is currently on some Button or not. With this design you can add the same support for other GUI elements (if you need to) easily.
In my application, I have my re-implemented QGraphicsView checking for a mouseReleaseEvent(), and then telling the item at the position the mouse is at to handle the event.
The QGraphicsItem for my view is made up of two other QGraphicsItems, and I check which one of the two is being clicked on (or rather having the button released on), and handle the respective events.
In my Widget's constructor, I set one of the items as selected by default, using the same methods I used when the items detect a release.
When I debugged, I found that for the LabelItem, select is called without a problem from the constructor (and the result is clear when I first start the application). But, when I click on the items, the application terminates. I saw that I was getting into the select function, but not leaving it. So the problem is here.
Which is very weird, because the select function is just a single line setter.
void LabelItem::select()
{
selected = true;
}
This is the mouseReleaseEvent;
void LayerView::mouseReleaseEvent(QMouseEvent *event)
{
LayerItem *l;
if(event->button() == Qt::LeftButton)
{
l = (LayerItem *) itemAt(event->pos());
if(l->inLabel(event->pos()))
{ //No problem upto this point, if label is clicked on
l->setSelection(true); //in setSelection, I call select() or unselect() of LabelItem,
//which is a child of LayerItem, and the problem is there.
//In the constructor for my main widget, I use setSelection
//for the bottom most LayerItem, and have no issues.
emit selected(l->getId());
}
else if(l->inCheckBox(event->pos()))
{
bool t = l->toggleCheckState();
emit toggled(l->getId(), t);
}
}
}
When I commented the line out in the function, I had no errors. I have not debugged for the other QGraphicsItem, CheckBoxItem, but the application terminates for its events as well. I think the problem might be related, so I'm concentrating on select, for now.
I have absolutely no clue as to what could have caused this and why this is happening. From my past experience, I'm pretty sure it's something simple which I'm stupidly not thinking of, but I can't figure out what.
Help would really be appreciated.
If the LabelItem is on top of the LayerItem, itemAt will most likely return the LabelItem because it is the topmost item under the mouse. Unless the LabelItem is set to not accept any mouse button with l->setAcceptedMouseButtons(0).
Try to use qgraphicsitem_cast to test the type of the item. Each derived class must redefine QGraphicsItem::type() to return a distinct value for the cast function to be able to identify the type.
You also could handle the clicks in the items themselves by redefining their QGraphicsItem::mouseReleaseEvent() method, it would remove the need for the evil cast, but you have to remove the function LayerView::mouseReleaseEvent() or at least recall the base class implementation, QGraphicsView::mouseReleaseEvent(), to allow the item(s) to receive the event.
I have seen these odd behaviours: It was mostly binary incompatibility - the c++ side looks correct, and the crash just does not make sense. As you stated: In your code the "selected" variable cannot be the cause. Do you might have changed the declaration and forgot the recompile all linked objects. Just clean and recompile all object files. Worked for me in 99% of the cases.
My friend and I have each created parts of a GUI using Qt 4. They both work independently and I am trying to integrate his form with the my main window. As of now this is the code I am using to try and load his form:
//connect buttons and such
connect(exitbtn, SIGNAL(triggered()),this,SLOT(terminated()));
connect(add, SIGNAL(triggered()),this,SLOT(add_rec()));
void MainWindowImpl::add_rec()
{
//form quits as soon as it loads...?
DialogImpl dia;//name of his form
dia.show();
}
I have included his header file. The program compiles but when I hit the trigger his form loads up for maybe half a second and then closes. Does anyone know what I am doing wrong?
You have almost get it right. This is because the RAII of C++. If you allocate the Dialog on stack, it would be destructed as soon as the function return.
Assuming MainWindowImpl inherits publically from QWidget, you're looking for this:
void MainWindowImpl::add_rec()
{
// passing "this" to the constructor makes sure dialog will be cleaned up.
// Note that DialogImpl will need a constructor that takes a
// QObject* parent parameter.
DialogImpl* dialog = new DialogImpl(this);
dialog->show();
}
Look at the Qt documentation for examples of how the constructors should look.
Apparently QT4 only allows one instance of an object at a time, however pointers are another matter. Change both the main.cpp and what ever your main window to look something like this:
DialogImpl *dia=new DialogImpl;
dia->show();