For those following without the source, this is the focus of attention from the link in the OP:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
|
...
godlist = godlist->add_ordered(new Link{ God{ "Poseidon", "Greek", "", "Storms and earthquakes" } });
godlist = godlist->add_ordered(new Link{ God{ "Odin", "Norse", "The eightlegged horse Sleipnir", "The spear Gungnir" } });
godlist = godlist->add_ordered(new Link{ God{ "Zeus", "Greek", "", "Lightning" } });
godlist = godlist->add_ordered(new Link{ God{ "Tyr", "Norse", "", "Sword" } });
godlist = godlist->add_ordered(new Link{ God{ "Mercury", "Roman", "", "Caduceus staff" } });
godlist = godlist->add_ordered(new Link{ God{ "Jupiter", "Roman", "", "Staff" } });
godlist = godlist->add_ordered(new Link{ God{ "Loki", "Norse", "", "Trickery" } });
...
Link* Link::add_ordered(Link* n)
{
if (!n) return this;
Link* p = this;
bool before = false;
while (n->god.name < p->god.name) {
if (!before) before = true;
if (!p->prev) break;
else p = p->prev;
}
while (p->succ && n->god.name > p->god.name) p = p->succ;
if (before) {
cout << static_cast<int>(n->god.name[0]) << " < " << static_cast<int>(p->god.name[0]) << " insert(" << n->god.name << ") before " << p->god.name << "\n";
p = p->insert(n);
}
else {
cout << static_cast<int>(n->god.name[0]) << " > " << static_cast<int>(p->god.name[0]) << " add(" << n->god.name << ") after " << p->god.name << "\n";
p = p->add(n);
}
return p;
}
|
@grumblesnake,
This is an edge case issue.
All goes well up to the point where the list contains the following:
Jupiter
Mercury
Odin
Poseidon
Thor
Tyr
Zeus
|
Notice that "add_ordered" returns "p", which itself is returned from either "insert" or "add", which will be the new node in each case.
This means, for example, that when "add_ordered" for "Odin" returned, "godlist" in the calling code pointed to the link for "Odin", and when "add_ordered" for "Jupiter" returned, "godlist" pointed to "Jupiter" in the calling code.
This is the key point in the code which is an edge case situation. At this point, "godlist" in the calling code points to the link for "Jupiter", which at that moment is the top of the linked list. It may seem odd, but "godlist" may point to any link in the doubly linked list upon return. To this point in the execution, this edge case situation has not yet happened.
The edge case, specifically, is that "add_ordered" is now to be called for a new link "Loki", where "god_list" is the instance for which "add_ordered" is called, and so within that function "this" is the "Jupiter" link, the top of the list, and the new link is supposed to be inserted just after the "Jupiter" link, but before the "Mercury" link.
Here is where things go wrong, at the top of "add_ordered"
1 2 3 4 5 6 7
|
Link* p = this;
bool before = false;
while (n->god.name < p->god.name) {
if (!before) before = true;
if (!p->prev) break;
else p = p->prev;
}
|
The while loop will never execute its bracketed code, because the "n" link is "Loki", while the "p" link is "Jupiter", and since "Loki" is not less than "Jupiter", the loop will not execute.
The next code to execute is this:
while (p->succ && n->god.name > p->god.name) p = p->succ;
Here, since p->succ is not nullptr, and n->god.name is greater than p->god.name (being "Loki" is greater than "Jupiter", the loop does execute
p = p->succ;
It does this once, because p becomes the link for "Mercury", and thus stops the loop, with "p" pointing to "Mercury".
The requirement, at this point, is that "Loki" must be inserted before "Mercury".
The problem at this moment is that "before", a bool, is false.
It is false because the only place it can be made true is inside the loop at the top of this function, but that loop never executed. That left "before" false, when, under these circumstances, it would have to be true in order for the following to execute correctly
1 2 3 4 5 6 7 8
|
if (before) {
cout << static_cast<int>(n->god.name[0]) << " < " << static_cast<int>(p->god.name[0]) << " insert(" << n->god.name << ") before " << p->god.name << "\n";
p = p->insert(n);
}
else {
cout << static_cast<int>(n->god.name[0]) << " > " << static_cast<int>(p->god.name[0]) << " add(" << n->god.name << ") after " << p->god.name << "\n";
p = p->add(n);
}
|
This section, just after the loop that moved "p" to "Mercury", chooses how to apply the new node, either before or after the node "p".
As a result, it incorrectly executes the "after" clause, not the "before" clause as required.
There could be many "cures" for this.
One
might be something like:
1 2 3
|
while (p->succ && n->god.name > p->god.name) p = p->succ;
if ( n->god.name < p->god.name ) before = true;
|
Alternatively, the entire theory of the algorithm could be adjusted so as not to depend upon the "before" flag in the first place (which, at this point, it really doesn't if the above change is applied).