I have a 8 files FileSystem.cpp
, Node.cpp
, Directory.cpp
and File.cpp
and their header hpp
files.
this is basic model of in memory file system.
class FileSystem
is responsible for creating root Directory
which has its own nodes aka files or directories.
The class Node
is parent class for both Directory
and File
. I need to include Node
into both files to inherit from, but inside Node class implementation I need to access the File
and Directory
to do some operations. If I try to make foward decleration, I will got undefined refernces of class members. I read some articles about this, but I can't solve my problem with headers. What is the right approach for solving this kind of problem in c++. How/should I use header to solve this kind of problem?
Please note my background is different, so I think have missed something on c++ about headers and recursive declaration.
Here the source of these files.
FileSystem.cpp
#include <string>
#include <vector>
#include <iostream>
using namespace std;
#include "Node.hpp"
#include "Directory.hpp"
class FileSystem
{
private:
// methods
// attributes
string name;
Directory *root;
friend class Directory;
public:
int get_fresh_uid()
{
return 10;
}
FileSystem(string in_name)
{
name = in_name;
root = new Directory(this, get_fresh_uid(), "root", nullptr);
}
~FileSystem() {
}
// accessors
string get_name()
{
return name;
}
Directory *get_root()
{
return root;
}
friend ostream &operator<<(ostream &output, FileSystem &fs)
{
return output;
}
};
Node.cpp
#include <string>
#include "Directory.cpp"
#include "File.cpp"
class FileSystem;
using namespace std;
class Node
{
protected:
FileSystem *fs;
int uid;
string name;
Directory *parent;
Node(FileSystem *fs_in, int uid_in, string name_in, Directory *parent_in)
{
fs = fs_in;
name = name_in,
uid = uid_in;
parent = parent_in;
}
virtual void print_to(ostream os, int num) = 0;
public:
~Node() {
}
// accessors
string get_name()
{
return name;
}
// methods
virtual bool is_directory() = 0;
virtual Directory *to_directory() = 0;
virtual File *to_file() = 0;
virtual int size() = 0;
};
File.cpp
#include "Node.hpp"
#include "Directory.hpp"
#include "FileSystem.hpp"
class File : public Node
{
private:
string content;
~File() {
}
public:
File(FileSystem *fs_in, int uid_in, string name_in, Directory *parent_in);
// accessors
void set_content(const string &value)
{
content = value;
}
// mutators
string get_content()
{
return content;
}
// methods
int size()
{
return content.size();
}
bool is_directory()
{
return false;
}
Directory *to_directory()
{
return nullptr;
}
File *to_file()
{
return this;
}
void print_to(ostream os, int num)
{
// os << "+ file: " << name << ", uid: " << uid << ", size: " << size << ", " << "content: " << content;
}
};
Directory.cpp
#include "File.hpp"
#include "FileSystem.hpp"
#include "Node.hpp"
class Directory : public Node
{
private:
Directory(FileSystem *fs_in, int uid_in, string name_in, Directory *parent_in);
~Directory();
// attributes
vector<Node *> children;
// methods
bool child_exists(string name)
{
for (int i = 0; i < children.size(); i++)
{
return children[i]->get_name() == name;
}
}
friend class FileSystem;
public:
// accessors
string get_name() {
return name;
}
//methods
int size()
{
int sum;
for (int i = 0; i < children.size(); i++)
{
Node *child = children[i];
sum += child->size();
}
return sum;
}
bool is_directory()
{
return true;
}
Directory *to_directory()
{
return this;
}
File *to_file()
{
return nullptr;
}
File *add_file(string filename)
{
// check whether same name child node exists
if (child_exists(filename))
return nullptr;
// create file
File *new_file = new File(fs, fs->get_fresh_uid(), filename, this);
// add file to the children vector
children.push_back(new_file);
return new_file;
}
Directory *add_directory(string dirname)
{
// check whether same name child node exists
if (child_exists(dirname))
return nullptr;
// create file
Directory *new_dir = new Directory(fs, fs->get_fresh_uid(), dirname, this);
// add dir to the children vector
children.push_back(new_dir);
return new_dir;
}
bool remove_node(string name)
{
for (int i = 0; i < children.size(); i++)
{
if (children[i]->get_name() == name)
{
children.erase(children.begin() + i);
return true;
}
}
return false;
}
Node *find_node(string name)
{
for (int i = 0; i < children.size(); i++)
{
if (children[i]->get_name() == name)
return children[i];
}
return nullptr;
}
void print_to(ostream os, int num)
{
// os << "+ directory: " << name << ", uid: " << uid << ", size: " << size;
}
friend ostream &operator<<(ostream &output, Directory &dir)
{
return output;
}
};
Directory::~Directory()
{
for (int i = 0; i < children.size(); i++)
{
delete children[i];
}
}
#include "Directory.cpp" #include "File.cpp"
I recommend not using source files as headers. Conventionally source files are conventionally compiled but if one of those included files is compiled and linked with the "Node.cpp" file, then your program is ill-formed due to One Definition Rule violations.
If I try to make foward decleration, I will got undefined refernces of class members.
A forward declaration should never cause "undefined references of class members". You may have misunderstood something.
What is the right approach for solving this kind of problem in c++.
Simply, you need to sort the definitions in an order where depended definitions are before those that depend on them. If and only if such sorting doesn't exist, then there is a cycle in the dependency graph, and problem cannot be fixed by re-ordering the definitions. In such case the design would have to be changed.
The class Node is parent class for both Directory and File. I need to include Node into both files to inherit from, but inside Node class implementation I need to access the File and Directory
This sounds like bad design. It may be possible to implement, but that doesn't mean that the design is necessarily good.
That said, the following order works: