Выбрать главу

      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 true if 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 stringstream for 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 cpp file; and INCLUDE, the line in the cpp file 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 cppcheck will 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 <cstddef>

#include <cstdlib>

#include <cstdio>

#include <fstream>

#include <iostream>

#include <sstream>

#include <string>

#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]));