QT - QTableView removeRow() crashing - c++

This function should remove a row from my QStandardItemModel attached to a QTable View.
void ModManager::delete_Addin(int index)
{
QString addinId;
int i;
addinId = tableModel->item(index,0)->text();
for(i=0;i<modList->size();i++)
{
if(modList->at(i)->Id() == addinId)
{
delete modList->takeAt(i);
break;
}
}
tableModel->removeRow(index);
}
The strange thing is that the program crashes at the last instruction, tableModel->removeRow(index);
And its not going out of range because tableModel->item(index,0) is valid.
What could it be, then?

the code does not present relativity between modList and tableModel. tableModel->item(index,0) was valid before changing modList, while tableModel->rowAt(index) becomes invalid after modifying. There are a few possibilities:
Modifying modList affects tableModel, as #vahancho implies. This can be verified by commenting out the for loop or changing the order of lines. This can be lead by use modList as the real data of tableModel, for example, are you implementing a custom QTableModel by returning modList->at(i) as QTableModel::Data and returning modList->count() as QTableModel::rowCount()?
modList does not affect tableModel, but the item was referenced somewhere else. this cannot be tell from the code.

Related

I get an uninitialized object from a pointer

So i have some troubles getting pointers to work with SFML shapes. I'm not sure if it has something to do with SFML or if I'm doing anything wrong.
In Draw() x(a ControlWindow) does not contain valid values, it only shows "???" as shown here. However the m_controls(map) contains the correct values for the control object.
I'm quite new to C++ so any help would be greatly appreciated.
Exception
Exception thrown at 0x60B26EE5 (sfml-graphics-2.dll) in OokiiUI.exe: 0xC0000005: Access violation reading location 0x00000000.
Main
vector<WindowControl> windowControls;
void Draw ();
int main ()
{
RectangleShape rect(Vector2f(120,120));
WindowControl windowControl(nullptr,0);
Control testControl(&windowControl,1);
testControl.SetShape(&rect);
windowControl.AddControl(testControl);
windowControls.push_back(windowControl);
return 0;
}
WindowControl
class WindowControl : Control
{
public:
WindowControl ( WindowControl * windowControl, uint64_t uint64 )
: Control ( windowControl, uint64 )
{
}
void AddControl(Control control)
{
m_controls.insert_or_assign(control.GetId(), control);
m_controlPtrs.push_back(&control);
}
vector<Control*>* GetControls()
{
return &m_controlPtrs;
}
private:
map<uint64_t, Control> m_controls;
vector<Control*> m_controlPtrs;
};
Draw
for (auto x : windowControls)
{
vector<Control*> *controlPtrs = x.GetControls();
window->draw(x.GetControl(0)->GetShape());
}
There is a problem here:
void AddControl(Control control)
{
m_controls.insert_or_assign(control.GetId(), control);
m_controlPtrs.push_back(&control);
}
You add the address of the parameter control which is destroyed when the function ends. It looks like you want to add the address of the copy that you add to your map like this:
void AddControl(Control control)
{
m_controls.insert_or_assign(control.GetId(), control);
// don't use the parameter here, use the copy you put in the map
m_controlPtrs.push_back(&m_controls[control.GetId()]);
}
Although that is not ideal because if you send the same control twice, it will only appear once in the map (updated) but twice in the vector of pointers. You can use the returned pait from insert_or_update to fix that:
void AddControl(Control control)
{
auto [iter, was_inserted] = m_controls.insert_or_assign(control.GetId(), control);
// only add to vector if it was not in the map before
if(was_inserted)
m_controlPtrs.push_back(&iter->second);
}
A side note:
It is more idiomatic to return a reference in this situation rather than a pointer:
vector<Control*>& GetControls()
{
return m_controlPtrs;
}
This also breaks encapsulation so it may be worth thinking about how you can avoid accessing the internals of your objects so directly.
Your problem is that you are adding the pointer of a local variable into your m_controlPtrs:
void AddControl(Control control)
{
m_controlPtrs.push_back(&control);
}
Here you take a copy of Control, then add the address of that into your vector. The moment the function returns, your object goes out of scope and that memory is pointing to uninitialised garbage.
You probably want to update AddControl to take a Control&.
#ShadowRanger raises a good point in the comments: what I've mentioned may fix your issue, perhaps indefinitely, but your design still isn't terrific. Any time you have a Control which won't outlive m_controlPtrs you're going to encounter this same problem. Your code is small now, but this may eventually turn into a nightmare to fix. It's likely you should instead update m_controlPtrs to share (or take) ownership of the Control so this problem won't occur.
The easiest way out is to have m_controlPtrs declared as a std::vector<std::shared_ptr<Control>>, but it's something you should think about.

How to make a string into a reference?

I have looked into this, but it's not what I wanted: Convert string to variable name or variable type
I have code that reads an ini file, stores data in a QHash table, and checks the values of the hash key, (see below) if a value is "1" it's added to World.
Code Examples:
World theWorld;
AgentMove AgentMovement(&theWorld);
if(rules.value("AgentMovement") == "1")
theWorld.addRule(&AgentMovement);
INI file:
AgentMovement=1
What I want to do is, dynamically read from the INI file and set a reference to a hard coded variable.
for(int j = 0; j < ck.size(); j++)
if(rules.value(ck[j]) == "1")
theWorld.addRule("&" + ck[j]);
^
= &AgentMovement
How would you make a string into a reference as noted above?
This is a common theme in programming: A value which can only be one of a set (could be an enum, one of a finite set of ints, or a set of possible string values, or even a number of buttons in a GUI) is used as a criteria to perform some kind of action. The simplistic approach is to use a switch (for atomic types) or an if/else chain for complex types. That is what you are currently doing, and there is nothing wrong with it as such:
if(rules.value(ck[j]) == "1") theWorld.addRule(&AgentMovement);
else if(rules.value(ck[j]) == "2") theWorld.addRule(&AgentEat);
else if(rules.value(ck[j]) == "3") theWorld.addRule(&AgentSleep);
// etc.
else error("internal error: weird rules value %s\n", rules.value(ck[j]));
The main advantages of this pattern are in my experience that it is crystal clear: anybody, including you in a year, understands immediately what's going on and can see immediately which criteria leads to which action. It is also trivial to debug which can be a surprising advantage: You can break at a specific action, and only at that action.
The main disadvantage is maintainability. If the same criteria (enum or whatever) is used to switch between different things in various places, all these places have to be maintained, for example when a new enum value is added. An action may come with a sound, an icon, a state change, a log message, and so on. If these do not happen at the same time (in the same switch), you'll end up switching multiple times over the action enum (or if/then/else over the string values). In that case it's better to bundle all information connected to an action in a data structure and put the structures in a map/hash table with the actions as keys. All the switches collapse to single calls. The compile-time initialization of such a map could look like this:
struct ActionDataT { Rule rule; Icon icon; Sound sound; };
map<string, AcionDataT> actionMap
= {
{"1", {AgentMovement, moveIcon, moveSound} }
{"2", {AgentEat, eatIcon, eatSound } } ,
//
};
The usage would be like
for(int j = 0; j < ck.size(); j++)
theWorld.addRule(actionMap[rules.value(ck[j])].rule);
And elsewhere, for example:
if(actionFinished(action)) removeIcon(actionMap[action].icon);
This is fairly elegant. It demonstrates two principles of software design: 1. "All problems in computer science can be solved by another level of indirection" (David Wheeler), and 2. There is often a choice between more data or more code. The simplistic approach is code-oriented, the map approach is data oriented.
The data-centrist approach is indispensable if switches occur in more than one situation, because coding them out each time would be a maintenance nightmare.
Note that with the data-centrist approach none of the places where an action is used has to be touched when a new action is added. This is essential. The mechanism resembles (in principle and implementation, actually) the call of a virtual member function. The calling code doesn't know and isn't really interested in what is actually done. Responsibility is transferred to the object. The calling code may perform actions later in the life cycle of a program which didn't exist when it was written. By contrast, compare it to a program with many explicit switches where every single use must be examined when an action is added.
The indirection involved in the data-centrist approach is its disadvantage though, and the only problem which cannot be solved by another level of indirection, as Wheeler remarked. The code becomes more abstract and hence less obvious and harder to debug.
You have to provide the mapping from the names to the object by yourself. I would wrap it into a class, something like this:
template <typename T>
struct ObjectMap {
void addObject(std::string name,T* obj){
m[name] = obj;
}
T& getRef(std::string name) const {
auto x = m.find(name);
if (x != m.end() ) { return *(x->second);}
else { return dummy; }
}
private:
std::map<std::string,T*> m;
T dummy;
}
The problem with this approach is that you have to decide what to do if an object is requested that is actually not in the map. A reference always has to reference something (in contrast to a pointer that can be 0). I decided to return the reference to a dummy object. However, you might want to consider to use pointers instead of references. Another option might be to throw an error in case the object is not in the map.

Dereferencing a set iterator causes a seg fault

I'm trying to determine why the following code is throwing a segfault on line 10 (where we dereference upgradeIter).
bool UpgradeType::isAffected(const UnitType *unitType) const{
if(std::find(effects.begin(), effects.end(), unitType)!=effects.end()) return true;
// Check if the unit has any of the affected tags
std::set<string>::iterator upgradeIter;
for(upgradeIter = tags.begin(); upgradeIter != tags.end(); ++upgradeIter) {
std::set<string>::iterator unitIter;
for(unitIter = unitType->getTags().begin(); unitIter != unitType->getTags().end(); ++unitIter) {
string unitTag = *unitIter;
string upgradeTag = *upgradeIter;
if(unitTag == upgradeTag) return true;
}
}
return false;
}
The context is that UpgradeType has "tags" (just a set of strings). Units also have tags. If a unit shares at least one tag with the upgrade, then the unit is affected by the upgrade.
I don't see any reason why the mentioned line would crash. It seems to me that there is no circumstances under which the iterator could be invalid.
In other parts of the code that display the contents of tags (used in very similar ways), the output is as expected.
EDIT: I've just found out that unitType->getTags().size() is 0. So I don't understand why the body of the for loop is even executed. unitIter != unitType->getTags().end(), however, is evaluating to true. This seems off.
I managed to find a solution to this with the help of Yggdrasil on this site (which also means that Matt McNabb in the question's comments was correct). Quoting his post below:
As someone more or less mentioned on stackoverflow: Change getTags() to return a reference, not a value/copy.
const set &getTags() const {return tags;}
Be aware that the return type is const, so use a const iterator.
Not sure if that's all, but you don't want a (deep) copy there, for sure. The iterator gets out of bounds because you check against the end of a different set. Every call to getTags() gets its own copy.

Public variables of class not being preserved when altered in function c++

I've found a question sorta similar to this one, though put in a more complex way than I think I require (received a -2 for question score). Hopefully this will be easier to follow.
The general gist of things is the two classes involved are GUI and Player (I've had the same problem elsewhere with other classes, but if I can understand why this one isn't working, it should apply to the rest).
GUI includes "Player.h". The class Player has a public boolean variable 'hasBall'; When a 'Player' is passed into a function Pass() and the boolean value changed, it seems that it is only a temp object thus isn't updating the object being passed itself. See code below:
This works fine, boolean values for Plyr1A and Plyr2A (defined in Gui.h) are changed and preserved
Plyr1A.hasBall = false;
Plyr2A.hasBall = true;
However boolean values for Plyr1A and Plyr2A remain the same with this.
Pass(Plyr1A,Plyr2A); //Boolean values for Plyr1A and Plyr2A remain the same with this.
void GUI::Pass(Player passer, Player receiver) {
passer.hasBall = false;
receiver.hasBall = true;
}
If anyone could explain to me why this occurs I'd be rather thankful! If there is any extra information needed please let me know.
Cheers :)
Your function makes a copy of the arguments (they are passed by value), then changes the copy, not the "original" objects(in the body of the function).
You should change it to take pointers or references, for example:
//-------------------v---------------v <---these are references
void GUI::Pass(Player& passer, Player& receiver) {
Of course, you should change the declaration, too.
Use references to pass your objects.
Consider this function:
void someFunction (int j)
{
j = 8;
}
And say we call it like this:
someFunction (3);
Are you thinking the assignment in someFunction somehow makes that 3 become an 8? Your expectation makes no sense. Without some kind of special arrangement, it cannot be that an assignment inside a function changes values in the caller.

Receiving assert failure on Reference Call

(Disclaimer: I have removed the Qt tag in case the problem is in my syntax / understanding of the references involved here)
I have a foreach loop with an object Member. When I enumerate through the list and try to access a member field, the debugger stops and I get a message:
Stopped: 'signal-received' -
The assert failure is:
inline QString::QString(const QString &other) : d(other.d)
{ Q_ASSERT(&other != this); d->ref.ref(); }
I have checked if the member is NULL, and it isn't. I have tried re-working the code, but I keep failing on this simple call.
Some thing's I missed out. MemberList is a singleton (definitely initialized and returns a valid pointer) that is created as the application launches and populates the MemberList with Members from a file. When this is created, there are definitely values, as I print them to qDebug(). This page is literally the next page. I am unsure as to how the List items can be destroyed.
The code is as follows:
int i = 0;
QList<Member*> members = ml->getMembers();
foreach (Member* mem, members)
{
QString memID = mem->getMemberID(); // Crash happens here
QListWidgetItem *lstItem = new QListWidgetItem(memID, lsvMembers);
lsvMembers->insertItem(i, lstItem);
i++;
}
The Member classes get is as follows:
QString getMemberID() const;
and the actual function is:
QString Member::getMemberID() const
{
return MemberID;
}
The ml variable is received as follows:
QList<Member*> MemberList::getMembers()
{
return MemberList::getInstance()->memberList;
}
Where memberList is a private variable.
Final answer:
I decided to rework the singleton completely and found that I was not instantiating a new Member, rather reusing the previous object over and over. This caused the double reference. S'pose thats pointers for you. Special thanks to Troubadour for the effort!
If mem is not null it could still be the case that the pointer is dangling i.e. the Member it was pointing to has been deleted.
If Member inherits from QObject then you could temporarily change your QList<Member*> that is stored in ml (assuming that's what's stored in ml) into a QList< QPointer<Member> >. If you then get a null QPointer in the list after calling getMembers or at any point during the loop then the object must have been destroyed at some point.
Edit
As regards the singleton, are you sure it's initiliased properly? In other words does MemberList::getInstance() return a valid pointer or just a random uninitialised one?
Edit2
Since we've exhausted most possibilities I guess it must be in the singleton somewhere. All I can suggest is to keep querying the first item in the list to find out exactly where it goes bad.