Welcome!

Welcome to the official BlackBerry Support Community Forums.

This is your resource to discuss support topics with your peers, and learn from each other.

inside custom component

Native Development

Reply
Developer
greenmr
Posts: 919
Registered: ‎03-20-2013
My Device: Red LE Developer Z10
Accepted Solution

QVariantMap losing pointers

[ Edited ]

I am passing instance pointers to one of my classes around as one element of a QVariantMap. 98% of the time it works well, but that 2% is killing me. Sometimes when I extract the pointer somewhere else in my app it comes out as NULL rather than that valid pointer that was put in there.

 

Here's an example of how I get the pointer into the QVariantMap:

 

MyClass* myClassPtr;
QVariantMap map;

myClassPtr = &myClassInstance;
map["title"] = "Pointer to my class";
Q_ASSERT(myClassPtr); map["myClass"] = QVariant::fromValue( myClassPtr );
Q_ASSERT(map["settings"].value<MyClass*>());

Once "map" is filled up with other values too it get passed around in the app by various means, such as a signal when the user does something. At the other end, when I need the pointer back, I do this:

 

MyClass* myClassPtr = map["settings"].value<MyClass*>();
Q_ASSERT(myClassPtr);

As I said, 98% of the time this works as intended, but the remaining times the pointer comes back out as NULL, which obviously has critical consequences when I try to call object functions from it. Strangely, the "title" element always comes out correctly even when the pointer doesn't. I know the pointer is getting lost in transit because the first and second Q_ASSERTs never fire, even when the third one does. Incidentally, I am trying to extract the pointer at the same location in code each time. It will work fine 10, 20, 40 times in a row, then the next time... boom!

 

I just can't figure out what is happening here.

 

 

:Censored:

 

Update: on the off-chance that it might help, I'll explain how the QVariantMap gets passed around. That first block of code is in a class that builds a QList<QVariantMap> and uses it to populate a GroupDataModel.

 

The second block is in a function that is triggered when the user taps one of the items in the ListView that uses that GroupDataModel, and it gets the index path of the item the user tapped as a parameter. To expand on that second block:

 

QVariantMap map = dataModel->data( indexPath ).value<QVariantMap>();
QString title = map["title"].toString();
myClass* myClassPtr = map["settings"].value<MyClass*>();
Q_ASSERT(myClassPtr);

AsI said before, "title" always comes out correctly, and "settings" usually does, but not always. Maybe somebody will notice something wrong with this extra detail.



Developer of Built for BlackBerry certified multiFEED RSS/Atom feed reader and aggregator.
Developer
BBSJdev
Posts: 6,118
Registered: ‎07-05-2012
My Device: Playbook, Dev Alpha C, Z10 LE, Z30

Re: QVariantMap losing pointers

It's possible that fromValue doesn't do a deep copy so any change of address by optimisation say or from your own code will likely get the results you are getting. I've searched for information on whether this is true or not but information is sparse to say the least.

 

Could you knock up a simple example?

 


If you've been helped click on Like Button, if you've been saved buy the app. :smileyhappy:

Developer of stokLocker, Sympatico and Super Sentences.
Developer
greenmr
Posts: 919
Registered: ‎03-20-2013
My Device: Red LE Developer Z10

Re: QVariantMap losing pointers

UPDATE: Turns out I was chasing a red herring, as it has nothing to do with QVariantMap. After weeks of having my app "randomly" shut down I realized last night that there was a pattern after all and I learned something new about ListView.

 

What I didn't know is that ListView issues a triggered() signal not ONLY when you tap on an item in the list, but also when you tap on a header. My items are quite wide whereas my headers are very narrow. What was happening was that a few times out of a hundred I was a little sloppy tapping the item and hit the header instead. This triggered a function that expected to receive an index path pointing to a QVariantMap from the datamodel that had been populated with the the "settings" item. When I tapped the header by mistake, it wasn't getting the QVariantMap with "settings" so when I cast it back to MyClass* it was always winding up NULL.

 

What made this particularly hard to pin down was that I very rarely got sloppy enough to hit the header by mistake, and there was no indication I had done so, so I thought the app was dying when I was tapping the ListView item normally. I didn't realize that ListView sends out the triggered() signal for header taps too.

 

I fixed it by checking the length of indexPath in the ListView onTriggered slot. If the length is two then an item was tapped, so I call the function. If however the length is only one then it is a header instead, so I do nothing.



Developer of Built for BlackBerry certified multiFEED RSS/Atom feed reader and aggregator.
Developer
greenmr
Posts: 919
Registered: ‎03-20-2013
My Device: Red LE Developer Z10

Re: QVariantMap losing pointers

I had actually thought of this and did my own research into fromValue(), which as you say is hard to come by, even in the Qt documentation, which is often much better than the Cascades version. For one thing, I often find the Qt code samples illuminating, even if not directly applicable to Cascades. Unfortunately BlackBerry doesn't seem to want us to even see these samples since they show the link but all it does it pop up hover text saying that the sample is not applicable to Cascades. When I see this I often jump over to the Qt version of the class documentation so I can see what they are hiding from me. For those who don't know, Cascades documentation is just a reformatting of the Nokia Qt documents, word for word in most cases.

 

From what I can make out, when you use fromValue() with a pointer to a class (which must have been declared with the Q_DECLARE_METATYPE() macro), what gets inserted into the resulting QVariant is a QPtr only, and not a copy of the class instance, which is why you have to cast it back to the correct pointer type before using it. To actually place a full object instance into a QVariant you not only have to use Q_DECLARE_METATYPE() on the class, but it must be copyable and assignable (must have a copy constructor and operator==() friend function), which no ancestor of QObject is. For this reason the ONLY way to pass QObject derived subclasses around in a QVariant is as a pointer to an instance using fromValue().

 


BBSJdev wrote:

It's possible that fromValue doesn't do a deep copy so any change of address by optimisation say or from your own code will likely get the results you are getting. I've searched for information on whether this is true or not but information is sparse to say the least.

 

Could you knock up a simple example?

 






Developer of Built for BlackBerry certified multiFEED RSS/Atom feed reader and aggregator.