Search code examples
c++structscopedirectory-structure

A little confused on scope and how to create nested structs for directory tree


I'm trying to show a directory tree UI of an unknown amount of nested folders that is read from a flat file. My idea was to use a struct that could point to children and parents like so:

struct audioSelectTreeItem {
    FString folderName;
    FString folderPath;
    struct audioSelectTreeItem* folderParentItem;
    TArray<struct audioSelectTreeItem*> childFolderItems;
};

When I read the file of saved directories I save any subfolders in childFolderItems and the parent in folderParentItem. That way in my interface if someone wants to go up a folder I look at folderParentItem and then list out it's subfolders with childFolderItems.

The problem I'm running into is how to save this. I made a function that loops through folders and children and can create all these structs and save them. However the issue I think is if I create a struct in a loop then save a pointer to it in the previous struct (children folders) I cannot access that from anywhere else in the program.

Is there a way to accomplish creating these more permanent, and also how would I go about cleaning up when done?

Edit: Wrote as if I'm reading live directories. Was a text file with directory information I need to put into a tree interface.

Update: Here's a smaller example of what the loop does:

void AMyPlayerController::showAudioPicks() {
for (int counter = startCount; counter < fileStringArray.Num(); counter++) {  //startcount 3

        audioSelectTreeItem newTreeItem;
        newTreeItem.folderPath = fileStringArray[counter];
        mainListTree.childFolderItems.Add(&newTreeItem);

...

So my issue lies with creating these new audioSelectTreeItem's to add nested in structs but keep them accessible elsewhere in the program.


Solution

  • What you are doing here is creating a pointer to a stack variable which is scoped to the for loop and destroyed on every loop:

    audioSelectTreeItem newTreeItem;
    newTreeItem.folderPath = fileStringArray[counter];
    mainListTree.childFolderItems.Add(&newTreeItem);
    

    If you want to create an object (or structure) on the heap instead of the stack, you have to use new:

    audioSelectTreeItem *newTreeItem = new audioSelectTreeItem;
    newTreeItem->folderPath = fileStringArray[counter];
    mainListTree.childFolderItems.Add(newTreeItem);
    

    But if you do so, you have to make sure the object is freed with delete. Typically this is done in the destructor of the audioSelectTreeItem class (or structure):

    class audioSelectTreeItem {
    public:
        ~audioSelectTreeItem()
        {
            for (int i = 0; i < childFolderItems.Num(); i++)
                delete childFolderItems[i];
        }
    
        FString folderName;
        FString folderPath;
        class audioSelectTreeItem* folderParentItem;
        TArray<class audioSelectTreeItem*> childFolderItems;
    };
    

    The parent is the owner of its children, so there is no sense in deleting folderParentItem. As soon as you delete the root its destructor is deleting all of his children and so on.

    In current C++ there are safer solutions for trees like this. For example you could use managed pointers instead of plain pointers. This creates a bit of an overhead because of reference counting, but you never have to free the heap yourself:

    #include <memory>
    
    class audioSelectTreeItem
    {
    public:
        FString folderName;
        FString folderPath;
        std::weak_ptr<audioSelectTreeItem> folderParentItem;
        TArray<std::shared_ptr<audioSelectTreeItem>> childFolderItems;
    };
    

    Do not use a shared_ptr for the folderParentItem reference. This would lead to cyclic dependency (parent references child and child references parent, so none of them can be deleted)

    And a personal opinion: Type names should start with an upper case letter.