Chapter 7 - Chapter 7 Chapter Completing a Program Bjarne...

Info iconThis preview shows page 1. Sign up to view the full content.

View Full Document Right Arrow Icon
This is the end of the preview. Sign up to access the rest of the document.

Unformatted text preview: Chapter 7 Chapter Completing a Program Bjarne Stroustrup www.stroustrup.com/Programming Abstract Abstract Tokens and token streams Structs and classes Cleaning up the code Prompts Program organization constants Recovering from errors Commenting Code review Testing Testing A word on complexity and difficulty Variables Variables Stroustrup/Programming 2 '+' Token Token '8' 2.3 We want a type that can hold a “kind” and a value: struct Token { char kind; double value; // … }; Token t; t.kind = '8'; // define a type called Token // define // what kind of token // what // used for numbers (only): a value // used // . (dot) is used to access members // (dot) // (use ‘8’ to mean “number”) (use t.value = 2.3; Token u = t; Token cout << u.value; // a Token behaves much like a built-in type, such as int behaves // so u becomes a copy of t // so becomes // will print 2.3 // will Stroustrup/Programming 3 Token Token struct Token { // user-defined type called Token // user-defined // data members // data // function members // function }; A struct is the simplest form of a class struct “class” is C++’s term for “user-defined type” Defining types is the crucial mechanism for organizing programs in C++ as in most other modern languages a class (including structs) can have class struct data members (to hold information), and function members (providing operations on the data) Stroustrup/Programming 4 Token Token struct Token { char kind; double value; // what kind of token // what // for numbers: a value // for Token(char ch) : kind(ch), value(0) { } Token(char Token(char ch, double val) : kind(ch), value(val) { } // constructor // constructor // constructor // constructor }; A constructor has the same name as its class A constructor defines how an object of a class is initialized Here kind is initialized with ch, and Here kind ch, value is initialized with val or 0 value val Token('+'); // make a Token of “kind” ‘+’ // Token ‘+’ Token('8',4.5); // make a Token of “kind” ‘8’ and value 4.5 // Token ‘8’ 4.5 Stroustrup/Programming 5 Token_stream Token_stream A Token_stream reads characters, producing Tokens on demand reads We can put a Token into a Token_stream for later use Token_stream A Token_stream uses a “buffer” to hold tokens we put back into it uses Token_stream buffer: empty Input stream: 1+2*3; For 1+2*3;, expression() calls term() which reads 1, then reads +, For 1+2*3; then +, decides that + is a job for “someone else” and puts + back in the Token_stream decides “someone Token_stream (where expression() will find it) (where will Token_stream buffer: Token(‘+') Input stream: Stroustrup/Programming 2*3; 6 Token_stream Token_stream A Token_stream reads characters, producing Tokens reads We can put back a Token class Token_stream { // representation: not directly accessible to users: // representation: bool full; // is there a Token in the buffer? is in Token buffer; // here is where we keep a Token put back using putback() Token here Token public: // user interface: // user Token get(); // get a Token // Token void putback(Token); // put a Token back into the Token_stream void Token Token_stream Token_stream(); // constructor: make a Token_stream // constructor: }; A constructor defines how an object of a class is initialized has the same name as its class, and no return type Stroustrup/Programming 7 Token_stream implementation Token_stream class Token_stream { bool full; // is there a Token in the buffer? is in Token buffer; // here is where we keep a Token put back using putback() Token here Token public: Token get(); // get a Token // Token void putback(Token); // put back a Token // Token Token_stream() :full(false), buffer(0) { } // the buffer starts empty }; }; void Token_stream::putback(Token t) { if (full) error("putback() into a full buffer"); buffer=t; full=true; } Stroustrup/Programming 8 Token_stream implementation Token_stream Token Token_stream::get() // read a Token from the Token_stream read { if (full) { full=false; return buffer; } // check if we already have a Token ready if check char ch; cin >> ch; // note that >> skips whitespace (space, newline, tab, etc.) // note skips switch (ch) { case '(': case ')': case ';': case 'q': case '+': case '-': case '*': case '/': case return Token(ch); // let each character represent itself return // let case '.': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { cin.putback(); // put digit back into the input stream // put double val; cin >> val; // read a floating-point number // read return Token('8',val); // let ‘8’ represent “a number” // ‘8’ } default: error("Bad token"); } } Stroustrup/Programming 9 Streams Streams Note that the notion of a stream of data is extremely Note general and very widely used general Most I/O systems E.g., C++ standard I/O streams with or without a putback/unget operation We used putback for both Token_stream and cin We Token_stream cin Stroustrup/Programming 10 The calculator is primitive The We can improve it in stages Style – clarity of code Comments Naming Use of functions ... Functionality – what it can do Better prompts Recovery after error Negative numbers % (remainder/modulo) Pre-defined symbolic values Variables … Stroustrup/Programming 11 Prompting Prompting Initially we said we wanted Expression: 2+3; 5*7; 2+9; Result : 5 Expression: Result: 35 Expression: Result: 11 Expression: But this is what we implemented 2+3; 5*7; 2+9; 5 35 11 What do we really want? > 2+3; =5 > 5*7; = 35 > Stroustrup/Programming 12 Adding prompts and output indicators Adding double val = 0; cout << "> "; while (cin) { Token t = ts.get(); if (t.kind == 'q') break; if (t.kind == ';') if cout << "= " << val << "\n > "; cout else ts.putback(t); val = expression(); } > 2+3; 5*7; 2+9; =5 > = 35 > = 11 > // print prompt // print // check for “quit” // check // print “= result” and prompt // print // read and evaluate expression // read evaluate the program doesn’t see input before you hit “enter/return” Stroustrup/Programming 13 “But my window disappeared!” Test case: +1; cout << "> "; // prompt // prompt while (cin) { Token t = ts.get(); while (t.kind == ';') t=ts.get(); // eat all semicolons // eat if (t.kind == 'q') { keep_window_open("~~"); return 0; } ts.putback(t); cout << "= " << expression() << "\n > "; } keep_window_open("~~"); return 0; Stroustrup/Programming 14 The code is getting messy The Bugs thrive in messy corners Time to clean up! Read through all of the code carefully Improve comments Replace obscure names with better ones Improve use of functions Add functions to simplify messy code Remove “magic constants” Try to be systematic (“have you looked at all the code?”) E.g. '8' ('8' what could that mean? Why '8'?) E.g. '8' '8' Once you have cleaned up, let a friend/colleague review the Once code (“code review”) code Stroustrup/Programming 15 Remove “magic constants” Remove // Token “kind” values: // Token const char number = '8'; const char quit = 'q'; const char print = ';'; // a floating-point number // floating-point // an exit command // an // a print command // print // User interaction strings: // User const string prompt = "> "; const string result = "= "; // indicate that a result follows // indicate Stroustrup/Programming 16 Remove “magic constants” Remove // In Token_stream::get(): // Token_stream::get() case '.': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { cin.putback(); // put digit back into the input stream put double val; cin >> val; // read a floating-point number read return Token(number,val); // rather than Token('8',val) return rather } // In primary(): // In case number: // rather than case '8': // rather return t.value; // return the number’s value // return Stroustrup/Programming 17 Remove “magic constants” Remove // In main(): // main() while (cin) { cout << prompt; // rather than "> " // "> Token t = ts.get(); while (t.kind ==print) t=ts.get(); // rather than ==';' // ==';' if (t.kind == quit) { // rather than =='q' rather keep_window_open(); return 0; } ts.putback(t); cout << result << expression() << endl; } Stroustrup/Programming 18 Remove “magic constants” Remove But what’s wrong with “magic constants”? “Magic” is detrimental to your (mental) health! It causes you to stay up all night searching for bugs It causes space probes to self destruct (well … it can … sometimes …) If a “constant” could change (during program maintenance) or if If someone might not recognize it, use a symbolic constant. someone Everybody knows 3.14159265358979323846264, 12, -1, 365, 24, Everybody 3.14159265358979323846264 2.7182818284590, 299792458, 2.54, 1.61, -273.15, 6.6260693e-34, 2.7182818284590 2.54 -273.15 0.5291772108e-10, 6.0221415e23 and 42! 0.5291772108e-10, 6.0221415e23 No; they don’t. Note that a change in precision is often a significant change 3.14! Note 3.14! =3.14159265 0 and 1 are usually fine without explanation, -1 and 2 sometimes (but -1 rarely) are. rarely) 12 can be okay (the number of months in a year rarely changes), but probably is not (see Chapter 10). probably If a constant is used twice, it should probably be symbolic That way, you can change it in one place Stroustrup/Programming 19 So why did we use “magic constants”? So To make a point Now you see how ugly that first code was just look back to see Because we forget (get busy, etc.) and write ugly code “Cleaning up code” is a real and important activity Not just for students Re-test the program whenever you have made a change Ever so often, stop adding functionality and “go back” and Ever review code review It saves time It Stroustrup/Programming 20 20 Recover from errors Recover Any user error terminates the program That’s not ideal Structure of code int main() try { // … do “everything” … // do } catch (exception& e) { // … // } catch(…) { // … // } // catch errors we understand something about // catch // catch all other errors // catch Stroustrup/Programming 21 Recover from errors Recover Move code that actually does something out of main() leave main() for initialization and cleanup only leave int main() // step 1 // step try { calculate(); keep_window_open(); // cope with Windows console mode // cope return 0; } catch (exception& e) { // errors we understand something about // errors cerr << e.what() << endl; keep_window_open("~~"); return 1; } catch (...) { // other errors // other cerr << "exception \n"; keep_window_open("~~"); return 2; Stroustrup/Programming } 22 Recover from errors Recover Separating the read and evaluate loop out into calculate() allows us to calculate() Separating simplify it no more ugly keep_window_open() ! keep_window_open() no void calculate() { while (cin) { cout << prompt; Token t = ts.get(); while (t.kind == print) t=ts.get(); // first discard all “prints” // first if (t.kind == quit) return; // quit // quit ts.putback(t); cout << result << expression() << endl; } } Stroustrup/Programming 23 Recover from errors Recover Move code that handles exceptions from which we can Move recover from error() to calculate() error() calculate() int main() // step 2 // step try { calculate(); keep_window_open(); // cope with Windows console mode // cope return 0; } catch (...) { // other errors (don’t try to recover) // other cerr << "exception \n"; keep_window_open("~~"); return 2; } Stroustrup/Programming 24 Recover from errors Recover void calculate() { while (cin) try { cout << prompt; Token t = ts.get(); while (t.kind == print) t=ts.get(); // first discard all “prints” // first if (t.kind == quit) return; // quit // quit ts.putback(t); cout << result << expression() << endl; } catch (exception& e) { cerr << e.what() << endl; // write error message // write clean_up_mess(); // <<< The tricky part! // The } } Stroustrup/Programming 25 Recover from errors Recover First try void clean_up_mess() { while (true) { // skip until we find a print while // skip Token t = ts.get(); if (t.kind == print) return; } } Unfortunately, that doesn’t work all that well. Why not? Consider the Unfortunately, input 1@$z; 1+3; 1@$z; When you try to clean_up_mess() from the bad token @, you get a “Bad from bad you token” error trying to get rid of $ error We always try not to get errors while handling errors Stroustrup/Programming 26 Recover from errors Recover Classic problem: the higher levels of a program can’t recover Classic well from low-level errors (i.e., errors with bad tokens). well Only Token_stream knows about characters Only Token_stream We must drop down to the level of characters The solution must be a modification of Token_stream: The Token_stream class Token_stream { bool full; // is there a Token in the buffer? is Token buffer; // here is where we keep a Token put back using putback() Token here public: Token get(); // get a Token // get void putback(Token t); // put back a Token // put Token_stream(); // make a Token_stream that reads from cin // make void ignore(char c); // discard tokens up to and including a c // discard }; Stroustrup/Programming 27 Recover from errors Recover void Token_stream::ignore(char c) // skip characters until we find a c; also discard that c // skip { // first look in buffer: // first if (full && c==buffer.kind) { // && means and // means full = false; return; } full = false; // discard the contents of buffer // discard // now search input: // now char ch = 0; while (cin>>ch) if (ch==c) return; } Stroustrup/Programming 28 Recover from errors Recover clean_up_mess() now is trivial and it works void clean_up_mess() { ts.ignore(print); ts.ignore(print); } Note the distinction between what we do and how we do it: clean_up_mess() is what users see; it cleans up messes The users are not interested in exactly how it cleans up messes ts.ignore(print) is the way we implement clean_up_mess() clean_up_mess() We can change/improve the way we clean up messes without affecting users Stroustrup/Programming 29 Features Features We did not (yet) add Read about that in Chapter 7 Negative numbers % (remainder/modulo) Pre-defined symbolic values Variables % and variables demonstrate useful techniques Major Point Providing “extra features” early causes major problems, delays, bugs, Providing and confusion and “Grow” your programs First get a simple working version Then, add features that seem worth the effort Stroustrup/Programming 30 Next lecture Next In the next two lectures, we’ll take a more In systematic look at the language features we have used so far. In particular, we need to know more about classes, functions, statements, expressions, and types. statements, Stroustrup/Programming 31 ...
View Full Document

This note was uploaded on 02/18/2012 for the course CSCE 121 taught by Professor Walter daugherity during the Fall '09 term at Texas A&M.

Ask a homework question - tutors are online