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.
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.