Allow for SortedSet to be notified of element mutation
Submitted by Jim Nelson
Link to original bug (#736444)
Description
If an element in the SortedSet is mutated in such a way that it affects the comparator's operation, then various problems will result, including the inability to remove that object from the SortedSet. For example, this will assert:
class Xyzzy : Object, Gee.Comparable<Xyzzy>
{
public int value { get; set; }
public Xyzzy(int value) {
this.value = value;
}
public int compare_to(Xyzzy other) {
if (this == other)
return 0;
return value - other.value;
}
}
void main() { Xyzzy x1 = new Xyzzy(1); Xyzzy x2 = new Xyzzy(2); Xyzzy x3 = new Xyzzy(3);
Gee.TreeSet`<Xyzzy>` redblack = new Gee.TreeSet`<Xyzzy>`();
redblack.add(x1);
redblack.add(x2);
redblack.add(x3);
x1.value = 4;
assert(redblack.contains(x1));
}
Note that this fails with or without the identity test in compare_to() because the new value leads the internal iterator down the wrong tree branch.
Worse, if this code is used after x1.value is set to 4:
Gee.Iterator`<Xyzzy>` iter = redblack.iterator();
while (iter.next()) {
if (iter.get() == x1)
iter.remove();
}
Gee will assert with:
ERROR:treeset.c:3258:gee_tree_set_iterator_real_remove: assertion failed: (success)
So, the user can't even manually remove and re-add the element to re-sort it into position.
I realize this is not a bug per se, but in a more complicated program this issue may be indeterminate in reproducibility and even more difficult to track down.
In the least, this situation should be documented so the caller understands the limitations of TreeSet (and probably most SortedSet implementations).
What I'm proposing is a new method for SortedSet:
public abstract void mutated(G element, Gee.EqualDataFunc? equal_func = null);
The implementation would use the equal_func (which is obtained from Functions if null) instead of CompareDataFunc to locate the element, remove it, then re-add it using the CompareDataFunc.
If mutated() seems too specific to the problem, it would at least be helpful if there was some way to remove the element using a supplied EqualDataFunc rather than rely solely on the CompareDataFunc for traversal.
Version: git master