Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

C++ For Mathematicians (2006) [eng]

.pdf
Скачиваний:
194
Добавлен:
16.08.2013
Размер:
31.64 Mб
Скачать

376

C++ for Mathematicians

With the final version built, you do not have to use Visual Studio to run your program. Using the Windows, navigate to the directory that houses your project (e.g., C:\research\math-programs\twin-primes). Within this folder, open a folder named Release where you should find an executable file named (based on our illustration) twin-primes.exe. This file can be copied to any convenient location on your hard drive. You can run this program either by double-clicking it, or by opening a DOS command window, going to the appropriate director, and typing its name.

A.2.2 Xcode for Macintosh OS X

Starting a new project

Launch Xcode8 and select New Project from the File menu. Scroll until you reach the heading Command Line Utility under which you select C++ Tool; click Next. Type in a name for your project and select a directory (folder) in which this project is to reside. Click Finish.

A project window opens. Notice that a main.cpp file is already present in the project. You may either edit this file (double-click its name to get started) or you can delete it: select the file (single-click on its name) and press the Delete key on your keyboard. Choose Delete References & Files and main.cpp is deleted from the project and from your hard drive.

Adding files to the project

You are now ready to create your program files. From the File menu select New Empty File. This opens a new window in which you can type your program (either a header .h file or a code .cc file).

Start typing and be sure to save your work. The first time you save you are prompted for a name and location for your file. Save your work inside the project folder. Next, the computer asks if you wish to add this file to your project; click Add to Project and then Add.

If there is a file already on your computer that you want to add to this project (perhaps a file you created in a different project), select9 Add to Project from the Project menu. Navigate until you find the file you want and then click Add. A second panel opens at the top of which is an option to Copy items into destination group’s folder (if needed). Click the check box next to this to indicate that you want a copy of the file made inside your project; then click Add.

To edit a file that is already in your project, simply double-click on its name in the project window.

8Launch Xcode by double-clicking on its icon. The Xcode application is not in the main Applications folder. Rather, use the Finder to navigate to the top level of your hard disk, open the Developer folder and then the Applications folder inside there.

9If the Add to Project option is dimmed, click once on the name of your project just below the heading “Groups & Files” in the left portion of your main project window.

Your C++ Computing Environment

377

Compiling

Once your source files are collected in your project, it’s time to compile. Click on the Build icon in the project window (or select Build from the Build menu) and the compiler gets to work.

When the compiler finishes you see, with luck, the message “Build succeeded” at the bottom left of the project window. More likely, however, is that there are errors to fix. The number of errors found in each file is reported in the main project window. You should also see a small red stop sign (octagon) with a white X at the lower right corner of the project window; click on this to bring up a list of errors. As you click on each error, the lower portion of the window jumps to the trouble spot in the relevant file.

After you repair the first error, you have two choices. You may proceed to fixing the second error or you can try to compile again. Sometimes fixing one mistake resolves subsequent errors; in this case, rebuilding removes several items at once and allows you to move efficiently to the next trouble spot.

Iterate this process of building and fixing errors until no errors or warnings remain.

Running and debugging your program

Once the program compiles without errors, it’s time to run it. Simply click on the Build and Go button on the project window (or select Build and Run from the Build menu) and the program starts executing. A window opens to show you the output of the program and provide a place for you to type any input the program requires.

If your program is not behaving as you expected, then you need to find the error(s) in your program. This is more difficult than fixing problems that arose during the compilation process. Fortunately, the debugger in Xcode is extremely helpful (also see the advice in Section A.3 later in this appendix). Open any of your C++ code files (by double-clicking its name in the project window). If you click in the left margin next to any of the statements in the code a small black arrow appears. This indicates that you have set a break point into the program. Place these break points wherever you want the program to pause. To remove a break point, simply click on it.

Once the break points are set, choose Build and Debug from the Build menu. The program starts running and then stops at the first break point it encounters. A list of variables with their current values is displayed. If some of the variables are classes, you can inspect the data members (including private members) for those objects.

Continue executing the program by pressing the Continue button on the debugger window. The program now resumes execution until it reaches another break point or the end of main().

Final version

Once your program is functioning properly it is time to perform one final build. In the Project menu, find the Set Active Build Configuration submenu, and select

378

C++ for Mathematicians

Release.

Next select Edit Project Settings from the Project menu. A window appears with four panel selectors at the top; choose Build from these. Reading from the top of the page, there is a drop-down menu for “Configuration”—choose “Release.” Next there is a drop-down menu labeled “Collection”—choose “Code Generation.”

You should see a list of setting/value pairs. Find the “Optimization Level” setting and, using the up/down arrows to the right, set the optimization level to “Fastest [–O3].”

Now close the Info window and click the Build button on the main project window. This creates a “release” version of your program.

Once this final version is built, you do not have to run it in Xcode. Instead, you can find the executable program inside the project directory. (Open the project directory in the Finder, double-click the folder named “Build” and then the folder named “Release.”) You may now either double-click the executable file you find there, or copy it to another location on your hard drive. Alternatively, you can open a Terminal window and change to the directory containing the executable program. For example, if the project (and the program) is named say, poetry, you launch the program by typing ./poetry once you are inside the appropriate directory.

A.3 General advice on debugging

Finding mistakes in computer programs can be a frustrating painful experience. Here we present some advice for how to find errors in programs and some common pitfalls.

Keep procedures short. It can be difficult to understand what’s happening in a long procedure. Break long procedures into a few shorter ones. Then you can test each individual piece to make sure it behaves as expected.

Name and document your procedures with care. Suppose you have a procedure declared like this:

double my_proc(double x, double y, long j);

This may make perfect sense to you today, but when you return to your work after the weekend, you may be confused by what my_proc does and what its arguments represent.

Liberally comment your code and use names that reflect the meaning of the symbols.

Print, print, print! Sprinkle your code with output statements to show the contents of variables at each point of the program. Here’s a handy way to do this. In each procedure, define a bool variable named DEBUG and wrap your debugging code in an if statement, like this:

Your C++ Computing Environment

379

int count_instances(const Group& G) { const bool DEBUG = true;

...

if (DEBUG) {

cerr << "i = " << i << " and j = " << j << endl;

}

...

}

Once this procedure is working, you can edit the first line of the procedure to read

const DEBUG = false;

thereby switching off all the debugging code you entered.

Alternatively, if you are working with an IDE, you can set break points and inspect variables as the program runs.

Be meticulous in your use of const. Suppose we have a procedure declared like this:

int count_generators(const Group& G);

If this procedure invokes a Group method on G that has not been flagged const, then—even if that method does not modify G—the compiler reports an error.

Make sure all variables are declared. It is easy to forget to declare a variable or to misspell its name.

Watch for memory leaks. If your class uses a destructor to free memory, you can insert debugging code into the class’s destructor so you can observe when (if) the destructor is doing its job.

Turn on all warnings. There are legal C++ statements that, almost surely, are not what you intend. The C++ compiler may help you find such errors. Here is a classic example.

#include <iostream> using namespace std;

int main() { long a = 10; long b = 23; if (a=b) {

cout << "They are equal" << endl;

}

return 0;

}

 

 

This program compiles without error and the output is the mystifying

 

 

 

 

 

 

 

 

 

 

 

 

They are equal

 

 

 

 

 

 

 

380

C++ for Mathematicians

where clearly a and b do not hold equal values.

However, by turning on warnings (e.g., with -Wall in g++) we get the following warning message from the compiler concerning line 7 of the program:

suggest parentheses around assignment used as truth value

This is not an especially helpful message, but at least it does point us to line 7 where, in fact, the problem lies. The a=b ought to be a==b.

Remember the using namespace std; statement. If the compiler complains that it doesn’t know what cout is, then you probably forgot the using statement.

Do not name your program test. This is a subtle problem that can be truly maddening. Suppose you name the following program test.cc and compile it as an executable named test:

#include <iostream> using namespace std; int main() {

cout << "Does it work?" << endl; return 0;

}

The program compiles witout error and now you attempt to run it by typing test at the command prompt. What happens? Apparently nothing. The message Does it work? never appears on your screen.

There is nothing wrong with the program. The problem is that UNIX computers already contain a standard program named test. When you type test at the command prompt, that version of test is run (not yours). To make sure the test in the current directory is the one that is executed, type ./test at the command prompt.

A way to avoid this problem is to name your program try or attempt.

Appendix B

Documentation with Doxygen

Document your code

Write a description for every class, method, and procedure you create. Explain what the input parameters represent (and what the valid ranges for these variables are) and what the output means. Include descriptions of what the variables inside the procedure mean.

This vital step in programming takes a bit of effort, but ultimately saves you hours. Over time, you will develop a personal library of classes and procedures. At some point you will want to reuse a class or procedure you have already created. If you don’t document, you will spend nearly as much time trying to figure out what the procedure is for as it would take to rewrite it!

The most likely reader of your comments is you. However, a colleague might learn that you have created a C++ class that he or she would like to use in another project. Your comments will make your colleague’s work much simpler (and your colleague won’t be repeatedly visiting your office asking you what the procedures do and what the parameters represent).

The simplest way to document your code is to include C++ comments in the .h and .cc files. However, there is a variety of tools that—with scant extra effort— make your documentation much easier to use (and beautiful to behold as well). These tools scan your C++ files for specially structured comments that are used to create Web pages (or LATEX documents) that are easy to use.

In this appendix we describe one such tool: Doxygen (available for free download from www.doxygen.org). There you can find the source code and, more conveniently, already compiled, ready-to-use versions for Windows, Mac OS X, and Linux.

Doxygen is rich in features. The description we present here is just a primer, but is sufficient to get you started.

B.1 Doxygen comments

Doxygen reads your C++ source files (.cc and .h) and looks for specially structured comments. A C++ single-line comment begins with a double slash // and a

381

382

C++ for Mathematicians

multiline C++ comment is enclosed between /* and */. Doxygen reads what you write in those comments. To tell Doxygen to read a particular comment begin either with a triple slash /// (for single-line comment) or with the sequence /** (for a multiple-line comment).

B.1.1 Documenting files

Each file (either .cc or .h) should begin with a brief comment explaining the file’s general purpose. Structure the comment like this:

/**

*@file Polygon.h

*@brief Declaration of the Polygon class

*/

The tag @file gives the name of the file and the @brief tag gives a short description of what the file is for. Notice the double asterisk on the first line; Doxygen looks for this to process this comment.

Note: The initial asterisks on lines 2 and 3 are optional and do not affect Doxygen’s output. These stars are useful to the human reader making this portion of the file stand out clearly as a comment.

B.1.2 Documenting procedures

Procedures are declared in a .h file and the full code is given in a .cc file. To document a procedure for processing by Doxygen, we place special comments before the procedure declaration in the .h file.

We generally write two comments. The first is a short, single-line overview of what the procedure does. The second is a long, multiple-line description of the procedure, its input arguments, and its return value.

The brief comment begins with a triple slash ///. We follow this with a few words that describe the procedure.

The long comment begins with /** giving a more elaborate description of the procedure. This includes special tags to document the input parameters and the return value. Here is an example.

Program B.1: Documenting a procedure for Doxygen.

1/// Number of real roots of a quadratic.

2/**

3* This procedure determines the number of real roots of a quadratic

4* polynomial by examining its discriminant.

5*

6* @param a coefficient of x-squared

7* @param b coefficient of x

8* @param c constant term

9* @return the number of real roots of axˆ2 + bx + c

10*/

11int nroots(double a, double b, double c);

Documentation with Doxygen

383

Program B.1 is an excerpt from a header file (say, nroots.h). The brief description (line 1) tells us the procedure’s purpose.

The expanded comment (starting at line 2) provides more detail including a description of the technique used. Following the description are tags that describe the input parameters (lines 6–8) and the return value (line 9). The input parameters are documented like this:

@param param_name role of this input parameter

The return value is documented with an @return tag that describes the value returned by this procedure.

Note that the initial asterisks on lines 3–9 are optional and are ignored by Doxygen. They serve the human reader by showing that this stretch of the file is a comment.

Finally, the declaration of the nroots procedure is given on line 11.

The code for this procedure is in a separate .cc file. For the sake of completeness, we give it here.

#include "nroots.h"

int nroots(double a, double b, double c) { double d; // The discriminant of the quadratic

d = b*b - 4.*a*c;

// Check sign of the discriminant and return number of roots

if (d > 0.) { return 2;

}

if (d < 0.) { return 0;

}

return 1;

}

Note that there are no Doxygen comments here, however, there are ordinary comments to help the human reader understand the code.

B.1.3 Documenting classes, data, and methods

Classes and their members are documented for Doxygen in a manner similar to ordinary procedures. The class and its members receive a brief description and then a detailed description. Methods for the class have their parameters and return values (if any) documented as well. In addition, the data members should be documented.

To illustrate these, we present a class named Polygon. The full .h file is presented in Program B.2. Notice that the file, the class, the class’s constructors and methods, and the class’s data elements all receive comments to be processed by Doxygen. For the data members, a brief description is sufficient, so we omit the detailed elaboration.

384

C++ for Mathematicians

Notice that constructors and void methods (such as move_vertex) do not have an @return tag because these do not return a value. The method perimeter takes no arguments, so no @param tag is given.

Program B.2: Documenting a class and its members for Doxygen.

1/**

2* @file Polygon.h

3* @brief Declaration of the Polygon class

4 */

5

6 #ifndef _POLYGON_

7#define _POLYGON_

8

9 #include <vector>

10 #include "Point.h"

11

12/// Planar polygons

13/**

14* The Polygon class represents a closed n-gon in the plane. The

15* vertices of the Polygon are held as a vector of Point objects.

16*/

17class Polygon {

18private:

19/// Hold the vertices of the Polygon

20vector<Point> vertices;

21

22/// The number of points in the Polygon

23int np;

24

25public:

26/// Default constructor

27/**

28* This default constructor creates an empty Polygon, i.e., a

29* Polygon without any points.

30*/

31Polygon();

32

33/// Create a basic n-gon

34/**

35* This creates a polygon with a specified number of points. The

36* points are placed evenly around the unit circle.

37* @param n the number of points.

38*/

39Polygon(int n);

40

41/// Copy constructor

42/**

43* Creates an exact copy of another Polygon.

44* @param P the Polygon we’re copying

45*/

46Polygon(const Polygon& P) {

47np = P.np;

48vertices = P.vertices;

49}

50

Documentation with Doxygen

385

51/// Move a vertex to a new location

52/**

53* Note: If an invalid vertex index is given, no change is made to

54* this Polygon.

55*

56* @param j index of the vertex we want to relocate

57* @param P new location for vertex j

58*/

59void move_vertex(int j, Point P);

60

61/// Perimeter of this Polygon

62/**

63* If the Polygon has fewer than two vertices, its perimeter is 0.

64* If it consists of exactly two vertices, the perimeter is twice

65* the distance between those points.

66*

67* @return the perimeter of this polygon

68*/

69double perimeter() const;

70};

71

72 #endif

For the sake of completeness, here is the .cc file that accompanies Polygon.h.

/**

*@file Polygon.cc

*@brief Code for the Polygon class methods

*/

#include "Polygon.h"

Polygon::Polygon() { np = 0; vertices.clear();

}

Polygon::Polygon(int n) {

//If n is negative, set n to be zero. if (n < 0) n = 0;

//If is zero, just clear the vertices data structure if (n==0) {

np = 0; vertices.clear(); return;

}

//At this point, n is positive

np = n; // set number of points to n vertices.resize(n); // allocate enough room

for (int k=0; k<n; k++) { double theta = (2*M_PI*k)/n;

vertices[k] = Point(cos(theta),sin(theta));

}