[multi_index] function to force to recreate indexes?
I have two multi_index_containers with smart pointers which refer to the same objects (at least some of the pointers refer to the objects). When I access and modify an object via an index of one of the two multi_index_containers the indexes of that container are recreated as far as I understand. But how do I make the other multi_index_container recreate its indexes? I can't and don't need to modify the object again. Is there anything to force a multi_index_container to recreate its indexes? Boris
Hello Boris, Boris ha escrito:
I have two multi_index_containers with smart pointers which refer to the same objects (at least some of the pointers refer to the objects). When I access and modify an object via an index of one of the two multi_index_containers the indexes of that container are recreated as far as I understand.
Correct, if you've used replace(), modify() or modify_key() to do the modification.
But how do I make the other multi_index_container recreate its indexes? I can't and don't need to modify the object again. Is there anything to force a multi_index_container to recreate its indexes?
Let's call m1 the first multi_index_container (the one you're modyfing the element through) and m2 the second one. If you've got an m2-iterator to the modified element, let's call it it2, then you can do the following just after the modification: struct null_modifier { template<typename T> void operator()(const T&)const{} }; ... m2.modify(it2,null_modifier()); which forces reindexation without actually touching *it2. Is this what you're after? Thanks for using Boost.MultiIndex, Joaquín M López Muñoz Telefónica, Investigación y Desarrollo
On Wed, 21 Feb 2007 19:10:03 +0200, Joaquín Mª López Muñoz
[...] struct null_modifier { template<typename T> void operator()(const T&)const{} };
...
m2.modify(it2,null_modifier());
which forces reindexation without actually touching *it2. Is this what you're after? Thanks for using Boost.MultiIndex,
Okay, thanks! I thought there might be something else like m2.refresh(). It would be nice if the multi_index_container provided such a method as you could change objects hold by the container directly (not through multi_index_container methods) and then make the container recreate the indexes. Thanks for your nice library, :) Boris
Boris ha escrito:
On Wed, 21 Feb 2007 19:10:03 +0200, Joaquín Mª López Muñoz
wrote:
[...]
m2.modify(it2,null_modifier());
which forces reindexation without actually touching *it2. Is this what you're after? Thanks for using Boost.MultiIndex,
Okay, thanks! I thought there might be something else like m2.refresh(). It would be nice if the multi_index_container provided such a method as you could change objects hold by the container directly (not through multi_index_container methods) and then make the container recreate the indexes.
First of all, one important note of warning: externally modifying elements is an outright violation of Boost.MultiIndex invariant contract, so strictly speaking it results in undefined behavior --even if you resync right after the modification as I've shown in my previous post. Given that premise, a refresh() facility cannot even be formulated as it'd operate on a basically broken container! That said, you can force the container to fully recreate its indices after massive modification with the following non-documented, non-supported trick: // modify elements thru m1 ... // force index recreation on m2 // mic_t2 is the type of m2 mic_t2 tmp(m2.begin(),m2.end()); m2.swap(tmp); I'm almost regretting having posted this trick :) since all of this is really completely unguaranteed behavior --so if you can do things in a safer fashion please go that way.
Thanks for your nice library, :) Boris
You're welcome. Joaquín M López Muñoz Telefónica, Investigación y Desarrollo
On Wed, 21 Feb 2007 20:37:42 +0200, Joaquín Mª López Muñoz
[...]I'm almost regretting having posted this trick :) since all of this is really completely unguaranteed behavior --so if you can do things in a safer fashion please go that way.
I won't use the trick but the code you posted before. :) But your proposal with the null_modifier looks like it can be implemented in a refresh() method of the multi_index_container and thus used by everyone to recreate indexes reliably? Boris
----- Mensaje original -----
De: Boris
On Wed, 21 Feb 2007 20:37:42 +0200, Joaquín Mª López Muñoz
wrote: [...]I'm almost regretting having posted this trick :) since all of this is really completely unguaranteed behavior --so if you can do things in a safer fashion please go that way.
I won't use the trick but the code you posted before. :) But your proposal with the null_modifier looks like it can be implemented in a refresh() method of the multi_index_container and thus used by everyone to recreate indexes reliably?
Even the null_modifier thing is not guaranteed behavior; but even overlooking that fact, you can't use it to implement refresh(). I guess you have something like the following in mind: void refresh() { for(iterator it=begin();it!=end();++it){ modify(it,null_modifier()); } } right? This has two problems, the latter of a fundamental nature: 1. Since elements are repositioned as you traverse them, you can skip elements, visit repositioned elements twice, etc. 2. modify() is not guaranteed to work in this context, as all the elements are shuffled, and modify() assumes that the only displaced element (at most) is the one being directly dealt with. This is not a theoretical limitation, if you try to do it chances are that the program crashes. At the end of the day, the problem is not whether we can have a reliable implementation of refresh(): you enter into the realm of undefined behavior once you modify an element externally to the container. By the time you are to call refresh(), you have already performed an illegal action. Joaquín M López Muñoz Telefónica, Investigación y Desarrollo
On Wed, 21 Feb 2007 23:17:28 +0200, JOAQUIN LOPEZ MU?Z
[...]At the end of the day, the problem is not whether we can have a reliable implementation of refresh(): you enter into the realm of undefined behavior once you modify an element externally to the container. By the time you are to call refresh(), you have already performed an illegal action.
Thanks for all your explanations! They will help me definitly to build a more robust program around multi_index_container. If I understand you correctly it all comes down to what is explained in the documentation anyway: Use replace() and not modify() if you want to be safe? Boris
Boris ha escrito:
On Wed, 21 Feb 2007 23:17:28 +0200, JOAQUIN LOPEZ MU?Z
wrote: [...]At the end of the day, the problem is not whether we can have a reliable implementation of refresh(): you enter into the realm of undefined behavior once you modify an element externally to the container. By the time you are to call refresh(), you have already performed an illegal action.
Thanks for all your explanations! They will help me definitly to build a more robust program around multi_index_container.
If I understand you correctly it all comes down to what is explained in the documentation anyway: Use replace() and not modify() if you want to be safe?
Umm, no, sorry, I think I haven't explained myself clearly... Both replace() and modify() are safe if used correctly --the difference is that replace() keeps the original content if the modification is incompatible with the uniqueness constraints of the container. What I'm referring to is: consider the following piece of code: typedef multi_index_container< shared_ptr<int>, indexed_by<...>
mic_t; ... mic_t m; m.insert(shared_ptr<int>(new int(0))); mic_t::iterator it=m.begin(); **it=3; // A m.modify(it,null_modifier()); // B
OK, this exemplifies the "resync" trick we were discussing a couple of posts ago. Although this works in practice, the problem is that line A is *illegal* as far as the semantics of Boost.MultiIndex (or STL associative containers for that matter) go: you are not allowed to externally modify an element if such modification results in a violation of the container invariant, namely that the elements are kept in ascending order. So, although //B restores the invariant, strictly speaking the code is illegal because of //A. When both //B is executed right after //A you are in practical terms safe, but *only* in practical terms, the formal specification of Boost.MultiIndex does not accept the sequence //A,//B as a valid operation. If the null_modifier trick is illegal but safe in practice, going further than that --doing massive external modifications-- is equally illegal and besides highly dangerous, that's why it's sensible not to consider a refresh() function that would in principle ratify massive external modifications. The subject is a little tricky as it deals with safety in practical and theoretical terms, so if anything's still not clear enough please do not hesitate to come back to me. Joaquín M López Muñoz Telefónica, Investigación y Desarrollo
On Thu, 22 Feb 2007 09:25:24 +0200, Joaquín Mª López Muñoz
[...]The subject is a little tricky as it deals with safety in practical and theoretical terms, so if anything's still not clear enough please do not hesitate to come back to me.
I see. To put it in my own words: Any direct modification of items in a multi_index_container results in undefined behavior as there is no way for the multi_index_container to make sure that the modification is actually allowed for the defined indexes. While it might be possible for a developer to make sure himself that a modification is allowed there can never be a guarantee by the specification of multi_index_container as the question if it's really allowed depends on the implementation of multi_index_container. Is this correct now? :) Boris
Boris ha escrito:
On Thu, 22 Feb 2007 09:25:24 +0200, Joaquín Mª López Muñoz
wrote: [...]The subject is a little tricky as it deals with safety in practical and theoretical terms, so if anything's still not clear enough please do not hesitate to come back to me.
I see. To put it in my own words: Any direct modification of items in a multi_index_container results in undefined behavior as there is no way for the multi_index_container to make sure that the modification is actually allowed for the defined indexes. While it might be possible for a developer to make sure himself that a modification is allowed there can never be a guarantee by the specification of multi_index_container as the question if it's really allowed depends on the implementation of multi_index_container. Is this correct now? :)
Almost :) You can do modifications if these do not affect the relevant keys of the element; it is modifications affecting the key(s) that are banned. Joaquín M López Muñoz Telefónica, Investigación y Desarrollo
participants (3)
-
"JOAQUIN LOPEZ MU?Z"
-
Boris
-
Joaquín Mª López Muñoz