vendredi 4 décembre 2020

c++ Unable to use functions on Nodes in a vector

I am trying to make a c++ program that emulates parts of a linux filesystem.

I have a vector<Node*>. In order to add to it I have to first create a Node* that I then .push_back() to the vector, right? Is it possible to .push_back() without having to first create another Node* to push_back()?

Another problem is that I cannot invoke any methods on the nodes inside of the vector<Node*>. In order to do anything with them like, say, use a GetName() method on it, I have to first create a Node* with the index of the vector that has the data I want to use. I am fairly certain that this way of going about it is what's causing me so much grief.

What's killing me right now is that when I test all the associated methods in a separate main.cpp file, they all cooperate and do what I want. When I run them through a TEST_CASE however, they misbehave and cause a crash. I can't think of anything that could be causing these problems mostly because they behave in a main method.

For Reference, here's all my code: node.hpp:

#ifndef NODE_HPP
#define NODE_HPP
using namespace std;
#include <string>
#include <vector>
class Node {
    private:
        char type;
        string name;
        vector<Node*> children;
        Node* parent;
        void RemoveHelper(Node* subtree);
    public:
        Node(string name, char type);
        Node();
        ~Node();
        void AddChild(Node* child);
        void AddChild(string name, char type);
        bool RemoveChild(string name);
        void SetParent(Node* parent);
        void SetType(char type);
        Node* GetChild(string name);
        vector<Node*> GetChildren();
        Node* GetParent();
        char GetType();
        string GetName();
};

#endif

node.cpp:

#include <iostream>
#include <string>
#include <vector>
using namespace std;

#include "node.hpp"

void Node::RemoveHelper(Node* subtree){
    for (int j = 0; j < subtree->GetChildren().size(); j++){
        //cout << j << " ";
        Node* n = subtree->GetChildren()[j];
        if (!n->GetChildren().empty()){
            for (int i = 0; i < n->GetChildren().size(); i++){
                //cout << i << endl;
                RemoveHelper(subtree->GetChildren()[i]);
                subtree->RemoveChild(n->GetName());
            }
        }
    }
}

Node::Node(){
    name = "";
    type = ' ';
    parent = nullptr;
}

Node::Node(string name, char type){
    this->name = name;
    this->type = type;
    parent = nullptr;
}

Node::~Node(){
    children.clear();
}

void Node::AddChild(string name, char type){
    Node* n = new Node(name, type);
    AddChild(n);
    delete n;
}

void Node::AddChild(Node* child){
    children.push_back(child);
}

bool Node::RemoveChild(string name){
    Node* n = GetChild(name);
    for (int i = 0; i < children.size(); i++){
        if (n == children[i]){
            RemoveHelper(children[i]);
            children.erase(children.begin()+i);
            //delete n;
            return true;
        }
    }   
    return false;
}

void Node::SetParent(Node * parent){
    this->parent = parent;
}

void Node::SetType(char type){
    this->type = type;
}

Node* Node::GetChild(string name){
    for (int i = 0; i < children.size(); i++){
        Node* n = children[i];
        if (n->GetName() == name){
            //delete n;
            return children[i];
        }
        //delete n;
    }
    return nullptr;
}

vector<Node*> Node::GetChildren(){
    return children;
}

Node* Node::GetParent(){
    return parent;
}

char Node::GetType(){
    return type;
}

string Node::GetName(){
    return name;
}

filesystem.hpp:

#ifndef FILESYSTEM_HPP
#define FILESYSTEM_HPP
using namespace std;
#include <string>
#include "node.hpp"

class FileSystem {
    private:
        Node* root;
        Node* currentDirectory;
        void AddNode(Node* newNode);
        Node* FindNode(string name);
    public:
        FileSystem();
        ~FileSystem();
        string mkdir(string name);
        string touch(string name);
        string pwd();
        string ls();
        string rm(string name);
        string mv(string from, string to);
        string cd(string dirname);
};

#endif

filesystem.cpp

#include <iostream>
#include <string>
using namespace std;

#include "filesystem.hpp"
#include "node.hpp"

void FileSystem::AddNode(Node* newNode){
    currentDirectory->AddChild(newNode);
    currentDirectory->GetChild(newNode->GetName())->SetParent(currentDirectory);
}

Node* FileSystem::FindNode(string name){
    return currentDirectory->GetChild(name);
}

FileSystem::FileSystem(){
    root = new Node("root", 'd');
    currentDirectory = root;
}

FileSystem::~FileSystem(){
    for (int i = root->GetChildren().size(); i > i; i--){
        Node* n = root->GetChildren()[i];
        root->RemoveChild(n->GetName());
        //delete n;
    }
    delete root;
    
    for (int i = currentDirectory->GetChildren().size(); i > 0; i--){
        Node* n = currentDirectory->GetChildren()[i];
        currentDirectory->RemoveChild(n->GetName());
        delete n;
    }
    delete currentDirectory;
}

string FileSystem::mkdir(string name){
    if (currentDirectory->GetChild(name) == nullptr){
        Node* n = new Node(name, 'd');
        AddNode(n);
        
        return "directory " + name + " created successfully";
    }
    return "Error: " + name + " exists";
}

string FileSystem::touch(string name){
    if (currentDirectory->GetChild(name) == nullptr){
        currentDirectory->AddChild(name, 'f');
        return "file " + name + " created successfully";
    }
    return "Error: " + name + " exists";
}

// I know that this only works for up to 3 directories. That's fine for this project.
string FileSystem::pwd(){
    string ret = "/" + root->GetName();
    if (root != currentDirectory){
        if (currentDirectory->GetParent() != root){
            ret += "/" + currentDirectory->GetParent()->GetName();
        }
        ret += "/" + currentDirectory->GetName();
    }
    return ret;
}

string FileSystem::ls(){
    string ret = "";
    for (int i = 0; i < currentDirectory->GetChildren().size(); i++){
        Node* n = currentDirectory->GetChildren()[i];
        ret += n->GetType();
        ret += " " + n->GetName() + "\n";
        //delete n;
    }
    return ret;
}

string FileSystem::rm(string name){
    if (currentDirectory->RemoveChild(name)){
        return name + " removed successfully";
    }
    return "No such file or directory";
}

string FileSystem::mv(string from, string to){
    currentDirectory->AddChild(to, currentDirectory->GetChild(from)->GetType());
    currentDirectory->RemoveChild(from);
    return "file/dir renamed successfully";
}

string FileSystem::cd(string dirname){
    cout << currentDirectory->GetChild("dir");
    if ((dirname == ".." && currentDirectory == root) || dirname == currentDirectory->GetName()){
        return "can't change to directory " + dirname;
    }
    else if (dirname == ".."){
        currentDirectory = root;
    }
    else if (currentDirectory->GetChild(dirname) == nullptr){
        return "can't change to directory " + dirname;
    }
    else{
        currentDirectory = currentDirectory->GetChild(dirname);
    }
    return pwd();
}

One of the test files:

#include "catch/catch.hpp"
#include "../filesystem.hpp"

TEST_CASE("Test creating a file system")
{
    FileSystem fs;
    CHECK ("/root" == fs.pwd());
    CHECK ("" == fs.ls());
}

TEST_CASE ("Test touch and mkdir")
{
    FileSystem fs;
    CHECK ("file file1 created successfully" == fs.touch("file1"));
    CHECK ("Error: file1 exists" == fs.touch("file1"));
    CHECK ("file file2 created successfully" == fs.touch("file2"));
    CHECK ("directory dir1 created successfully" == fs.mkdir("dir1"));
    CHECK ("Error: dir1 exists" == fs.mkdir("dir1"));
    CHECK ("Error: dir1 exists" == fs.touch("dir1"));
    CHECK ("f file1\nf file2\nd dir1\n" == fs.ls());
    CHECK ("/root" == fs.pwd());
}

If there is anything else anyone catches that could help avoid other issues I haven't caught yet, that would also be greatly appreciated.

Aucun commentaire:

Enregistrer un commentaire