newH << "//@//\n" // Change marker
<< newheader.str();
}
}
if(!existcpp) { // Create cpp file
ofstream newcpp(part[IMPLEMENT].c_str());
assure(newcpp, part[IMPLEMENT].c_str());
newcpp << part[CPPLINE1] << endl
<< part[INCLUDE] << endl;
} else { // Already exists; verify it
stringstream cppfile;
ostringstream newcpp;
cppfile << existcpp.rdbuf();
// Check that first two lines conform:
bool changed = false;
string s;
cppfile.seekg(0);
getline(cppfile, s);
bool lineUsed = false;
for (int line = CPPLINE1;
cppfile.good() && line <= INCLUDE;
++line) {
if(startsWith(s, part[line])) {
newcpp << s << endl;
lineUsed = true;
if (getline(cppfile, s))
lineUsed = false;
} else {
newcpp << part[line] << endl;
changed = true;
lineUsed = false;
}
}
// Copy rest of file
if (!lineUsed)
newcpp << s << endl;
newcpp << cppfile.rdbuf();
// If there were changes, overwrite file:
if(changed){
existcpp.close();
ofstream newCPP(part[IMPLEMENT].c_str());
assure(newCPP, part[IMPLEMENT].c_str());
newCPP << "//@//\n" // Change marker
<< newcpp.str();
}
}
}
int main(int argc, char* argv[]) {
if(argc > 1)
cppCheck(argv[1]);
else
cppCheck("cppCheckTest.h");
} ///:~
First notice the useful function startsWith( ), which does just what its name says—it returns trueif the first string argument starts with the second argument. This is used when looking for the expected comments and include-related statements. Having the array of strings, part, allows for easy looping through the series of expected statements in source code. If the source file doesn’t exist, we merely write the statements to a new file of the given name. If the file does exist, we search a line at a time, verifying that the expected lines occur. If they are not present, they are inserted. Special care has to be taken to make sure we don’t drop existing lines (see where we use the Boolean variable lineUsed). Notice that we use a stringstreamfor an existing file, so we can first write the contents of the file to it and then read from it and search it .
The names in the enumeration are BASE, the capitalized base file name without extension; HEADER, the header file name; IMPLEMENT, the implementation file ( cpp) name; HLINE1, the skeleton first line of the header file; GUARD1, GUARD2, and GUARD3, the "guard" lines in the header file (to prevent multiple inclusion); CPPLINE1, the skeleton first line of the cppfile; and INCLUDE, the line in the cppfile that includes the header file .
If you run this program without any arguments, the following two files are created:
// CPPCHECKTEST.h
#ifndef CPPCHECKTEST_H
#define CPPCHECKTEST_H
#endif // CPPCHECKTEST_H
// CPPCHECKTEST.cpp
#include "CPPCHECKTEST.h"
(We removed the colon after the double-slash in the first comment lines so as not to confuse the book’s code extractor. It will appear in the actual output produced by cppCheck.)
You can experiment by removing selected lines from these files and re-running the program. Each time you will see that the correct lines are added back in. When a file is modified, the string "//@//" is placed as the first line of the file to bring the change to your attention. You will need to remove this line before you process the file again (otherwise cppcheckwill assume the initial comment line is missing) .
Detecting compiler errors
All the code in this book is designed to compile as shown without errors. Any line of code that should generate a compile-time error is commented out with the special comment sequence "//!". The following program will remove these special comments and append a numbered comment to the line. When you run your compiler, it should generate error messages, and you will see all the numbers appear when you compile all the files. This program also appends the modified line to a special file so that you can easily locate any lines that don’t generate errors .
//: C04:Showerr.cpp
// Un-comment error generators
#include
#include
#include
#include
#include
#include
#include
#include "../require.h"
using namespace std;
const string usage =
"usage: showerr filename chapnum\n"
"where filename is a C++ source file\n"
"and chapnum is the chapter name it's in.\n"
"Finds lines commented with //! and removes\n"
"comment, appending //(#) where # is unique\n"
"across all files, so you can determine\n"
"if your compiler finds the error.\n"
"showerr /r\n"
"resets the unique counter.";
class Showerr {
const int CHAP;
const string MARKER, FNAME;
// File containing error number counter:
const string ERRNUM;
// File containing error lines:
const string ERRFILE;
stringstream edited; // Edited file
int counter;
public:
Showerr(const string& f, const string& en,
const string& ef, int c) : FNAME(f), MARKER("//!"),
ERRNUM(en), ERRFILE(ef), CHAP(c) { counter = 0; }
void replaceErrors() {
ifstream infile(FNAME.c_str());
assure(infile, FNAME.c_str());
ifstream count(ERRNUM.c_str());
if(count) count >> counter;
int linecount = 1;
string buf;
ofstream errlines(ERRFILE.c_str(), ios::app);
assure(errlines, ERRFILE.c_str());
while(getline(infile, buf)) {
// Find marker at start of line:
size_t pos = buf.find(MARKER);
if(pos != string::npos) {
// Erase marker:
buf.erase(pos, MARKER.size() + 1);
// Append counter & error info:
ostringstream out;
out << buf << " // (" << ++counter << ") "
<< "Chapter " << CHAP
<< " File: " << FNAME
<< " Line " << linecount << endl;
edited << out.str();
errlines << out.str(); // Append error file
}
else
edited << buf << "\n"; // Just copy
linecount++;
}
}
void saveFiles() {
ofstream outfile(FNAME.c_str()); // Overwrites
assure(outfile, FNAME.c_str());
outfile << edited.rdbuf();
ofstream count(ERRNUM.c_str()); // Overwrites
assure(count, ERRNUM.c_str());
count << counter; // Save new counter
}
};
int main(int argc, char* argv[]) {
const string ERRCOUNT("../errnum.txt"),
ERRFILE("../errlines.txt");
requireMinArgs(argc, 1, usage.c_str());
if(argv[1][0] == '/' || argv[1][0] == '-') {
// Allow for other switches:
switch(argv[1][1]) {
case 'r': case 'R':
cout << "reset counter" << endl;
remove(ERRCOUNT.c_str()); // Delete files
remove(ERRFILE.c_str());
return 0;
default:
cerr << usage << endl;
return 1;
}
}
if (argc == 3) {
Showerr s(argv[1], ERRCOUNT, ERRFILE, atoi(argv[2]));
s.replaceErrors();
s.saveFiles();
}
} ///:~
You can replace the marker with one of your choice .
Each file is read a line at a time, and each line is searched for the marker appearing at the head of the line; the line is modified and put into the error line list and into the string stream, edited. When the whole file is processed, it is closed (by reaching the end of a scope), it is reopened as an output file, and editedis poured into the file. Also notice the counter is saved in an external file. The next time this program is invoked, it continues to sequence the counter .
Читать дальше