0

I’m a long time C# developer trying to learn C++, and I’m writing a program to recursively process a folder structure to build a hierarchy of Directory objects. BTW, I’m aware of recursive_directory_iterator, but I don’t think it would give me the level of control I need. Why is my child_dirs std::list not retaining the objects that are added to it? I’m pretty sure it’s because it’s going out of scope, but I don’t understand that because the top level Directory is in scope, so it’s members should be too shouldn’t they? I’m thinking once I run this on a starting path that has tons of children (e.g., C:), I may need to switch over to dynamic storage, but I’m trying to keep thing simpler for now … or is there no way to do what I’m trying to do w/out switching to Dynamic storage?

Directory.h

#pragma once
#include <list>
#include <filesystem>
#include <iostream>
#include "FileEntry.h"
class Directory
{
public:
    std::string debug;
    std::string path;
    std::list<FileEntry> child_files {};
    std::list<Directory> child_dirs {};

    Directory();
    Directory(std::filesystem::directory_entry in_path);
    Directory(std::string in_path);
};

Directory.cpp

#include <list>
#include <filesystem>
#include "Directory.h"

//Constructors
Directory::Directory(){
}

Directory::Directory(std::filesystem::directory_entry in_path) {
    path = in_path.path().string();
}
Directory::Directory(std::string in_path) {
    path = in_path;
}

FileEntry.h

#pragma once
#include <string>
#include <filesystem>
class FileEntry
{
public:
    std::string FileName = "";
    uintmax_t SizeInBytes = 0;

    FileEntry(std::filesystem::directory_entry entry) 
    {
        FileName = entry.path().string();
        SizeInBytes = entry.file_size();
    }
};

Main.cpp

void process_dir(Directory& path);
void print_dir(Directory& dir);

int main()
{
    Directory start_dir("C:\\Users\\steve\\source\\repos\\topdir");
    std::cout << "--- Starting Dir: " << start_dir.path << " ---" << std::endl;

    process_dir(start_dir);

    std::cout << "Done." << std::endl;
    print_dir(start_dir);
}

void process_dir(Directory& dir)
{
    std::cout << "Dir: " << dir.path << " child_dirs.size: " << dir.child_dirs.size() << std::endl;
    for (std::filesystem::directory_entry entry : std::filesystem::directory_iterator(dir.path))
    {
        if (entry.is_directory()) 
        {
            Directory new_dir(entry);
            dir.child_dirs.push_back(new_dir);
            process_dir(new_dir);
        }
        if (entry.is_regular_file() || entry.is_block_file() || entry.is_character_file()) {
            dir.child_files.push_back(entry);
            std::cout << "   File: " << entry.path() << " child_files.size: " << dir.child_files.size() << std::endl;
        }
    }
}

void print_dir(Directory& dir)
{
    std::cout << "DIR: " << dir.path << " child_files.size: " << dir.child_files.size() << std::endl;
    for (FileEntry file : dir.child_files)
    {
        std::cout << " File: " << file.FileName << std::endl;
    }
    for (Directory child_dir : dir.child_dirs)
    {
        print_dir(child_dir);
    }
}

Output:

--- Starting Dir: C:\Users\steve\source\repos\topdir ---
Dir: C:\Users\steve\source\repos\topdir child_dirs.size: 0
Dir: C:\Users\steve\source\repos\topdir\middir1 child_dirs.size: 0
Dir: C:\Users\steve\source\repos\topdir\middir1\lowdir1 child_dirs.size: 0
   File: "C:\\Users\\steve\\source\\repos\\topdir\\middir1\\lowdir1\\lowdir1filea.txt" child_files.size: 1
   File: "C:\\Users\\steve\\source\\repos\\topdir\\middir1\\lowdir1\\lowdir1fileb.txt" child_files.size: 2
Dir: C:\Users\steve\source\repos\topdir\middir1\lowdir2 child_dirs.size: 0
   File: "C:\\Users\\steve\\source\\repos\\topdir\\middir1\\lowdir2\\lowdir2filea.txt" child_files.size: 1
   File: "C:\\Users\\steve\\source\\repos\\topdir\\middir1\\lowdir2\\lowdir2fileb.txt" child_files.size: 2
   File: "C:\\Users\\steve\\source\\repos\\topdir\\middir1\\middir1filea.txt" child_files.size: 1
   File: "C:\\Users\\steve\\source\\repos\\topdir\\middir1\\middir1fileb.txt" child_files.size: 2
Dir: C:\Users\steve\source\repos\topdir\middir2 child_dirs.size: 0
   File: "C:\\Users\\steve\\source\\repos\\topdir\\middir2\\middir2filea.txt" child_files.size: 1
   File: "C:\\Users\\steve\\source\\repos\\topdir\\middir2\\middir2fileb.txt" child_files.size: 2
   File: "C:\\Users\\steve\\source\\repos\\topdir\\topdirfilea.txt" child_files.size: 1
   File: "C:\\Users\\steve\\source\\repos\\topdir\\topdirfileb.txt" child_files.size: 2
Done.
DIR: C:\Users\steve\source\repos\topdir child_files.size: 2
 File: C:\Users\steve\source\repos\topdir\topdirfilea.txt
 File: C:\Users\steve\source\repos\topdir\topdirfileb.txt
DIR: C:\Users\steve\source\repos\topdir\middir1 child_files.size: 0
DIR: C:\Users\steve\source\repos\topdir\middir2 child_files.size: 0
stevoh
  • 53
  • 3
  • 5
    You perform `dir.child_dirs.push_back(new_dir)` before you call `process_dir(new_dir);`. So the copy of `new_dir` that gets added to `dir.child_dirs` is the unprocessed version. The processing happens to the local variable that goes out of scope, which is separate from the one that gets saved into `dir.child_dirs`. – Nathan Pierson Aug 06 '23 at 20:22
  • 2
    Unrelated to your problem but in general `std::vector` is better than `std::list` in most cases – Alan Birtles Aug 06 '23 at 20:59
  • @NathanPierson, swapping the order of those 2 statements appears to work. I think my C# experience may be messing me up. I'm viewing new_dir as in instance of an class, and assuming that I'm passing an address around, including into my list. If my list is getting a copy, I can see where the order would matter. Is that what's going on here? Thanks. – stevoh Aug 06 '23 at 22:46
  • 1
    `new_dir` _is_ an instance of a class. But that doesn't mean you're passing an address around. The address of `new_dir` is `&new_dir`, not `new_dir` directly. And the exact way a class gets passed to a function depends on the signature of that function. In [std::list::push_back's](https://en.cppreference.com/w/cpp/container/list/push_back) case, overload (1) is chosen which accepts a const reference to `new_dir` and adds a copy of it to the list. – Nathan Pierson Aug 07 '23 at 01:04
  • @NathanPierson, thanks for clearing that up. Man, I need to get my head around the fact the objects aren't always passed around by ref like in C#. If you can enter your replies as an answer, I can mark it as accepted. – stevoh Aug 07 '23 at 16:47
  • Does this answer your question? [How to pass objects to functions in C++?](https://stackoverflow.com/questions/2139224/how-to-pass-objects-to-functions-in-c) – Nathan Pierson Aug 07 '23 at 17:10

0 Answers0