I'm currently creating a FAT filesystem in C++. I have three classes:
Sdisk (which formats the disk via string to file output).
Filesys (which writes the FAT and the root).
Shell (offers ability to perform various file system operations.)
During each class coding I wrote simple main initializations of Sdisk and Filesys constructors. Now that I'm writing the Shell when I test Shell's default constructor I'm having issues with memory. Specifically: EXC_BAD_ACCESS(code=1 address=0xfffffffffffffff....).
I decided to use my debugger to find out what went wrong and determined that the values I was passing in the Shell constructor to the Sdisk default constructor wasn't correct. I started to dig and found out that calling another classes constructor inside of the current classes constructor was called: "Delegating" or "Constructor Chaining". I had no idea. I found out that you have to "use it from the initialization list, not from the constructor body".
I've created an extra constructor with no parameters for Shell. What I need to do is call Sdisk from inside of the Shell constructor with Sdisk's default three parameters. But when I try that I continue to get the memory errors. I've even attempted to give the default constructor of Shell no parameters but as long as I call it in main it causes the error.
Any help regarding this matter is extremely appreciated! Thank you!
Here is my reference for Delegating Constructors
class Shell: public Filesys
{
public:
Shell(string filename, int blocksize, int numberofblocks):Filesys(disk) {Filesys(filename,blocksize,numberofblocks);}; // creates the file system.
int dir(); // call ls which lists all files
int add(string file); // add a new file using input from the keyboard.
int del(string file); // deletes the file
int type(string file); //lists the contents of file
int copy(string file1, string file2);//copies file1 to file2
friend class Sdisk;
friend class Filesys;
};
class Sdisk
{
public :
Sdisk() { }
Sdisk(string diskname); // Default Constructor
Sdisk(string diskname, int numberofblocks, int blocksize);
int getblock(int blocknumber, string& buffer);
int putblock(int blocknumber, string buffer);
int getblocksize() {return blocksize; } // Returns the blocksize.
int getnumberofblocks() { return numberofblocks; } // Returns the number of blocks.
string getfilename() { return diskname; } // Returns the disk name.
friend class Shell;
friend class Filesys;
private :
int numberofblocks; // number of blocks on disk
string diskname; // file name of pseudo-disk
string diskname1;
int blocksize; // block size in bytes/the number of blocks.
};
class Filesys
{
public:
Filesys(Sdisk&);
int fsclose();
int newfile(string file);
int rmfile(string file);
int getfirstblock(string file);
int addblock(string file, string block);
int delblock(string file, int blocknumber);
int readblock(string file, int blocknumber, string& buffer);
int writeblock(string file, int blocknumber, string buffer);
int nextblock(string file, int blocknumber);
bool checkblock(string file, int blocknumber);
vector<string> block(string buffer, int b);
Sdisk disk;
friend class Shell;
private :
int fssync(); //writes the Root and FAT to the disk.
string buffer;
int rootsize; // maximum number of entries in ROOT
int fatsize; // number of blocks occupied by FAT
vector<string> filename; // filenames in ROOT
vector<int> firstblock; // firstblocks in ROOT parallel
vector<int> fat; // FAT # of blocks
};
int main()
{
Shell("disk",256,128);
string s;
string command="go";
string op1,op2;
while (command != "quit")
{
command.clear();
op1.clear();
op2.clear();
cout << "$";
getline(cin,s);
unsigned long firstblank = s.find(' ');
if (firstblank < s.length()) s[firstblank]='#';
unsigned long secondblank = s.find(' ');
command=s.substr(0,firstblank);
if (firstblank < s.length())
op1=s.substr(firstblank+1,secondblank-firstblank-1);
if (secondblank < s.length())
op2=s.substr(secondblank+1);
if (command=="dir")
{
cout << "dir" << endl;
// use the ls function
}
if (command=="add")
{
// The variable op1 is the new file
cout << "add" << endl;
}
if (command=="del")
{
cout << "del" << endl;
// The variable op1 is the file
}
if (command=="type")
{
cout << "type" << endl;
// The variable op1 is the file
}
if (command=="copy")
{
cout << "copy" << endl;
// The variable op1 is the source file and the variable op2 is the destination file.
}
if (command=="exit")
{
cout << "Exiting now..." << endl;
return 0;
}
}
return 0;
}
Filesys::Filesys(Sdisk& sdisk):Sdisk(disk)
{
this-> disk = sdisk;
rootsize = disk.getblocksize()/12;
fatsize = (disk.getnumberofblocks()*5) / (disk.getblocksize())+1;
cout << "rootsize: " << rootsize << endl << "fatsize: " << fatsize << endl << "number of blocks: " << disk.getnumberofblocks() << endl << "getblocksize(): " << disk.getblocksize() << endl;
for(int i=0; i<rootsize; i++)
{
filename.push_back("XXXXXX");
firstblock.push_back(0);
}
int k= disk.getnumberofblocks();
fat.push_back(fatsize + 2);
for (int i = 0; i <= fatsize; i++)
{
fat.push_back(0);
}
for(int i = fatsize + 2; i < k; i++)
{
fat.push_back(i+1);
}
fat[fat.size()-1] = 0;
fssync();
}
Sdisk::Sdisk(string disk)
{
diskname = disk + ".dat";
diskname1 = disk + ".spc";
ifstream ifile(diskname1.c_str());
if(ifile.is_open())
{
ifile >> numberofblocks >> blocksize;
ifile.close();
}
else
{
cout << "Was unable to open the file" << endl;
}
}
// Sdisk default constructor
Sdisk::Sdisk(string disk, int numberofblocks, int blocksize)
{
this->diskname = disk + ".dat";
this->diskname1 = disk + ".spc";
this->numberofblocks = numberofblocks;
this->blocksize = blocksize;
fstream spcfile;
fstream datfile;
spcfile.open((this->diskname1).c_str(),ios::in | ios::out);
datfile.open((this->diskname).c_str(),ios::in | ios::out);
if (spcfile.good() && datfile.good())
{
cout << "The disk named: " << diskname.c_str() << " exists and is now ready to be written to." << endl;
}
else // .spc/.dat file creation.
{
cout << "The disk: " << diskname.c_str() << "could not be found. " << endl;
cout << "Both the SPC and DAT file were not found. Creating both now. Please wait...." << endl;
spcfile.open((this->diskname1).c_str(),ios::out);
datfile.open((this->diskname).c_str(),ios::out);
spcfile << numberofblocks << " " << blocksize;
cout << "The SPC file " << diskname.c_str() << " was created" << endl;
cout << "The DAT file " << diskname.c_str() << " was created" << endl;
for (int i=0; i<numberofblocks*blocksize; i++)
{
datfile.put('#'); // Fills the file with '#' character.
}
}
spcfile.close();
datfile.close();
return;
}
The problem is here:
Shell(string filename, int blocksize, int numberofblocks) : Filesys(disk)
{
Shell(filename,blocksize,numberofblocks);
};
The Shell constructor creates in the body a temporary shell object, which itself calls the Shell constructor again and so on. So you will end up with an infinite recursion and a full stack.
Other remarks:
In your shell constructor you also initalize the base class subobject with the mem-initializer Filesys(disk). I couldn't find a valid disk in your code snippet. But as you experience runtime problems and not compilation errors, I suppose it was simply lost in the copy&paste.
Are you sure that Shell should inherit from Filesys ? I.e. can you say that your Shell is a FAT filesystem ? And how would your class design evolve if later you'd decide to enrich the filesystems that your Shell suports with say NTFS or EXT3 filesystems ?
Finally, is the number of blocks and the blocksize not parameters of the filesystem rather than the shell you construct on its top ?
For these reasons I'd rather go for something like:
class Shell
{
string fname;
Filesys &fs;
public:
Shell(string filename, Filesys &filesystem)
: fname(filename), fs(disk)
{ ... }; // creates the file system.
...
};
In this case you'd create the Shell:
Sdisk mydisk("drive1", 32768, 4096); // disk data for the disk constructor
Filesys mysystem(mydisk); // fs parameters for the fs
Shell myshell("A:", mysystem); // higher level abstraction
I hope I got it right. You got quite many problems in your code. As I said in the comment this is very bad:
Shell(string filename, int blocksize, int numberofblocks):Filesys(disk) {
Shell(filename,blocksize,numberofblocks);
};
It is an obvious infinite recursion. You said you needed to call Sdisk from inside the Shell so I guess you meant:
Shell(string filename, int blocksize, int numberofblocks):Filesys(disk) {
Sdisk(filename,blocksize,numberofblocks);
};
However, it won't help you in any way. Such syntax will create a temporary Sdisk which will die immediately. You said you want to use default parameter but you don't provide any anywhere.
Generally your design is quite bad. The relations between classes are not clear and you ended up with strange solutions which do not make much sense. I.e. why do you want to call this constructor? What is the relation between Sdisk and Shell? Shell doesn't have any member variables so it cannot hold any state (I'm almost certain that it's not intended)... etc.
Related
Does anyone know how to fout an array like the below to a .dat file?
Let me know if this is not enough information.
My console output shows 4 rows of integers with 6 columns and I want to print that to my file. I can print everything else, but can not figure this one out..
for(i=0; i < 4; i++)
{
cout << " " << i+1;
P[i].TeamOutput();
}
void TeamOutput()
{
cout << teamwork << speed << power << defence << injury << endl;
}
You almost had it. You need a specific type of ostream to output. cout is an ostream, but a special one that outputs to the system's console. You need an ostream that outputs to a file. This kind of ostream is called an ofstream and is in the header file <fstream>. Here's how you can use it with an array.
#include <iostream>
#include <fstream>
using namespace std; // It's generally bad to do this
int main()
{
// Size of array
const int SIZE = 10;
// Make the array
int some_array[10];
// Fill the array with values
for (int i = 0; i < SIZE; i++)
{
some_array[i] = i + 1;
}
// THIS is where the magic happens; make the file stream to output to
ofstream file("file.dat"); // "file.dat" can be replaced by whatever filename
// Make sure the file opened okay. Otherwise, there's an error
if (file.is_open())
{ // The file opened just file, so do whatever you need to
// Save all the info from the array to "file.dat"
for (int i = 0; i < SIZE; i++)
{
file << some_array[i] << endl;
}
// Make sure to close the 'ofstream' when you're done
file.close();
}
else
{ // The file did NOT open okay; there's an error
cout << "Error opening file.dat!" << endl;
}
}
I have a pair of header files. Within IsingModel.h, publically I declare:
ofstream logfile1;
ofstream logfile2;
Then to open the relevant files (logfile1 and logfile 2 have different names) I use:
do {
name2.str(""); //reset name stringstream
n++; //increase n value
name2 << "output_" << gridSize << "_" << seed << "_" << n << "_eqmCalc.txt"; //stream created
} while (if_exist(name2.str())); //test if file already exists
logfile2.open(name2.str());
Which works in creating the file. Then, throughout the code I use the ofstreams to act on the files, for example:
logfile1 << counter << " " << calcM() << " " << calcE() << endl;
This is fine for actions that are independent for each file, however when I call the destructor I want to write the same standard information to each file. To that end, I am experimenting with iteratively writing to the files and it does not seem to work:
void IsingSystem::test() {
for (int i = 1; i = 2; i++) {
if (ofstream("logfile" + to_string(i)).is_open); {
ofstream("logfile" + to_string(i)) << "success" << endl;
}
}
}
This instead creates files called logfile1 and logfile2. As an alternative, I tried to create an array of ofstreams:
void createFileHandles() {
const int count = 2;
std::ofstream logfile[count];
}
But, I could not work out how to pass this between functions properly.
What is the proper way of handling ofstreams so that I can have multiple files open, writing different instructions to them simultaneously but also have some actions that happen to both?
You can have a vector of ofstream
vector<ofstream> ofstreams(2);
//fill vec
for (int i = 0; i < 2; i++)
{
if (ofstreams[i].is_open);
{
ofstreams[i]<< "success" << endl;
}
}
You can then pass ofstreams to functions.
I have such piece of code:
typedef struct reader
{
char name[50];
char card_num[50];
char title[100];
}reader_t;
int main()
{
vector<reader> vec;
ifstream input_file("D:\\lab.txt", ios::binary);
reader_t master[1];
input_file.read((char*)&master, sizeof(master));
for (size_t idx = 0; idx < 1; idx++)
{
reader temp;
strcpy(temp.name, master[idx].name);
strcpy(temp.card_num, master[idx].card_num);
strcpy(temp.title, master[idx].title);
vec.push_back(temp);
cout << "Name: " << master[idx].name << endl;
cout << "Card num: " << master[idx].card_num << endl;
cout << "Title: " << master[idx].title<<endl;
}
cout << vec.size();
getchar();
}
What is does: it reads structures from binary file into an array of structures,copies them into vector and displays structure.And yes, I do need to do like this - I need to store structures from file in vector and this is the only working way to do it I could find(if you can tell, how to read structures to vector directly from file - you are welcome).
So,everything works fine, but the problem is that I need to create a function which would be able to do the same, but with dynamic array.I wrote something like this:
void read_structs(int vec_size)
{
ifstream input_file("D:\\lab.txt", ios::binary);
//Here I commented 2 ways how I tried to create a dynamic array of structs
//reader* master = new reader[vec_size];
//reader* master = (reader*)malloc(sizeof(reader) * vec_size);
input_file.read((char*)&master, sizeof(master));
for (size_t idx = 0; idx < vec_size; idx++)
{
reader temp;
strcpy(temp.name, master[idx].name);
strcpy(temp.card_num, master[idx].card_num);
strcpy(temp.title, master[idx].title);
vec.push_back(temp);
cout << "Name: " << master[idx].name << endl;
cout << "Card num: " << master[idx].card_num << endl;
cout << "Title: " << master[idx].title<<endl;
}
}
And that worked fine too unless I tried to run it.VS wasn't higlighting error in my code, it just was throwing an exception right as the moment when the program tried to access master[0].name.
There is absolutely no point in the temp struct. See, the
vec.push_back(temp);
is already using copy constructor, so copy constructor must work and then the set of strcpy is not doing anything different from that, so just go with
vec.push_back(master[0]).
You can't read into vector directly. You do need to read into temporary. So that is correct. Except I suppose you want to read all entries from the file no matter how many of them there are, so you need to put the read itself also into the loop.
There is not much point in creating an array of one element.
reader_t master[1];
input_file.read((char*)master, sizeof(master));
// ^ you *don't* need & here, arrays degrade to pointers automatically
and
reader_t master;
input_file.read((char *)&master, sizeof(master));
// ^ but you do need & here.
are equivalent. I would go with the later.
So we are basically down to:
reader temp; // calling it temp; the master name makes no sense.
while (input_file.read((char*)&temp, sizeof(temp)))
// read returns input_file and input_file is false if last operation failed
{
vec.push_back(temp);
// verify the stored values by reading back vfrom vec.back().
cout << "Name: " << vec.back().name << endl;
cout << "Card num: " << vec.back().card_num << endl;
cout << "Title: " << vec.back().title<<endl;
}
In the second example, you didn't initialize master, so it obviously crashed.
There is a more C++ approach though. First, you define a read operator for the structure:
std::istream &operator>>(std::istream &in, reader &r) {
return in.read((char *)&r, sizeof(r));
}
and then you simply read the vector using the istream_iterator:
vec.assign(std::istream_iterator<reader>(input_file),
std::istream_iterator<reader>());
and the standard library will generate the above loop for you.
Let's say I have a program that does the follow:
for (i=1; i<10; i++)
{
computeB(i);
}
where the computeB just outputs a list of values
computeB(int i)
{
char[6] out_fname="output";
//lines that compute `var` using say, Monte Carlo
string fname = out_fname + (string)".values";
ofstream fout(fname.c_str());
PrintValue(fout,"Total Values", var);
}
From another file:
template <class T>
void PrintValue(ofstream & fout, string s, T v) {
fout << s;
for(int i=0; i<48-s.size(); i++) {
fout << '.';
}
fout << " " << v << endl;
}
Before implementing that loop, computeB just outputted one file of values. I now want it to create multiple values. So if it originally created a file called "output.values", how can I write a loop so that it creates "output1.values", "output2.values", ..., "output9.values"?
EDIT: I forgot to mention that the original code used the PrintValue function to output the values. I originally tried to save space and exclude this, but I just caused confusion
Disregarding all the syntax errors in your code ...
Use the input value i to compute the output file name.
Use the file name to construct an ofstream.
Use the ofstream to write var to.
Here's what the function will look like:
void combuteB(int i)
{
char filename[100];
sprintf(filename, "output%d.values", i);
ofstream fout(filename);
fout << "total values";
fout << " " << var << endl; // Not sure where you get
// var from. But then, your
// posted code is not
// exactly clean.
}
You can use std::to_string() to convert from an int to a string:
void computeB(int i)
{
if (std::ofstream fout("output" + std::to_string(i) + ".values"))
fout << "total values" << " " << var << '\n';
else
throw std::runtime_error("unable to create output file");
}
Okay, so I'm trying to read input from a binary file. I've changed this code a bit, but with this version, I'm getting an access violation error... So it's trying to access something that isn't there. Here's my source code for the problem area:
void HashFile::fileDump (ostream &log)
{
HashNode *temp = new HashNode;
fstream bin_file;
bin_file.open ("storage_file.bin", ios::in | ios::binary);
for(int i = 0; i < table_size; i++)
{
bin_file.seekg( i * sizeof(HashNode) );
bin_file.read( (char *)&temp, sizeof(HashNode) );
printDump(HashNode(temp->title, temp->artist, temp->type, temp->year,
temp->price), log, i);
}
bin_file.close();
}
void HashFile::printDump(HashNode A, ostream &log, int N)
{
log << "(" << N << ") " << A.title << ", " << A.artist
<< ", " << A.type << ", " << A.year << ", $"
<< setprecision(2) << A.price << endl;
}
I know that I should have some kind of error checking. Right now the error is occurring in the printDump function. Whenever I try to output to the log I get an access violation error. However, I change the log to cout and my code will run fine somewhat. It will read the binary file I've created correctly until it gets to the last element. For what I've been testing with, table_size should be equal to 5. So I get into the for loop and i is incremented until it reaches 5 and then it keeps going. table_size is being changed to some random value even though I haven't physically touched it. Am I somehow writing over table_size's address in memory?
Here is the definition of my Node:
class HashNode
{
public:
HashNode();
~HashNode();
HashNode(string title, string artist, string type, int year, float price);
friend class HashFile;
private:
char title [35];
char artist [25];
char type [12];
int year;
float price;
};
This
bin_file.read( (char *)&temp, sizeof(HashNode) );
should be this
bin_file.read( (char *)temp, sizeof(HashNode) );
You are getting confused over pointers.
Whether that code will actually work depends strongly on the definition of Node which you haven't shown.
Also the code leaks memory as temp is never deleted. It would be better not to allocate temp at all, like this
void HashFile::fileDump (ostream &log)
{
HashNode temp;
fstream bin_file("storage_file.bin", ios::in | ios::binary);
for(int i = 0; i < table_size; i++)
{
bin_file.seekg( i * sizeof(HashNode) );
bin_file.read( (char *)&temp, sizeof(HashNode) );
printDump(HashNode(temp.title, temp.artist, temp.type, temp.year, temp.price), log, i);
}
}
Not clear why you feel the need to create a new node from temp, why not just pass temp to printDump? Like this
printDump(temp, log, i);
But without seeing the definition of Node I can't say for sure.
Also no need to close the file, that happens automatically, also opening the file in the constructor is a little cleaner IMHO.
EDIT
OK having seen the definition of Node this would be my recommendation
void HashFile::fileDump(ostream &log)
{
fstream bin_file("storage_file.bin", ios::in | ios::binary);
for(int i = 0; i < table_size; i++)
{
bin_file.seekg(i * sizeof(HashNode));
HashNode temp;
bin_file.read((char *)&temp, sizeof(HashNode));
printDump(temp, log, i);
}
}
Also I would change printDump to use a const reference, this avoids copying a Node object (it is quite big).
void HashFile::printDump(const HashNode& A, ostream &log, int N)
{
log << "(" << N << ") " << A.title << ", " << A.artist
<< ", " << A.type << ", " << A.year << ", $"
<< setprecision(2) << A.price << endl;
}