Results 1 to 19 of 19

Thread: deleting internal pointer in QModelIndex

  1. #1
    Join Date
    Jan 2006
    Location
    Earth (Terra)
    Posts
    87
    Thanks
    4
    Thanked 6 Times in 4 Posts
    Qt products
    Qt4 Qt5
    Platforms
    MacOS X Unix/X11 Windows Symbian S60

    Default deleting internal pointer in QModelIndex

    Hmm.

    Here's an issue: I'm creating QModelIndexes, and storing a pointer with traversal information via a 'new' operator in the index (so the object is allocated on the heap.)

    Problem is, I don't know when the index is deleted - an obvious memory leak issue for the internal pointer object.

    Looking through the docs and the code, there doesn't seem to be any way to know when a QModelIndex is being deleted. It would be nice if there was a flag in the index - something like: 'deletePointerOnDestroy'. That way the index could take care of the deletion on it's own.

    Any thoughts on how to do something like this? I suppose I could keep a vector of the pointers and then delete them after I know no QModelIndexes were required, but that's not necessarily obvious, either.

    Scratching my head... Any thoughts appreciated.

    rickb

  2. #2
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    33,359
    Thanks
    3
    Thanked 5,015 Times in 4,792 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows Android Maemo/MeeGo
    Wiki edits
    10

    Default Re: deleting internal pointer in QModelIndex

    The pointer should not be owned by the index! It should be owned by the model and only assigned to the index so that in may access it. Model indices are shortlived objects - you shouldn't store them or use them after manipulating the model - they become stale very quickly. Model indices are always required - they are used internally by the model, the view and everything else that touches the model. So... keep your hands away from making model indices too complex. They should be simple and should not carry any information that is not contained in the model. And the pointer should never be used outside the model - it's called "internal" for a reason.

  3. #3
    Join Date
    Jan 2006
    Location
    Earth (Terra)
    Posts
    87
    Thanks
    4
    Thanked 6 Times in 4 Posts
    Qt products
    Qt4 Qt5
    Platforms
    MacOS X Unix/X11 Windows Symbian S60

    Lightbulb Re: deleting internal pointer in QModelIndex

    Philosophically, I think I would disagree.

    In this case, the pointer contains information to help the traversal and essentially becomes an extension of the index. As such, it is handed off to the index, and it is appropriate that the index be entrusted with destroying it.

    Alternatively (if you want to adhere to the assertion), then the index could let me (the model) know that it (the index) is being destroyed, and the model can then do what it needs to clean up anything it has given to the index before said index is destroyed. That would preserve the notion that anything given to the index belongs to the model.

    I don't think that breaks any design paradigms, and is consistent with the idea of the index.

    my 2c
    rickb

  4. #4
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    33,359
    Thanks
    3
    Thanked 5,015 Times in 4,792 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows Android Maemo/MeeGo
    Wiki edits
    10

    Default Re: deleting internal pointer in QModelIndex

    Quote Originally Posted by rickbsgu View Post
    Philosophically, I think I would disagree.
    It has nothing to do with philosophy or other difficult words. QModelIndex was created to be a simple lightweight object.

    In this case, the pointer contains information to help the traversal and essentially becomes an extension of the index. As such, it is handed off to the index, and it is appropriate that the index be entrusted with destroying it.
    No, because as long as an item is valid, it has some way to be found (traversed to). If you have an index and then you modify the model, your traversal data becomes stale (invalid) - so keeping such data without regard to the item itself doesn't make sense. It is the model that knows how to find a particular item. If you need some special data for finding the item, store it in the model (or even in the item - for instance using parent-child pointers) and make the model create, update or destroy traversal data.

    Alternatively (if you want to adhere to the assertion), then the index could let me (the model) know that it (the index) is being destroyed, and the model can then do what it needs to clean up anything it has given to the index before said index is destroyed. That would preserve the notion that anything given to the index belongs to the model.
    See my comment above.

    I don't think that breaks any design paradigms, and is consistent with the idea of the index.
    Again - the idea of an index is that it is an object that is quick to create and quick to destroy. It should also provide means to traverse data using parent(), sibling() and child() methods for accessing appropriate indices. If you make the index heavy, your application will constantly be busy creating and destroying your traversal data. For instance, if you have 100 items, the process of showing them in a view involves creation and destruction of few hundred indices. Do you want to create and destroy such information several times a second? Isn't it better to just keep it and use it when appropriate?

    What kind of data do you have such that it needs some extra data to be traversable? Assuming you have some "Item" structure in your model where you keep items, can't you have methods there that allow traversal and simply pass that item pointer to the index as its internal pointer? Data in applications is usually organized into some kind of graph (net of items) and such structures are very easily traversable.

    The post is quite long, so a summary follows:
    As long as your model is static (doesn't change), the way to access each of its items doesn't change, hence no extra data needs to be created or destroyed. It is the model that knows if items are created, destroyed or moved thus it is best suited to update any extra data associated with its items. A model index is a volatile and lightweight object - it should be treated as a pointer, nothing more. It doesn't need to know anything about the model - it is the model that can create valid indices, not the other way round - the model controls its indices, indices know nothing about state changes in the model. If the model changes, all its indices should be considered invalid and should be queried for again. If you need to store an index, you should use QPersistentModelIndex and it is the model's job to update all its persistent indices when its state changes.

  5. #5
    Join Date
    Jan 2006
    Location
    Earth (Terra)
    Posts
    87
    Thanks
    4
    Thanked 6 Times in 4 Posts
    Qt products
    Qt4 Qt5
    Platforms
    MacOS X Unix/X11 Windows Symbian S60

    Default Re: deleting internal pointer in QModelIndex

    Well, no, I think you're missing the point - the little things I'm creating are expected to die with the index. They're simply little containers that provide parent/child information, so I don't have to carry them in the model.

    Sure, I can create a traversal mechanism (and have, since I can't seem to get a way to insure these items are deleted.) The traversal mechanism can give me paths and I can derive parent-child relationships from the paths. And they're temporary and go away when I'm done with them.

    Fineree.

    But - why do I have to do a traversal, when Qt is already doing the traversal? I'd rather take advantage of Qt's traversal mechanism, since it's doing it anyway. (And I could, if I could get these little items deleted along with the QModelIndex deletion. )

    For instance, if you have 100 items, the process of showing them in a view involves creation and destruction of few hundred indices. Do you want to create and destroy such information several times a second? Isn't it better to just keep it and use it when appropriate?
    Well, the indices have to get created and destroyed. What's another 20 bytes or so? Doesn't seem to be adding a lot of weight.

    (BTW, the behavior you describe is something of a curiosity - I notice I get called a lot of times for the same indices.)

    If the model changes, all its indices should be considered invalid and should be queried for again. If you need to store an index, you should use QPersistentModelIndex and it is the model's job to update all its persistent indices when its state changes.
    No, I certainly don't need that. All I'm looking to do is give the indexes a little extra information that will help me create proper indexes, subsequently.

    Anyway. The tradeoff boils down to adding a small amount of complexity to the indices or a larger amount of complexity to the model to achieve the same thing. I'd rather go with the smaller amount of complexity, if I have the choice.

    Since I don't have the choice (at least not without modifying the Qt code - which I really don't want to do), I've re-implemented along the lines you've suggested.

    Cheers,
    rickb
    Last edited by rickbsgu; 11th December 2007 at 22:25.

  6. #6
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    33,359
    Thanks
    3
    Thanked 5,015 Times in 4,792 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows Android Maemo/MeeGo
    Wiki edits
    10

    Default Re: deleting internal pointer in QModelIndex

    Quote Originally Posted by rickbsgu View Post
    Well, no, I think you're missing the point
    We can continue this discussion forever but it won't change the fact you are trying to do with the index something it was never designed for. If you want to be smarter than people who actually thought of and implemented QModelIndex, go argue with them - I'm just repeating what I was told and with what I completely agree.

    - the little things I'm creating are expected to die with the index. They're simply little containers that provide parent/child information, so I don't have to carry them in the model.
    "parent" and "child" are attributes of items, not index objects. No matter how many times you are going to deny that, I'll keep repeating it, because hierarchy is the attribute of the model and not its index.

    The traversal mechanism can give me paths and I can derive parent-child relationships from the paths. And they're temporary and go away when I'm done with them.
    What do you mean by that you are done with them? If you want to manipulate the model again a while later will they be different than a moment ago? If not, why constantly create and delete them? It's a big overhead.

    But - why do I have to do a traversal, when Qt is already doing the traversal? I'd rather take advantage of Qt's traversal mechanism, since it's doing it anyway. (And I could, if I could get these little items deleted along with the QModelIndex deletion. )
    Don't ask me - I never needed any traversal data for my models and I implemented quite a number of them in my life. I'm using the mechanism Qt provides - using QAbstractItemModel::index(), QAbstractItemModel::parent() and QAbstractItemModel::rowCount().

    Well, the indices have to get created and destroyed. What's another 20 bytes or so?
    Yes... If you have 10000 of these created every second then that's quite some time needed to allocate and deallocate memory.

    Doesn't seem to be adding a lot of weight.
    They do, but it's not about the memory footprint but rather the time needed to create and destroy them.

    (BTW, the behavior you describe is something of a curiosity - I notice I get called a lot of times for the same indices.)
    Yes, that's one of the problems with InterView - it wants data much too often. But that's the way it is and until InterViewNG comes out we have to live with it.

    All I'm looking to do is give the indexes a little extra information that will help me create proper indexes, subsequently.
    It's the model that creates indexes, not QModelIndex... (how many times have I already written this?) Thus the index doesn't need any extra data. The internal pointer is to be used solely by the model or abused by external objects if they are not designed correctly. internalPointer() shouldn't be available outside the model - it's one of those little things that breaks its OO design.

    Anyway. The tradeoff boils down to adding a small amount of complexity to the indices or a larger amount of complexity to the model to achieve the same thing. I'd rather go with the smaller amount of complexity, if I have the choice.
    Please do and when you're finished with it, write an article about it and I will gladly read it. Just note there are no virtual methods in QModelIndex (and no wonder - it is meant to be lightweight!), so subclassing it is useless.

    Since I don't have the choice (at least not without modifying the Qt code - which I really don't want to do), I've re-implemented along the lines you've suggested.
    That's not true. You can subclass the index, inherit QObject that has a virtual destructor and even emit signals from the index. Just expect your application to be really slow.

    As a side note - it occured that QModelIndex in its current state was too expensive to use on Java, so Qt Jambi doesn't use it.

  7. #7
    Join Date
    Jan 2006
    Location
    Earth (Terra)
    Posts
    87
    Thanks
    4
    Thanked 6 Times in 4 Posts
    Qt products
    Qt4 Qt5
    Platforms
    MacOS X Unix/X11 Windows Symbian S60

    Default Re: deleting internal pointer in QModelIndex

    Quote Originally Posted by wysota View Post
    We can continue this discussion forever but it won't change the fact you are trying to do with the index something it was never designed for. If you want to be smarter than people who actually thought of and implemented QModelIndex, go argue with them - I'm just repeating what I was told and with what I completely agree.


    "parent" and "child" are attributes of items, not index objects. No matter how many times you are going to deny that, I'll keep repeating it, because hierarchy is the attribute of the model and not its index.
    Then why does the view ask for parents? What does it care, if there are no parent/child relationships among the items (this is for a QTreeView, BTW).

    What do you mean by that you are done with them? If you want to manipulate the model again a while later will they be different than a moment ago? If not, why constantly create and delete them? It's a big overhead.
    No, it's not that big. Just following a few pointers. (It's the way OpenInventor works, BTW - doesn't seem to slow it up. XML XPaths do the same kind of thing. XML can be slow, but not because of pathing. If anything, pathing can speed things up - if you can re-use the paths.)

    Don't ask me - I never needed any traversal data for my models and I implemented quite a number of them in my life. I'm using the mechanism Qt provides - using QAbstractItemModel::index(), QAbstractItemModel::parent() and QAbstractItemModel::rowCount().
    Gotta get it a parent, somehow...


    It's the model that creates indexes, not QModelIndex... (how many times have I already written this?) Thus the index doesn't need any extra data. The internal pointer is to be used solely by the model or abused by external objects if they are not designed correctly. internalPointer() shouldn't be available outside the model - it's one of those little things that breaks its OO design.
    I'm not arguing that. I'm (the model) the only one using the internal pointer. It just has to get deleted, somehow.

    Please do and when you're finished with it, write an article about it and I will gladly read it. Just note there are no virtual methods in QModelIndex (and no wonder - it is meant to be lightweight!), so subclassing it is useless.
    It's a demo of something I did in Qt3. Want to re-implement it w/ Qt4's model/view architecture. It's coming along, albeit slowly.

    That's not true. You can subclass the index, inherit QObject that has a virtual destructor and even emit signals from the index. Just expect your application to be really slow.
    No, don't want to carry the overhead of QObject in the indexes (or in the model items, for that matter) - total agreement, there. (I had a big discussion about that in a previous contract gig, BTW. They wanted the items to behave as QObjects. I disagreed, but was over-ruled. Implemented it under protest.)

    As a side note - it occured that QModelIndex in its current state was too expensive to use on Java, so Qt Jambi doesn't use it.
    That is interesting. I wonder if it's the same in Qyoto? I know it's quite slow, at the moment, but I don't know if QModelIndex is the culprit.

    Another discussion.

    thanx,
    rickb

  8. #8
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    33,359
    Thanks
    3
    Thanked 5,015 Times in 4,792 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows Android Maemo/MeeGo
    Wiki edits
    10

    Default Re: deleting internal pointer in QModelIndex

    Quote Originally Posted by rickbsgu View Post
    Then why does the view ask for parents? What does it care, if there are no parent/child relationships among the items (this is for a QTreeView, BTW).
    I don't see your point...

    If I show a directory that has a parent directory, I want to be able to access it... What is wrong with that?


    No, it's not that big. Just following a few pointers. (It's the way OpenInventor works, BTW - doesn't seem to slow it up. XML XPaths do the same kind of thing. XML can be slow, but not because of pathing. If anything, pathing can speed things up - if you can re-use the paths.)
    Two short questions - which is quicker, creating/managing/destroying an object on stack or on heap? Why?

    Gotta get it a parent, somehow...
    I don't really see a problem... Have you seen the simple tree model example in Qt?


    I'm (the model) the only one using the internal pointer. It just has to get deleted, somehow.
    The pointer was never meant to be owned by the index. And it doesn't make sense to do so. If you have two indexes pointing to the same item, what is the reason for having two different "internal pointers" in them? Because that's what is meant to happen with your approach and you can't work it around without keeping those objects in the model. And if you do keep them in the model, they shouldn't be owned by indexes or they will make the model go out of sync or worse.

    That is interesting. I wonder if it's the same in Qyoto? I know it's quite slow, at the moment, but I don't know if QModelIndex is the culprit.
    I don't know what Qyoto is. I know that there is no concept of stack based objects in Java so the whole point in having QModelIndex is denied.

  9. #9
    Join Date
    Jan 2006
    Location
    Earth (Terra)
    Posts
    87
    Thanks
    4
    Thanked 6 Times in 4 Posts
    Qt products
    Qt4 Qt5
    Platforms
    MacOS X Unix/X11 Windows Symbian S60

    Default Re: deleting internal pointer in QModelIndex

    Quote Originally Posted by wysota View Post
    I don't see your point...

    If I show a directory that has a parent directory, I want to be able to access it... What is wrong with that?
    This is in response to your statement that the indexes don't need to know parent child relationships. If that is the case, then why are they asking for them? - to rephrase the question.

    Two short questions - which is quicker, creating/managing/destroying an object on stack or on heap?
    Obviously, on the stack, but I can't do that, can I? Since I return after the call, my stack frame is gone.

    Why?
    Ok, if we must - because all that has to be done is increase the stack pointer and the memory from the last stack pointer position is now ours to use. But, when I return out of my routine, that space is no longer valid. Allocating on the heap requires maintenance of free space/used space pointers, but it sticks around until it's deleted. Heap management algorithms have been optimized to a very high degree in modern operating systems, so it's much less an issue than it used to be.

    I guess now it's my turn to ask: what's the relevance?

    I don't really see a problem... Have you seen the simple tree model example in Qt?
    Yup. And the way they're doing it is carrying a parent pointer in every single item. I don't want to do that.

    The pointer was never meant to be owned by the index. And it doesn't make sense to do so. If you have two indexes pointing to the same item, what is the reason for having two different "internal pointers" in them? Because that's what is meant to happen with your approach and you can't work it around without keeping those objects in the model. And if you do keep them in the model, they shouldn't be owned by indexes or they will make the model go out of sync or worse.
    Ok, well when we talk about 'ownership' we are most certainly talking on a philosophical basis, aren't we? At least in terms of design.

    (If it gets heated, then I guess it's religion...)

    Let's see: I don't mind owning the extra little pieces, but I have to know when an index is destroyed, so I can destroy the extra little piece it's carrying. OTOH, I don't have a problem giving the index ownership of the extra little piece - if I do it explicitly, and there is an explicit mechanism for the index to delete it once it's finished.

    You're correct, there would be different 'pointers' for multiple requests of the same index, but the internal information would be the same. That's an unfortunate outfall of the implementation - it seems a smarter implementation would only ask for each index once (on a given operation - it would re-ask for the index on a new operation, since the indexes used for the previous operation have been trashed. We hope.)

    I don't know what Qyoto is. I know that there is no concept of stack based objects in Java so the whole point in having QModelIndex is denied.
    It's the C# binding for Qt. Think it has a lot of promise, as C# is really a fast development environment, and the resource usage isn't as heavy as Java. It's being headed up by Richard Dale - there's more info here: http://cougarpc.net/qyoto/. I think C# is a stack based language...

    Hmm. Are you suggesting the QModelIndexes are allocated on the stack? That would be pretty tricky, since the index is returned out of the creation stack frame and then the 'request to the model stack' frame. Anything in either stack frames would be invalid. Have to walk through that section of the code and see how it's done.

    Later,
    rickb

  10. #10
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    33,359
    Thanks
    3
    Thanked 5,015 Times in 4,792 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows Android Maemo/MeeGo
    Wiki edits
    10

    Default Re: deleting internal pointer in QModelIndex

    Quote Originally Posted by rickbsgu View Post
    This is in response to your statement that the indexes don't need to know parent child relationships. If that is the case, then why are they asking for them? - to rephrase the question.
    Here is how QModelIndex::parent() is implemented ("m" is a pointer to the model):

    Qt Code:
    1. inline QModelIndex QModelIndex::parent() const
    2. { return m ? m->parent(*this) : QModelIndex(); }
    To copy to clipboard, switch view to plain text mode 

    As you see it is the model that gives back the information about the parent.

    Obviously, on the stack, but I can't do that, can I? Since I return after the call, my stack frame is gone.
    I don't know what you mean, but the point is that QModelIndex is meant to be created on stack. That's why it's so slow in Java - there is no stack there.

    Allocating on the heap requires maintenance of free space/used space pointers, but it sticks around until it's deleted.
    Sure, but you want to delete it when the index is deleted and the index is created on stack hence you slow down the index by creating some part of it on heap. QModelIndex is so optimized, it doesn't even have a P-IMPL or a shared data pointer.

    Heap management algorithms have been optimized to a very high degree in modern operating systems, so it's much less an issue than it used to be.
    See the java issue.

    I guess now it's my turn to ask: what's the relevance?
    I think I already answered that two quotes earlier.

    Yup. And the way they're doing it is carrying a parent pointer in every single item. I don't want to do that.
    Yes, it's better to carry a whole bunch of stuff in an index... Please...

    [quote]Ok, well when we talk about 'ownership' we are most certainly talking on a philosophical basis, aren't we? At least in terms of design./quote]
    No. "Ownership" is a well defined term that means that an object can (and should) delete all object it owns when it is destroyed (or sooner).

    You're correct, there would be different 'pointers' for multiple requests of the same index, but the internal information would be the same.
    Exactly.

    That's an unfortunate outfall of the implementation - it seems a smarter implementation would only ask for each index once (on a given operation - it would re-ask for the index on a new operation, since the indexes used for the previous operation have been trashed. We hope.)
    And that's exactly what happens if that "extra information" is owned by the model. And in the simplest case the extra information is a pointer to the parent sitting in an item whose pointer is kept as an internal pointer of an index. I dare you to show a simpler solution using up less than sizeof(void*) memory.

    Hmm. Are you suggesting the QModelIndexes are allocated on the stack?
    Yes! That's the whole point of creating the class in the first place!

    That would be pretty tricky, since the index is returned out of the creation stack frame and then the 'request to the model stack' frame.
    The index is volatile - it is to be created, used and destroyed immediately.

    Take a look at this: http://chaos.troll.no/~ahanssen/devd...delView.tar.gz (slides 32-35).

  11. #11
    Join Date
    Jan 2006
    Location
    Earth (Terra)
    Posts
    87
    Thanks
    4
    Thanked 6 Times in 4 Posts
    Qt products
    Qt4 Qt5
    Platforms
    MacOS X Unix/X11 Windows Symbian S60

    Default Re: deleting internal pointer in QModelIndex

    Quote Originally Posted by wysota View Post
    Here is how QModelIndex::parent() is implemented ("m" is a pointer to the model):

    Qt Code:
    1. inline QModelIndex QModelIndex::parent() const
    2. { return m ? m->parent(*this) : QModelIndex(); }
    To copy to clipboard, switch view to plain text mode 

    As you see it is the model that gives back the information about the parent.
    The question remains unanswered: If the view doesn't care about parent-child relationships, why is it asking for parents? And why is there a 'child()' member?

    I'll provide the answer that I perceive: it is creating a view hierarchy, and it needs to set up it's own parent-child relationship to mirror that of the model. It uses the model index queries to do so.


    Yes, it's better to carry a whole bunch of stuff in an index... Please...
    Are we married? If not, I'd like to keep the discussion on a professional level without the snide asides. The tone of a bickering couple is not conducive to the exchange of ideas.


    So - using the examples you sent (thanks for the reference, BTW - that's a good write-up. Wish I could have gone), the standard test seems to be 100,000 items.

    100,000 items, every one of them carrying a parent pointer, adds up to 100,000 words, or 400,000 bytes on a 32-bit machine devoted to back-pointers. Twice that on a 64 bit machine.

    Given that page sizes are typically what? around 8k or so, that's a lot of pages tied up in carrying pointers. If they were contiguous, the memory manager could shove them out of the way until they're needed. But, the problem is, the pointers are interspersed throughout the model, so you're upping the liability of page-faults carrying around what is essentially a lot of dead weight - especially in a flat hierarchy (which UI hierarchies tend to be - most of the pointers will be carrying duplicate information.)

    So, then, a little itty bitty structure that is consructed on the fly and then goes away when it's not needed, to establish parent-child relationships makes a lot of sense.

    Ok, well when we talk about 'ownership' we are most certainly talking on a philosophical basis, aren't we? At least in terms of design.
    No. "Ownership" is a well defined term that means that an object can (and should) delete all object it owns when it is destroyed (or sooner).
    Fine. I give ownership of the little chunks I create to the model item.

    "Ownership" is well-defined to whom? The code? The machine? Neither have any concept of 'ownership'. It's purely a design concept imparted by the designers. All they (the objects) 'know' is they carry a pointer that needs to be or does not need to be deleted. And, since there are different notions of how ownership plays out - for instance, whether it should be permantly assigned or be transferable is a matter of policy, it falls squarely in the province of philosophy.

    I don't have a problem with transferring ownership of a bit of memory. You apparently do. That is a fundamental difference in philosophy. We'll have to agree to disagree on that score.

    And that's exactly what happens if that "extra information" is owned by the model. And in the simplest case the extra information is a pointer to the parent sitting in an item whose pointer is kept as an internal pointer of an index. I dare you to show a simpler solution using up less than sizeof(void*) memory.
    Sure - do as another member here has done: store paths of small integers. Of course you're limited to the number of children you can store in a byte or a nibble, and that correspondingly limits the depth. A small structure, on the other hand, removes such constraints.

    The index is volatile - it is to be created, used and destroyed immediately.
    That it's short lived, I don't have a problem with. But they're not all destroyed immediately, because they're fed back to me for further action (like discovering parent-child relationships.) Not only that, but they come to me in a specific order.

    They wink in and out of existence - no problem. In fact, that's a benefit. I would either like to know when they go out of existence, since they're carrying a bit of information I gave them, or I can transfer ownership to them (knowing they can be destroyed at any moment), and work within that constraint. No sweat.

    As far as memory allocation algorithms being inefficient, they're pretty well optimized for creating and destroying little pieces of the same size frequently in a tight loop. The following code works on four architectures: MacOS, Linux, FreeBSD, and Windows. It does a million allocations and a few less frees of objects. You'll note the allocation routine settles down to around 20-26 pointers assigned/reassigned, over-all. It runs almost instantaneously on Mac/Linux - a bit slower on Windows (maybe all this optimization effort is to compensate for Windows inefficiencies... wouldn't be surprised.)


    In summary, my assertion is that page-swapping is the real culprit, and if you're not reducing weight over-all, you're just asking for more page-faults. One page-fault is worth a whole lot of machine instructions.

    My 2c

    Qt Code:
    1. #include <iostream>
    2. #include <time.h>
    3.  
    4. using namespace std;
    5.  
    6. #ifdef _WIN32
    7. #define random rand
    8. #define srandom srand
    9. #endif
    10.  
    11. struct LittleStruct
    12. {
    13. void *pointer1, *pointer2;
    14. int val1, val2;
    15. LittleStruct *ls1;
    16. };
    17.  
    18. static const int SIZE_ASSIGNED = 100000, NUM_RUN = 1000000, SIZE_POOL = 20,
    19. DRAIN = SIZE_POOL - SIZE_POOL / 4, LOG_EVERY = NUM_RUN / 1000;
    20.  
    21. #ifdef _WIN32
    22. static const long long RAND_TOP = RAND_MAX;
    23. #else
    24. static const long long RAND_TOP = (1 << 31) - 1;
    25. #endif
    26.  
    27. static LittleStruct *assigned_ptrs[SIZE_ASSIGNED], *pool[SIZE_POOL];
    28.  
    29. static int numAssigned = 0, numAllocs = 0, numFrees = 0;
    30.  
    31. static inline int getPoolIX()
    32. {
    33. long long bigrand = (long long)random() * 20;
    34. int rand = bigrand / RAND_TOP;
    35. if (rand == 20) rand = 19;
    36. // clunky, but it'll do for this
    37. return (rand);
    38. }
    39.  
    40. static void freeSlot()
    41. {
    42. int slot = getPoolIX();
    43. if (pool[slot] != NULL)
    44. {
    45. numFrees++;
    46. delete pool[slot];
    47. pool[slot] = NULL;
    48. }
    49. }
    50.  
    51. static LittleStruct *allocSlot()
    52. {
    53. int ix;
    54. for (;;)
    55. {
    56. for (ix = 0; ix < SIZE_POOL; ix++)
    57. {
    58. int slot = getPoolIX();
    59. if (pool[slot] == NULL)
    60. {
    61. numAllocs++;
    62. return (pool[slot] = new LittleStruct());
    63. }
    64. else
    65. freeSlot();
    66. }
    67.  
    68. for (ix = 0; ix < DRAIN; ix++)
    69. freeSlot();
    70. }
    71.  
    72. return NULL;
    73. }
    74.  
    75. static void tallyAlloced(LittleStruct *ls)
    76. {
    77. for (int ix = 0; ix < numAssigned; ix++)
    78. if (assigned_ptrs[ix] == ls) return;
    79.  
    80. assigned_ptrs[numAssigned++] = ls;
    81. }
    82.  
    83. int main(int argc, char **argv)
    84. {
    85. bool dolog = false;
    86.  
    87. if (argc > 1)
    88. {
    89. switch(argv[1][0])
    90. {
    91. case 't': // check some values and test the random number generator
    92. {
    93. char buf[32];
    94. cout << "SIZE_POOL: " << SIZE_POOL << endl;
    95. cout << "RAND_TOP: 0x" << hex << RAND_TOP << dec << endl;
    96.  
    97. for (int ix = 0; ix < 1000; ix++)
    98. cout << ix << ": " << getPoolIX() << endl;
    99. }
    100. exit(0);
    101.  
    102. case 'l': // do logs
    103. dolog = true;
    104. break;
    105.  
    106. default:
    107. cout << "alloctest { t | l }" << endl;
    108. cout << "where:" << endl;
    109. cout << " t: test random number generator, show values." << endl;
    110. cout << " l: log output (slows things up, of course...)" << endl;
    111. exit(0);
    112. }
    113. }
    114.  
    115. else
    116. {
    117. srandom(time(NULL));
    118. memset(pool, SIZE_POOL * sizeof(LittleStruct *), 0);
    119. // make sure this is cleared, no matter what
    120.  
    121. while (numAllocs < NUM_RUN)
    122. {
    123. tallyAlloced(allocSlot());
    124.  
    125. if (dolog && (numAllocs % LOG_EVERY) == 0)
    126. cout << "NumAllocs: " << numAllocs <<
    127. ", NumFrees: " << numFrees <<
    128. ", NumAssigned: " << numAssigned << endl;
    129.  
    130. if (numAssigned >= SIZE_ASSIGNED)
    131. {
    132. cout << "Overflowed assigned pointer vector" << endl;
    133. exit(1);
    134. }
    135. }
    136.  
    137. cout << endl << "Run ended: " << endl;
    138. cout << "Used:\t\t" << numAllocs << " allocations, " << numFrees << " frees." <<endl;
    139. cout << " \t\t" << numAssigned << " pointers, total." << endl << endl;
    140. }
    141. }
    To copy to clipboard, switch view to plain text mode 
    Last edited by rickbsgu; 14th December 2007 at 23:46.

  12. #12
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    33,359
    Thanks
    3
    Thanked 5,015 Times in 4,792 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows Android Maemo/MeeGo
    Wiki edits
    10

    Default Re: deleting internal pointer in QModelIndex

    Quote Originally Posted by rickbsgu View Post
    The question remains unanswered: If the view doesn't care about parent-child relationships, why is it asking for parents?
    Most probably because of QAbstractItemView::rootIndex(). But you can always return an empty index from within parent() - it's your choice, the view won't care.

    And why is there a 'child()' member?
    I don't really understand that question...

    I'll provide the answer that I perceive: it is creating a view hierarchy, and it needs to set up it's own parent-child relationship to mirror that of the model. It uses the model index queries to do so.
    I don't see your point. The view is what you implement, nothing more, nothing less.

    Are we married? If not, I'd like to keep the discussion on a professional level without the snide asides.
    What kind of asides?

    The tone of a bickering couple is not conducive to the exchange of ideas.
    Hmm?

    Given that page sizes are typically what? around 8k or so, that's a lot of pages tied up in carrying pointers. If they were contiguous, the memory manager could shove them out of the way until they're needed. But, the problem is, the pointers are interspersed throughout the model, so you're upping the liability of page-faults carrying around what is essentially a lot of dead weight - especially in a flat hierarchy (which UI hierarchies tend to be - most of the pointers will be carrying duplicate information.)
    If you care about it, use placement new and you're done. I don't really see a problem. In addition, if you have a flat hierarchy, you don't need the parent pointer at all. To me it seems you are desperately trying to find a problem where there is none. I know a whole bunch of problems with the architecture, but that's not one of them. The model is just an interface to your data, nothing more. How you keep your data is only your choice as long as you return proper data within the abstract item model interface.

    So, then, a little itty bitty structure that is consructed on the fly and then goes away when it's not needed, to establish parent-child relationships makes a lot of sense.
    I don't see the sense you mention. Especially if I have two or more indexes pointing to the same object (and that's almost always the case) I don't see a reason why to have two copies of the data you mention (whatever data that is). It surely wastes much more space than bare pointers. And speaking of pointers, alignment issues cause more space to be lost than adding a pointer to the structure. Unless of course you always order your compiler to pack all your structures. Do you?

    "Ownership" is well-defined to whom? The code? The machine?
    To programmers. I don't really see a reason of discussing definitions of terms which are well known and understood. If you have an own concept of ownership, stick with it. But if you talk about ownership with others, try to stick to the well known definitions even if they are different than yours. All you said about the ownership seems odd when we notice the need of having smart pointers and even different classes of them (more or less useful).

    Sure - do as another member here has done: store paths of small integers. Of course you're limited to the number of children you can store in a byte or a nibble, and that correspondingly limits the depth. A small structure, on the other hand, removes such constraints.
    Small structure less than sizeof(void*)? How does the compiler handle that in relation to alignment? And how is that related to keeping 100k of items?


    But they're not all destroyed immediately, because they're fed back to me for further action (like discovering parent-child relationships.)
    No, they are never fed "back". They can only be passed in one direction, otherwise sooner or later you'll be accessing an index to an item which has been changed in the meantime. Notice the "const" modifiers everywhere.

    Not only that, but they come to me in a specific order.
    That's an implementation detail that can well change in future. The order is not guaranteed by the specification. Of course you can rely on it, but don't be surprised when one day your application stops working and you'll be one of the people asking why did Trolls break something in Qt that used to work fine and now does not - that's mostly when one relies on an undocumented feature.

    It runs almost instantaneously on Mac/Linux - a bit slower on Windows (maybe all this optimization effort is to compensate for Windows inefficiencies... wouldn't be surprised.)
    It's still slower than just moving the stack pointer. The biggest inefficiency is that you have to switch to kernel mode, effectively halting some things.

    In summary, my assertion is that page-swapping is the real culprit, and if you're not reducing weight over-all, you're just asking for more page-faults. One page-fault is worth a whole lot of machine instructions.
    Don't swap pages then Use placement new or QVector or similar structure that guarantees continuous region of memory.

    BTW. Your code doesn't prove anything - when deleting some region of memory, it is not deallocated unless some other process needs memory (it just returns to the pool). Try running it in border or extreme conditions and then start throwing figures or conclusions.

  13. #13
    Join Date
    Jan 2006
    Location
    Earth (Terra)
    Posts
    87
    Thanks
    4
    Thanked 6 Times in 4 Posts
    Qt products
    Qt4 Qt5
    Platforms
    MacOS X Unix/X11 Windows Symbian S60

    Default Re: deleting internal pointer in QModelIndex

    I don't see the sense you mention. Especially if I have two or more indexes pointing to the same object (and that's almost always the case) I don't see a reason why to have two copies of the data you mention (whatever data that is). It surely wastes much more space than bare pointers.
    That's the whole point, is you don't have redundant data. At any given time, a path will only be as deep as the tree of the model. So, let's say your model is five levels deep. Then, at most, you'll have 5 structs allocated at any time. And, they're gone after the traversal is needed. Beats hanging on to 400mb of backpointers, forever. (this for the pathing solution.)

    And speaking of pointers, alignment issues cause more space to be lost than adding a pointer to the structure. Unless of course you always order your compiler to pack all your structures. Do you?
    When I work on an architecture that needs it, yes. That's usually Mips or Sparc, and older versions of those, I think. Haven't worked on them, in a while. It's a non-issue on Intel.

    To programmers. I don't really see a reason of discussing definitions of terms which are well known and understood.
    Ok. The Modelndex has a pointer of data that I allocated. By this definition, the ModelIndex owns it. It should delete it when it dies. (the !path approach)

    No, they are never fed "back". They can only be passed in one direction, otherwise sooner or later you'll be accessing an index to an item which has been changed in the meantime. Notice the "const" modifiers everywhere.
    I think they are fed back. Or a copy is fed back. When it wants a parent index, it gives you the index it has on hand for the item for which it wants a parent.

    That's an implementation detail that can well change in future. The order is not guaranteed by the specification. Of course you can rely on it, but don't be surprised when one day your application stops working and you'll be one of the people asking why did Trolls break something in Qt that used to work fine and now does not - that's mostly when one relies on an undocumented feature.
    Seems like it would be nefarious if it did. The order of queries falls out from the coherence of a hierarchal structure - you have to know the parents before you can know the children. Anything else wouldn't make sense. Anyway, using paths, it's not an issue.

    It's still slower than just moving the stack pointer. The biggest inefficiency is that you have to switch to kernel mode, effectively halting some things.
    Ok, I'll concede that (referring to the !path approach.) I'm not sure it makes a difference in practical terms, but then I have to assume the trolls went through all those gyrations for a reason...

    BTW. Your code doesn't prove anything - when deleting some region of memory, it is not deallocated unless some other process needs memory (it just returns to the pool).
    I've got a lot of processes running on my machine, including two virtual machines - doesn't seem to have affected the example for the amount of time it runs. I guess I'd have to see a malignant example. Maybe someone has something coded up that demonstrates that.

    cheers,
    rickb

  14. #14
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    33,359
    Thanks
    3
    Thanked 5,015 Times in 4,792 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows Android Maemo/MeeGo
    Wiki edits
    10

    Default Re: deleting internal pointer in QModelIndex

    Quote Originally Posted by rickbsgu View Post
    That's the whole point, is you don't have redundant data.
    Qt Code:
    1. YourModel model;
    2. //...
    3. void *ptr1 = 0;
    4. {
    5. QModelIndex ind1 = model.index(1,1); // assuming the same parent index everywhere
    6. QModelIndex ind2 = model.index(1,1); // not invisible root item of course
    7. ptr1 = ind1.internalPointer();
    8. void *ptr2 = ind2.internalPointer();
    9. bool result = (ptr1==ptr2); // true or false?
    10. if(result){
    11. // assuming it's true and both indexes created independently share some data
    12. void *ptr3 = 0;
    13. {
    14. // artificial scope
    15. QModelIndex ind3 = model.index(1,1);
    16. ptr3 = ind3.internalPointer();
    17. }
    18. // here ind3 is destroyed, ptr3 points to ptr1 and ptr2 based on "result"
    19. InternalPointer * iptr = (InternalPointer*)ptr3;
    20. iptr->doSomething(); // segfault or not? ptr3 still valid or not?
    21. }
    22. }
    23. QModeIndex ind4 = model.index(1,1);
    24. void *ptr4 = ind4.internalPointer();
    25. bool result2 = (ptr1==ptr4); // true or false?
    26. if(!result2)
    27. for(int i=0;i<1000;i++) QModelIndex tmpIndex = model.index(1,1); // slow or fast?
    To copy to clipboard, switch view to plain text mode 


    It's a non-issue on Intel.
    Hmm?

    Qt Code:
    1. #include <stdio.h>
    2.  
    3. struct S1 {
    4. unsigned char s;
    5. int i;
    6. };
    7.  
    8. struct S2 {
    9. unsigned char s;
    10. };
    11.  
    12. struct S3 {
    13. unsigned char s;
    14. int i;
    15. } __attribute__((__packed__));
    16.  
    17. int main(){
    18. struct S1 x;
    19. struct S2 x2;
    20. struct S3 x3;
    21. printf("Struct S1 size: %d\n", sizeof(x));
    22. printf("Struct S2 size: %d\n", sizeof(x2));
    23. printf("Struct S3 size: %d\n", sizeof(x3));
    24. return 0;
    25. }
    To copy to clipboard, switch view to plain text mode 

    $ uname -m
    i686
    $ gcc tst.c -o tst
    $ ./tst
    Struct S size: 8
    Struct S2 size: 1
    Struct S3 size: 5

    Care to comment?


    When it wants a parent index, it gives you the index it has on hand for the item for which it wants a parent.
    It is not "back". parent() is const, thus the direction is maintained (data pulled, not pushed).

    Consider this:
    Qt Code:
    1. QModelIndexList selected = view->selectedIndexes(); // index(1,1)_copy1 in the list (shallow copy)
    2. while(!selected.isEmpty()){
    3. QModelIndex tmp = view->currentIndex(); // index(1,1)_copy3, index(1,1)_copy2
    4. if(tmp==selected.value(0)){ // value returns index(1,1)_copy4
    5. qDebug() << selected.value(0); // index(1,1)_copy5
    6. }
    7. selected.removeFirst(); // list modified, deep copy (index(1,1)_copy6)
    8. }
    To copy to clipboard, switch view to plain text mode 
    Six distinct instances of an index pointing to the same item - model not changed, multiple copies of the same index, some of them completely independent of each other (data couldn't be shared even with QSharedData). To make things worse:
    Qt Code:
    1. QModelIndexList selected = view->selectedIndexes();
    2. foreach(QModelIndex iter, selected){
    3. model.removeRow(iter.row(), iter.column(), iter.parent());
    4. }
    5.  
    6. QModelIndex iter = selected.at(0); // index invalid, was deleted earlier
    7. while(iter.parent()!=view->rootIndex()){
    8. qDebug() << iter.data(Qt::DisplayRole);
    9. iter = iter.parent();
    10. }
    To copy to clipboard, switch view to plain text mode 

    If parent() doesn't use information from the model but instead only the info contained in the index the second iteration won't know the item is no longer valid (here you have the index fed "back") and will go crazy (point to some completely different data at least). If it did use some info from the model (regardless of the nature of the data), and the model is aware that its item has been removed, it can return an invalid parent() index. So if you want your model to be safe, you have to get the data from the model and not from the index. My explanation might not be clear (it's late here) - in that case forget it and move on.

    Seems like it would be nefarious if it did.
    Wrong! Something that seems logical is not necessary optimal and may change in future when speed becomes an issue. Bubble Sort is logical, Quick Sort is fast, std::sort() doesn't assume implementation.

    I've got a lot of processes running on my machine, including two virtual machines
    Irrelevant. Try allocating and using large blocks of continuous memory (like 1GB each). Virtual memory (furthermore clean not dirty one!) and physical memory are quite different things. No page faults until you access a particular block, no point in allocating a physical page for the process - virtual memory usage high, physical - none. Memory fragmentation is also a factor - it's much easier to allocate a little block than a big one. Much less an issue when allocating on stack. Uncontrolled and unpredictable (random) test conditions can't prove anything. Let's drop the example and focus on testable things like the sizeof() code I provided.

  15. #15
    Join Date
    Jan 2006
    Location
    Earth (Terra)
    Posts
    87
    Thanks
    4
    Thanked 6 Times in 4 Posts
    Qt products
    Qt4 Qt5
    Platforms
    MacOS X Unix/X11 Windows Symbian S60

    Default Re: deleting internal pointer in QModelIndex

    Quote Originally Posted by wysota View Post
    [code]YourModel model;
    Ok, I think you're maintaining here that the ModelIndexes get copied internally, and that copies get destroyed, willy-nilly, leaving some valid MI's around with the same internal pointer/id.

    That certainly does kill the idea of trusting them with a piece of memory - you'd have to implement a ref system that would be sensitive to copies and all of that - certainly adds more weight to the MI's.

    On Word Boundary Alignment/Packed Structures
    Hmm?

    $ uname -m
    i686
    $ gcc tst.c -o tst
    $ ./tst
    Struct S size: 8
    Struct S2 size: 1
    Struct S3 size: 5

    Care to comment?
    Blow me down! Two errors on my part:
    1. I'm used to the MS compiler on Intel, and the automatic inclusion of the 'Zp1' arg, which packs structs. Have taken that somewhat for granted.
    2. Was thinking more of passing arg word alignment issues vs. pack structs (which I don't think are an issue on Intel - you can prove me wrong with an example... )

    That said, a word devoted to back-pointer is still a word devoted to back-pointer, and is still 4bytes on a 32bit machine, and 8 bytes on a 64bit machine. Not a big deal for small models - a huge deal for large models.

    It is not "back". parent() is const, thus the direction is maintained (data pulled, not pushed).
    Ok, here's where I don't understand. Consider this:
    Qt Code:
    1. void *data;
    2. mymodel::index(...)
    3. {
    4. /* ( stack frame pushed for this call ) */
    5. ....
    6. return createIndex(r,c,data);
    7. /* ( stack frame pushed and popped for create call,
    8.   | popped again on return. */
    9. ...
    10. }
    11.  
    12. mymodel::parent(..., parent)
    13. {
    14. ...
    15. if (parent.isValid)
    16. {
    17. void* parentData = parent.internalPointer();
    18. if (parentData == data)
    19. qdebug("Isn't this the modelitem I created above?");
    20. }
    21. }
    To copy to clipboard, switch view to plain text mode 

    I guess I could prove the latter question by hanging on to the pointer of
    the created MI and see if it's the same. At any rate, if the ModelItem is not fed back to me, something that looks very much like it is.

    Consider this: (two really interesting coding examples)
    Ok, I'm studying these. Again, the crux appears to be that copies are happening spontaneously all over the place, is that correct?

    So, again (if true), I agree - it kills the idea of handing a piece of memory to the ModelItem. (Beyond performance questions.)

    <...snip...>
    So if you want your model to be safe, you have to get the data from the model and not from the index. My explanation might not be clear (it's late here) - in that case forget it and move on.
    I don't think we disagree, here: The real information is coming from the model - in the small structure context I've questioned about, the little items aren't data - they're just pointer holders. You can think of them as large pointers that have to be allocated. I'm expecting them to die quickly, I'm not expecting to hang on to them, I'm not expecting them to be valid any longer than the index (which, as we have discussed, is very short-lived.)

    But, again, if they're (the MI's) being copied internally and the dups are being destroyed, that certainly puts a hole in the notion. That's a valid issue.

    cheers,
    rickb
    Last edited by rickbsgu; 18th December 2007 at 13:13.

  16. #16
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    33,359
    Thanks
    3
    Thanked 5,015 Times in 4,792 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows Android Maemo/MeeGo
    Wiki edits
    10

    Default Re: deleting internal pointer in QModelIndex

    Quote Originally Posted by rickbsgu View Post
    Ok, I think you're maintaining here that the ModelIndexes get copied internally, and that copies get destroyed, willy-nilly, leaving some valid MI's around with the same internal pointer/id.
    I'm not maintaining or suggesting anything. I'm simply asking what happens in each of the presented cases according to your design. You can either share the internal data or not. Both of these have advantages and disadvantages. If you want sharing to be effective, you need to keep the original within the model because it is the model that creates indexes, so it has to have access to the data. And if this is the case, you can't delete the data when an index gets destroyed, because the model has to have a copy of the data ready to pass it to a newly created index - thus effectively you are passing ownership to the piece of data to the model.
    If you don't want to share the data, it can be owned by the index, but then you have to create it from scratch each time QAbstractItemModel::index() is called which creates an unnecessary overhead. To remove the overhead you need to cache the data within the model - again passing ownership.

    In both cases it proves more effective to keep the data within the model. Please notice, that you don't have to keep it for all the items - you can keep it for MRU or MFU items and generate the rest on the fly.

    That certainly does kill the idea of trusting them with a piece of memory - you'd have to implement a ref system that would be sensitive to copies and all of that - certainly adds more weight to the MI's.
    QSharedData, QSharedDataPointer. But you'd have to subclass the index to use it and I'm not sure it'd work in every case.

    That said, a word devoted to back-pointer is still a word devoted to back-pointer, and is still 4bytes on a 32bit machine, and 8 bytes on a 64bit machine. Not a big deal for small models - a huge deal for large models.
    4-8MB for 1 megaitem model. Not that much of a deal compared to the size of linked Qt lbraries.

    At any rate, if the ModelItem is not fed back to me, something that looks very much like it is.
    Depends what you mean by "fed back". I don't consider function calls/returns different directions, especially that you can inline functions or not create stack frames at all. If you keep reading (passing a const reference in const methods), you are going in one direction, no matter how many function calls are made in between. But as soon as you write something to the model, you need a non-const method and you are entering the other direction. After that you can't trust any of the indexes created earlier, not in general case.

    Ok, I'm studying these. Again, the crux appears to be that copies are happening spontaneously all over the place, is that correct?
    Yes - you are having more than one instance of index pointing to the same item.

    So, again (if true), I agree - it kills the idea of handing a piece of memory to the ModelItem. (Beyond performance questions.)
    It depends. If you have some means to share the data (using some "global" cache object - like the model) you can live with it. But the data will be owned by the cache and not by any of the indexes (going back to the initial problem).


    I'm expecting them to die quickly
    The point is it's not worth it to have shortlived objects owned by indexes. There are really no benefits of that.

    I'm not expecting them to be valid any longer than the index
    That's bad then. The data remains valid as long as the model doesn't change, thus there is no point in deleting it just to create it again in a moment. If you want such volatile cache - implement it in the model, it knows better when data can or has to be destroyed.

    But, again, if they're (the MI's) being copied internally
    First of all they are instantiated by the model independently of each other. Creating real copies is another issue, but it can be ignored here.
    Qt Code:
    1. YourModel model;
    2. QModelIndex ind1 = model.index(1,1);
    3. QModelIndex ind2 = model.index(1,1);
    4. QModelIndex ind3 = ind2;
    To copy to clipboard, switch view to plain text mode 
    In the above example ind1 and ind2 are not copies whereas ind2 and ind3 are, although all of them point to the same item and have the same "path data" contents.

  17. The following user says thank you to wysota for this useful post:

    rickbsgu (18th December 2007)

  18. #17
    Join Date
    Jan 2006
    Location
    Earth (Terra)
    Posts
    87
    Thanks
    4
    Thanked 6 Times in 4 Posts
    Qt products
    Qt4 Qt5
    Platforms
    MacOS X Unix/X11 Windows Symbian S60

    Default Re: deleting internal pointer in QModelIndex

    The point is it's not worth it to have shortlived objects owned by indexes. There are really no benefits of that.
    Ok, it seems like it would be worth it, if they can help the model divine the information they want. And, if they go away, they're not using up any resources outside of the Qt-required context.

    That's bad then. The data remains valid as long as the model doesn't change, thus there is no point in deleting it just to create it again in a moment. If you want such volatile cache - implement it in the model, it knows better when data can or has to be destroyed.
    Again, I don't think it's bad. The MIs are short lived, there's no reason a tag-along piece of information can't be just as short-lived. The best analogy I could come up with was the 'big pointer' analogy - they're not model data, per se. And, if the memory manager is just going to dole out the same pointers from a pool, so much the better.

    (However: your index.parent().parent()... example in another thread is something to think about. Nothing better than going back to the original data - can't argue that.)

    Stack Allocator
    So, I walked through 'createIndex()' to see what it does. Nothing particularly magical. As you say, it creates the struct on the stack. The trick is that it relies on the copy constructor to fill each instance up the stack. Makes sense in a 'slap-your-forehead', 'of-course!' sort of way, when you see it.

    I created an example that uses the same construct to play with - you can watch the values being copied up the stack (in the debugger) as each frame backs out.

    A curiosity, though - I'm watching the destructor. I would expect the destructor to get called as it backs out of each frame (the frame's instance gets destroyed.) Instead, it only gets destroyed when the calling instance backs out. That's odd. It's almost as if there's a ref count on it.

    Tried both ms and gnu compilers - same behavior. Here's the code, if you're interested:

    Qt Code:
    1. #include <iostream>
    2.  
    3. using namespace std;
    4.  
    5. class TT
    6. {
    7. friend class A;
    8.  
    9. public:
    10. ~TT() { cout << "Destroyed!!" << endl; }
    11.  
    12. int A() { return a; }
    13. int B() { return b; }
    14. int C() { return c; }
    15. int D() { return d; }
    16.  
    17. private:
    18. int a, b, c, d;
    19. TT(int _a, int _b, int _c, int _d)
    20. { a = _a, b = _b, c = _c, d = _d; }
    21. };
    22.  
    23. class A
    24. {
    25. public:
    26. TT createTT(int a, int b, int c, int d) { return TT(a,b,c,d); }
    27.  
    28. void init_A()
    29. {
    30. TT item = getItemFromB();
    31. cout << "TT item: " << item.A() << ", " << item.B() << ", "
    32. << item.C() << ", " << item.D() << endl;
    33. }
    34.  
    35. virtual TT getItemFromB() = 0;
    36. };
    37.  
    38. class B : public A
    39. {
    40. public:
    41. virtual TT getItemFromB()
    42. { return createTT(1, 2, 3, 4); }
    43. };
    44.  
    45. int main (int argc, char **argv)
    46. {
    47. B b;
    48. b.init_A();
    49. return 0;
    50. }
    To copy to clipboard, switch view to plain text mode 

    Anyway, I need to get on to my little demo and try to get it coded up before the universe collapses.

    Thanks for the discussion. As always, C++ (and Qt) gives me something new to chew on when I dig under the covers.

    Cheers,
    rickb
    Last edited by rickbsgu; 18th December 2007 at 21:57.

  19. #18
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    33,359
    Thanks
    3
    Thanked 5,015 Times in 4,792 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows Android Maemo/MeeGo
    Wiki edits
    10

    Default Re: deleting internal pointer in QModelIndex

    Quote Originally Posted by rickbsgu View Post
    The MIs are short lived, there's no reason a tag-along piece of information can't be just as short-lived.
    "Can" and "should" are different things. Why destroy data that is perfectly valid and can be reused a moment later? Caches rarely delete data, instead they overwrite it with new data or mark old data as invalid. You can implement a simple cache in the model like so:
    Qt Code:
    1. private:
    2. ExtraData cache[128];
    3. //...
    4. QModelIndex Model::index(...){
    5. id = getCachedExtraDataFor(row, column, parent);
    6. return createIndex(row, column, id); // this version uses the internalId instead of internalPointer
    7. }
    To copy to clipboard, switch view to plain text mode 
    Here you have a simple 128 element cache that keeps data for 128 indexes (you can use a QVector and resize it if needed). You can then pass the index of the cache to the model index and be able to retrieve the extra data quickly when you need it. Of course the type of cache is irrelevant here (set, associative or mixed) just be aware you have to be able to tell if you hit or missed the cache before using the stored data. In the latter case you need to construct the extra data from scratch and store it in the cache. Of course the cache flicker issue steps in here, so it's your responsibility to design the cache properly.

    A curiosity, though - I'm watching the destructor. I would expect the destructor to get called as it backs out of each frame (the frame's instance gets destroyed.) Instead, it only gets destroyed when the calling instance backs out. That's odd. It's almost as if there's a ref count on it.
    Maybe that's how the compiler handles const references?


    Anyway, I need to get on to my little demo and try to get it coded up before the universe collapses.
    I heard someone whisper it won't happen before 2012, so you have still some time. And by then Qt5.0 will be out

  20. #19
    Join Date
    Aug 2008
    Location
    Nanjing, China
    Posts
    66
    Thanks
    12
    Thanked 2 Times in 2 Posts
    Qt products
    Qt4
    Platforms
    Unix/X11 Windows

    Wink Re: deleting internal pointer in QModelIndex

    I like this post!
    Jerry

Similar Threads

  1. Replies: 6
    Last Post: 21st September 2007, 13:51
  2. Replies: 2
    Last Post: 16th February 2006, 19:09

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
Digia, Qt and their respective logos are trademarks of Digia Plc in Finland and/or other countries worldwide.