Reputation: 49
I'm new to C++ and so far I've had all code in one and the same file. Now as I'm progressing, I need to separate my code in to source and header files which I'm not very familiar with.
I can make it work with simple tasks, but in this program that I'm now trying to divide in to separate files gives me an error, while when I have it all in one file I can compile it.
I get stuck on the error message
main.cpp:10:1: error: unknown type name 'textEditor'
textEditor siEditor;
If someone could explain why I'm running in to this error and how to prevent it would be appreciated. I read that it could be related to duplicated declarations, but I don't understand from where.
This is how my main.cpp looks:
#include <iostream>
#include <fstream>
using namespace std;
#include "textData.h"
#include "textEditor.h"
textData siData;
textEditor siEditor;
int main()
{
cout << "\nWelcome to siEdit!" << endl;
while (true)
{
cout << "\nWhat would you like to do? \nNew file = n, Append = a, View = v, Quit = q: ";
string toDo;
cin >> toDo;
if (toDo == "n")
{
siEditor.openText();
cout << "Now editing the file: " << siData.fileName.c_str() << endl;
cout << "Type '=!' to stop editing and save. \n " << endl;
siEditor.writeText();
}
else if (toDo == "a")
{
siEditor.appendTextOpen();
cout << "Now appending text: " << siData.appendTextfileName.c_str() << endl;
cout << "Type '=!' to stop editing and save changes. \n " << endl;
siEditor.appendText();
}
else if (toDo == "v")
{
siEditor.readText();
cout << "\n";
}
else if (toDo == "q")
{
return 0;
}
else
{
cout << "Invalid input." << endl;
}
}
}
siEdit.cpp:
#include <iostream>
#include <fstream>
using namespace std;
#include "textData.h"
#include "textEditor.h"
textData siData;
class textEditor
{
public:
void openText()
{
//when associated file is open.
while (siData.siFile.is_open())
{
siData.siFile.close();
}
cout << "\nWhat do you want to call your file? ";
cin >> siData.fileName;
//Creates / Opens fileEditor
const char* path = siData.fileName.c_str();
siData.siFile.open(path);
}
void writeText()
{
bool editing = true;
bool hasEditing = false;
while (editing == true)
{
//Get user input
string input = " ";
getline(cin, input);
string yesNo;
if (input == "=!")
{
cout << "Would you like to save the file? Y/N" << endl;
cin >> yesNo;
if (yesNo == "Y")
{
cout << "Filed saved: " << siData.fileName.c_str();
editing = false;
}
else if (yesNo == "N")
{
cout << "No changes have been saved. Exiting." << endl;
hasEditing = false;
editing = false;
siData.siFile.clear();
}
else
{
cout << "Invalid input. Type '=! to exit." << endl;
}
}
else
{
siData.siFile << input;
siData.siFile << endl;
hasEditing = true;
}
}
}
void readText()
{
string line;
cout << "\nEnter the name of your file: ";
cin >> siData.fileName;
cout << "\n";
const char* path = siData.fileName.c_str();
// input file stream
//Internal stream buffer which performes I/O on file.
ifstream siFileRead(path);
if(siFileRead.is_open())
{
while(getline(siFileRead,line))
{
cout << line << endl;
siData.siFile << line;
}
}
else
{
cout << "Unable to open file. Confirm name and file location.";
}
}
// open the existing text file
void appendTextOpen()
{
while (siData.siFileAppend.is_open())
{
// erase previous text
siData.siFileAppend.clear();
// close the input text file
siData.siFileAppend.close();
}
cout << "\nEnter the name of the file: ";
//find file name.
cin >> siData.appendTextfileName;
//Makes / Opens file
const char* path = siData.appendTextfileName.c_str();
siData.siFileAppend.open(path, fstream::app);
}
//add text together with previous input.
void appendText()
{
bool editing = true;
bool hasEditing = false;
while (editing == true)
{
//Gets user input
string input = " ";
getline(cin, input);
if (input == "=!")
{
if (hasEditing == true)
{
cout << "File saved: " << siData.appendTextfileName.c_str() << endl;
editing = false;
}
}
else
{
siData.siFileAppend << input;
siData.siFileAppend << endl;
hasEditing = true;
}
}
}
};
textData.h:
#ifndef SIEDITOR_H
#define SIEDITOR_H
class textData
{
public:
string fileName;
string appendTextfileName;
ofstream siFile;
ofstream siFileAppend;
};
#endif
textEditor.h:
#ifndef SIEDITOR_H
#define SIEDITOR_H
class textEditor
{
public:
void openText()
void writeText()
void readText()
void appendTextOpen()
void appendText()
};
#endif
Upvotes: 1
Views: 3879
Reputation: 8240
Class must be defined only once.
Move class definition to separated header file (join togather both content of your same name class: fields and methods):
// textEditor.h
#pragma once
class textEditor {
void appendText();
private:
string fileName;
}
Move class methods to separate source file:
// textEditor.cpp
#include "textEditor.h"
void textEditor::appendText() {
// ... impl
}
And in the main.cpp:
// main.cpp
#include "textEditor.h"
textEditor siEditor;
int main()
{
siEditor.appendText();
}
Upvotes: 1
Reputation: 27538
Consider what the preprocessor does. It runs separately for every *.cpp
file and processes all your #include
, #ifndef
, #define
and #endif
statements.
This is the start of the your main.cpp
:
#include <iostream> #include <fstream> using namespace std; #include "textData.h" #include "textEditor.h" textData siData; textEditor siEditor;
How would you preprocess this if you were a preprocessor?[*] You would probably start with the #include
statements. The intermediate result would be:
// contents of <iostream>...
// contents of <fstream>...
using namespace std;
#ifndef SIEDITOR_H
#define SIEDITOR_H
class textData
{
public:
string fileName;
string appendTextfileName;
ofstream siFile;
ofstream siFileAppend;
};
#endif
#ifndef SIEDITOR_H
#define SIEDITOR_H
class textEditor
{
public:
void openText()
void writeText()
void readText()
void appendTextOpen()
void appendText()
};
#endif
textData siData;
textEditor siEditor;
Now let's inspect this intermediate result:
// contents of <iostream>...
// contents of <fstream>...
using namespace std;
#ifndef SIEDITOR_H // <--- true, SIEDITOR_H is not defined, don't skip until #endif
#define SIEDITOR_H // <--- OK, SIEDITOR_H is now defined
class textData
{
public:
string fileName;
string appendTextfileName;
ofstream siFile;
ofstream siFileAppend;
};
#endif // <--- end of block started by #ifndef SIEDITOR_H
#ifndef SIEDITOR_H // <--- false, SIEDITOR_H is defined, skip until #endif
#define SIEDITOR_H
class textEditor
{
public:
void openText()
void writeText()
void readText()
void appendTextOpen()
void appendText()
};
#endif // <--- end of block started by #ifndef SIEDITOR_H
textData siData;
The result of the preprocessing turns out to be:
// contents of <iostream>...
// contents of <fstream>...
using namespace std;
class textData
{
public:
string fileName;
string appendTextfileName;
ofstream siFile;
ofstream siFileAppend;
};
textData siData;
textEditor siEditor; // error, unknown type textEditor
This explains the specific error message you have been asking about. The solution is to use a different include guard in every header file. You must choose completely unique include guard names. In big projects, this can become difficult. Here's some reading material:
Nevertheless, there are more errors in your code:
First of all, your header files assume a lot. They assume that someone else already included the necessary standard headers to get std::string
and std::ofstream
. They also assume that someone else has already used using namespace std;
or using std::string; using std::ofstream;
.
That's very bad practice. Your header file should include the standard headers it needs, and just spell out the complete names (no using namespace std;
).
In addition to that, it should use standard headers which are guaranteed to contain what you need. If you need std::string
, then include <string>
.
Standard headers may include other standard headers, but there are only very few guaranteed indirect inclusions (I am too lazy to look up in the standard if <iostream>
implies <string>
, but I guess it does not.)
Here is an example:
textEditor.h:
#ifndef SI_TEXT_DATA_H
#define SI_TEXT_DATA_H
#include <string>
#include <fstream>
class textData
{
public:
std::string fileName;
std::string appendTextfileName;
std::ofstream siFile;
std::ofstream siFileAppend;
};
#endif
Finally, you redefine your textEditor
class in siEdit.cpp
. That's not allowed. You should instead declare the member functions in the *.h
file inside of the class definition, and define the member functions in the *.cpp
file.
textEditor.h
should look like this:
#ifndef SI_TEXT_EDITOR_H
#define SI_TEXT_EDITOR_H
class textEditor // class definition begins
{
public:
void openText(); // declaration of member function
void writeText(); // declaration of member function
void readText(); // declaration of member function
void appendTextOpen(); // declaration of member function
void appendText(); // declaration of member function
}; // class definition ends
#endif
And siEdit.cpp
should look like this:
#include "textData.h"
#include "textEditor.h"
textData siData;
void textEditor::openText() // definition of a member function begins
{
// ...
} // definition of a member function ends
// other definitions
Your global variables (like textData siData;
) are not a very good idea, either, especially seeing as how they are not wrapped in anonymous namespaces.
[*] An actual preprocessor may not technically work like this, but you can picture it that way.
Upvotes: 0
Reputation: 145389
You're using the same include guard in both header files, namely SIEDITOR_H
. That prevents the second header's contents from being included. Use #pragma once
instead of include guard symbols.
#pragma once
is a de facto standard that's supported by all compilers of practical interest.
In your implementation file don't repeat the class definition. Just define the declared member functions. And static
data members, if any.
Upvotes: 1