TLinkedList Help

Hey Peeps,

So I am working with TLinkedList as the topic describes. I have hit a roadblock. I understand how Linked Lists work just fine, which is not the problem. I have the following:



    for( auto stage : QuestStages )
    {
        UQuestStage* newStage = NewObject<UQuestStage>(this->GetOuter(), stage->GetFName(), RF_NoFlags, stage->GetDefaultObject(), true);

        UE_LOG(QuestSystem, Warning, TEXT("newStage Name : %s"), *newStage->Name);

        if( !newStage )
        {
            UE_LOG(QuestSystem, Warning, TEXT("Quest Stage Object not valid!"));
            return;
        }

        TLinkedList<UQuestStage*> LinkList = TLinkedList<UQuestStage*>(newStage);

        if( !LinkedQuestStages.GetPrevLink() || !LinkedQuestStages.IsLinked() )
        {
            LinkedQuestStages = TLinkedList<UQuestStage*>(newStage);
            LinkedQuestStagesHead = &LinkedQuestStages;
            LinkList.LinkHead(LinkedQuestStagesHead);

            continue;
        }
        else
        {
            if( LinkedQuestStagesHead->GetPrevLink() || LinkedQuestStages.GetPrevLink() )
            {
                LinkList.LinkAfter(*LinkedQuestStagesHead->GetPrevLink());
            }
        }
    }

    for( auto Node : LinkedQuestStages)
    {
        UE_LOG(QuestSystem, Warning, TEXT("Stage Name: %s"), *Node->Name);
    }
}


For some reason, after the first loop, the LinkedQuestStagesHead and LinkedQuestStages GetPrevLink() and GetNextLink() are NULL and I am not sure as to why. I highly doubt it’s a garbage collection issue.

QuestStages is a


TArray<TSubclassOf<UQuestStage>>

Any ideas?

Still confused, it’s like the Previous and Next links are not getting updated. The loop runs through 3 times and the only data I have in my locals/autos when debugging are that the last loop replaces the GetNextLink() with the new link, so it’s not linking after, it’s replacing.



TLinkedList<UQuestStage*> LinkList = TLinkedList<UQuestStage*>(newStage);

        if( !LinkedQuestStages.GetPrevLink() || !LinkedQuestStages.IsLinked() )
        {
            LinkedQuestStages = TLinkedList<UQuestStage*>(newStage);
            LinkedQuestStagesHead = &LinkedQuestStages;
            LinkedQuestStages.LinkHead(LinkedQuestStagesHead);
        }
        else
        {
            if(!LinkList.IsLinked())
            {
                LinkList.LinkAfter(LinkedQuestStagesHead->GetNextLink());
            }            
        }




    /**
     * Adds this element as the head of the linked list, linking the input Head pointer to this element,
     * so that when the element is linked/unlinked, the Head linked list pointer will be correctly updated.
     *
     * If Head already has an element, this functions like LinkBefore.
     *
     * @param Head        Pointer to the head of the linked list - this pointer should be the main reference point for the linked list
     */


Is the comments I got from LinkHead, it says to use the head ptr as the main reference to the linked list.

So after talking to KnownBug on Discord, I have come with the solution and an explanation.

The solution to TLinkedList:


UQuestStage* newStage = NewObject<UQuestStage>(this->GetOuter(), NAME_None, RF_NoFlags, stage->GetDefaultObject(), true);

TLinkedList<UQuestStage*>* const NewStage = new TLinkedList<UQuestStage*>( newStage );

if (!QuestStagesList)
{
    QuestStagesList = NewStage;
    QuestStagesListLast = QuestStagesList;
}
else
{
    NewStage->LinkAfter(QuestStagesListLast);
    QuestStagesListLast = NewStage;
}

//Iteration
for (UQuestStage Stage : *QuestStagesList)
{
}


As you can see, we have to manually keep track of our list data, and manually link the new data after the Tail of the Linked List, making our new data the Tail. Also, we have to manually garbage collect the TLinkedList containers, via


Unlink()

or


delete ;

Another alternative, which I have working, and works like a charm, which also includes garbage collection is

like so:



 UQuestStage* newStage = NewObject<UQuestStage>(this->GetOuter(), NAME_None, RF_NoFlags, stage->GetDefaultObject(), true);

if( !newStage )
{
    UE_LOG(QuestSystem, Warning, TEXT("Quest Stage Object not valid!"));
    return;
}

if ( !LQuestStages )
{
    LQuestStages = new TDoubleLinkedList<UQuestStage*>();
    LQuestStages->AddHead(newStage);
}
else
{
    LQuestStages->AddTail(newStage);
}

for( auto Node : *LQuestStages)
{
    UE_LOG(QuestSystem, Warning, TEXT("Stage Name: %s"), *Node->Name);
}


As you can see, if LQuestStages is NULL, we Initialize LQuestStages, and then add newStage as the LQuestStages(TDoubleLinkedList)s head via “AddHead()”. Then throughout the next iterations, we simply push the newStage object to the end of the linked list(Tail) via AddTail() which acts like an array append.

And if you want to find an object in the linked list:



LQuestStages->FindNode(ObjectIamLookingFor(UQuestStage*))->GetValue();


1 Like

Thanks for this!

To expand on this, if allocation of the list isn’t necessary, it can be done like so:

UQuestStage* newStage = NewObject<UQuestStage>(this->GetOuter(), NAME_None, RF_NoFlags, stage->GetDefaultObject(), true);

if( !newStage )
{
    UE_LOG(QuestSystem, Warning, TEXT("Quest Stage Object not valid!"));
    return;
}

TLinkedDoubleList<UQuestStage> LQuestStages;

if ( LQuestStages.IsEmpty() )
{
    LQuestStages.AddHead(newStage);    
}
else
{
    LQuestStages.AddTail(newStage);
}

for( auto QuestStage : LQuestStages)
{
    UE_LOG(QuestSystem, Warning, TEXT("Stage Name: %s"), *QuestStage->Name);
}
1 Like