Upload
natalie-vaughn
View
254
Download
3
Tags:
Embed Size (px)
Citation preview
Chapter 7Chapter 7Completing a ProgramCompleting a Program
Bjarne StroustrupBjarne Stroustrupwww.stroustrup.com/Programmingwww.stroustrup.com/Programming
AbstractAbstract Tokens and token streamsTokens and token streams
Structs and classesStructs and classes Cleaning up the codeCleaning up the code
PromptsPrompts Program organizationProgram organization
constantsconstants Recovering from errorsRecovering from errors CommentingCommenting Code reviewCode review Testing Testing
A word on complexity and difficultyA word on complexity and difficulty Variables Variables
22Stroustrup/ProgrammingStroustrup/Programming
TokenToken
We want a type that can hold a “kind” and a value:We want a type that can hold a “kind” and a value:
struct Token {struct Token { // // define a type called Tokendefine a type called Tokenchar kind;char kind; // // what kind of tokenwhat kind of tokendouble value;double value; // // used for numbers (only): a valueused for numbers (only): a value // // ……
};};
Token t;Token t;t.kind = '8';t.kind = '8'; // // . . (dot) is used to access members(dot) is used to access members
//// (use ‘8’ to mean “number”)(use ‘8’ to mean “number”)t.value = 2.3;t.value = 2.3;
Token u = t; Token u = t; // // a a Token Token behaves much like a built-in type, such asbehaves much like a built-in type, such as int int// // soso u u becomes a copy ofbecomes a copy of t t
cout << u.value;cout << u.value; // // will printwill print 2.3 2.3
33
'8'
2.3
'+'
Stroustrup/ProgrammingStroustrup/Programming
TokenToken
struct Token {struct Token { // // user-defined type called Tokenuser-defined type called Token
// // data membersdata members
// // function membersfunction members
};};
A A structstruct is the simplest form of a class is the simplest form of a class ““class” is C++’s term for “user-defined type”class” is C++’s term for “user-defined type” Defining types is the crucial mechanism for organizing programs in C++Defining types is the crucial mechanism for organizing programs in C++
as in most other modern languagesas in most other modern languages a a classclass (including (including structstructs) can haves) can have
data members (to hold information), anddata members (to hold information), and function members (providing operations on the data)function members (providing operations on the data)
44Stroustrup/ProgrammingStroustrup/Programming
TokenToken
struct Token {struct Token {char kind;char kind; // // what kind of tokenwhat kind of tokendouble value;double value; // // for numbers: a valuefor numbers: a value
Token(char ch) : kind(ch), value(0) { }Token(char ch) : kind(ch), value(0) { } // // constructorconstructorToken(char ch, double val) : kind(ch), value(val) { }Token(char ch, double val) : kind(ch), value(val) { } // // constructorconstructor
};}; A constructor has the same name as its classA constructor has the same name as its class A constructor defines how an object of a class is initializedA constructor defines how an object of a class is initialized
Here Here kindkind is initialized with is initialized with ch,ch, and and valuevalue is initialized with is initialized with valval or or 00 Token('+');Token('+'); // make a // make a TokenToken of “kind” of “kind” ‘+’‘+’ Token('8',4.5);Token('8',4.5); // make a // make a TokenToken of “kind” of “kind” ‘8’‘8’ and value and value 4.54.5 55Stroustrup/ProgrammingStroustrup/Programming
Token_streamToken_stream
A A Token_stream Token_stream reads characters, producingreads characters, producing Token Tokens on demands on demand We can put aWe can put a Token Token into a into a Token_streamToken_stream for later use for later use A A Token_stream Token_stream uses a “buffer” to hold tokens we put back into ituses a “buffer” to hold tokens we put back into it
66
1+2*3;
emptyToken_stream buffer:
Input stream:
For For 1+2*3;1+2*3;,, expression() expression() calls calls term() term() which reads which reads 11, then reads , then reads +,+,decides that decides that ++ is a job for is a job for “someone else”“someone else” and puts and puts ++ back in the back in the Token_streamToken_stream(where (where expression() expression() will find it)will find it)
Token_stream buffer: Token(‘+')
2*3;Input stream:
Stroustrup/ProgrammingStroustrup/Programming
Token_streamToken_stream AA Token_stream Token_stream reads characters, producingreads characters, producing Token Tokenss WWe can put back ae can put back a Token Token
class Token_stream {class Token_stream {// // representation: not directly accessible to users:representation: not directly accessible to users:bool full;bool full; // // is there ais there a Token Token in thein the buffer buffer??Token buffer; // Token buffer; // here is where we keep ahere is where we keep a Token put back usingToken put back using putback() putback()
public:public:// // user interface:user interface:Token get();Token get(); // // get a get a TokenTokenvoid putback(Token); // void putback(Token); // put a put a TokenToken back into the back into the Token_streamToken_streamToken_stream();Token_stream();// // constructor: make aconstructor: make a Token_stream Token_stream
};};
A constructorA constructor defines how an object of a class is initializeddefines how an object of a class is initialized has the same name as its class, and no return typehas the same name as its class, and no return type
77Stroustrup/ProgrammingStroustrup/Programming
Token_stream implementationToken_stream implementation
class Token_stream {class Token_stream {bool full;bool full; // // is there ais there a Token Token in thein the buffer buffer??Token buffer; // Token buffer; // here is where we keep ahere is where we keep a Token put back usingToken put back using putback() putback()
public:public:Token get();Token get(); // // get a get a TokenTokenvoid putback(Token);void putback(Token); // // put back a put back a TokenTokenToken_stream() :full(false), buffer(0) { } //Token_stream() :full(false), buffer(0) { } // the buffer starts empty the buffer starts empty
};};
void Token_stream::putback(Token t)void Token_stream::putback(Token t){{
if (full) error("putback() into a full buffer");if (full) error("putback() into a full buffer");buffer=t;buffer=t;full=true;full=true;
}}88Stroustrup/ProgrammingStroustrup/Programming
Token_stream implementationToken_stream implementationToken Token_stream::get()Token Token_stream::get() // // read a Token from the Token_streamread a Token from the Token_stream{{
if (full) { full=false; return buffer; } // if (full) { full=false; return buffer; } // check if we already have a Token readycheck if we already have a Token ready
char ch;char ch;cin >> ch;cin >> ch; // // note thatnote that >> >> skips whitespace (space, newline, tab, etc.)skips whitespace (space, newline, tab, etc.)
switch (ch) {switch (ch) {case '(': case ')': case ';': case 'q': case '+': case '-': case '*': case '/': case '(': case ')': case ';': case 'q': case '+': case '-': case '*': case '/':
return Token(ch);return Token(ch); // // let each character represent itselflet each character represent itselfcase '.':case '.':case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':{{ cin.putback(ch);cin.putback(ch); // // put digit back into the input streamput digit back into the input stream
double val;double val;cin >> val;cin >> val; // // read a floating-point numberread a floating-point numberreturn Token('8',val);return Token('8',val); // // let let ‘8’‘8’ represent “a number” represent “a number”
}}default:default:
error("Bad token");error("Bad token");}}
}} 99Stroustrup/ProgrammingStroustrup/Programming
StreamsStreams
Note that the notion of a stream of data is extremely Note that the notion of a stream of data is extremely general and very widely usedgeneral and very widely used Most I/O systemsMost I/O systems
E.g.E.g., C++ standard I/O streams, C++ standard I/O streams
with or without a putback/unget operationwith or without a putback/unget operation We used putback for both We used putback for both Token_streamToken_stream and and cincin
1010Stroustrup/ProgrammingStroustrup/Programming
The calculator is primitiveThe calculator is primitive
We can improve it in stagesWe can improve it in stages Style – clarity of codeStyle – clarity of code
CommentsComments NamingNaming Use of functionsUse of functions ......
Functionality – what it can doFunctionality – what it can do Better promptsBetter prompts Recovery after errorRecovery after error Negative numbersNegative numbers % (remainder/modulo)% (remainder/modulo) Pre-defined symbolic valuesPre-defined symbolic values VariablesVariables ……
1111Stroustrup/ProgrammingStroustrup/Programming
PromptingPrompting
Initially we said we wantedInitially we said we wantedExpression: 2+3; 5*7; 2+9;Expression: 2+3; 5*7; 2+9;Result : 5Result : 5Expression: Result: 35Expression: Result: 35Expression: Result: 11Expression: Result: 11Expression:Expression:
But this is what we implementedBut this is what we implemented2+3; 5*7; 2+9;2+3; 5*7; 2+9;5535351111
What do we really want?What do we really want?> 2+3;> 2+3;= 5= 5> 5*7;> 5*7;= 35= 35>> 1212Stroustrup/ProgrammingStroustrup/Programming
Adding prompts and output indicatorsAdding prompts and output indicatorsdouble val = 0;double val = 0;cout << "> ";cout << "> "; // // print promptprint promptwhile (cin) {while (cin) {
Token t = ts.get();Token t = ts.get();if (t.kind == 'q') break;if (t.kind == 'q') break; // // check for “quit”check for “quit”if (t.kind == ';') if (t.kind == ';')
cout << "= " << val << "\n > ";cout << "= " << val << "\n > "; // // print “= result” and promptprint “= result” and promptelseelse
ts.putback(t);ts.putback(t);val = expression();val = expression(); // // read andread and evaluate expressionevaluate expression
}}
> 2+3; 5*7; 2+9;> 2+3; 5*7; 2+9; the program doesn’t see input before you hit “enter/return”the program doesn’t see input before you hit “enter/return”= 5= 5> = 35> = 35> = 11> = 11>> 1313Stroustrup/ProgrammingStroustrup/Programming
““But my window disappeared!”But my window disappeared!”
Test case:Test case: +1; +1;
cout << "> ";cout << "> "; // // promptpromptwhile (cin) {while (cin) {
Token t = ts.get();Token t = ts.get();while (t.kind == ';') t=ts.get();while (t.kind == ';') t=ts.get(); // // eat all semicolonseat all semicolonsif (t.kind == 'q') {if (t.kind == 'q') {
keep_window_open("~~");keep_window_open("~~");return 0;return 0;
}}ts.putback(t);ts.putback(t);cout << "= " << expression() << "\n > ";cout << "= " << expression() << "\n > ";
}}keep_window_open("~~");keep_window_open("~~");return 0;return 0;
1414Stroustrup/ProgrammingStroustrup/Programming
The code is getting messyThe code is getting messy Bugs thrive in messy cornersBugs thrive in messy corners Time to clean up!Time to clean up!
Read through all of the code carefullyRead through all of the code carefully Try to be systematic (“have you looked at all the code?”)Try to be systematic (“have you looked at all the code?”)
Improve commentsImprove comments Replace obscure names with better onesReplace obscure names with better ones Improve use of functionsImprove use of functions
Add functions to simplify messy codeAdd functions to simplify messy code Remove “magic constants”Remove “magic constants”
E.g. E.g. '8''8' ( ('8''8' what could that mean? Why what could that mean? Why '8''8'?)?) Once you have cleaned up, let a friend/colleague review the Once you have cleaned up, let a friend/colleague review the
code (“code review”)code (“code review”)
1515Stroustrup/ProgrammingStroustrup/Programming
Remove “magic constants”Remove “magic constants”
// // Token “kind” values:Token “kind” values:
const char number = '8';const char number = '8'; // // a floating-point numbera floating-point number
const char quit = 'q';const char quit = 'q'; // // an exit commandan exit command
const char print = ';';const char print = ';'; // // a print commanda print command
// // User interaction strings:User interaction strings:
const string prompt = "> ";const string prompt = "> ";
const string result = "= ";const string result = "= "; // // indicate that a result followsindicate that a result follows
1616Stroustrup/ProgrammingStroustrup/Programming
Remove “magic constants”Remove “magic constants”
// // In In Token_stream::get()Token_stream::get()::
case '.':case '.':case '0': case '1': case '2': case '3': case '4':case '0': case '1': case '2': case '3': case '4':case '5': case '6': case '7': case '8': case '9':case '5': case '6': case '7': case '8': case '9':
{{ cin.putback(ch);cin.putback(ch); // // put digit back into the input put digit back into the input streamstream
double val;double val;cin >> val;cin >> val; // // read a floating-point numberread a floating-point numberreturn Token(number,val); // return Token(number,val); // rather thanrather than Token('8',val) Token('8',val)
}}
// // InIn primary() primary()::
case number:case number: // // rather thanrather than case '8': case '8':return t.value;return t.value; // // return the number’s valuereturn the number’s value 1717Stroustrup/ProgrammingStroustrup/Programming
Remove “magic constants”Remove “magic constants”
// // In In main()main()::
while (cin) {while (cin) {cout << prompt;cout << prompt; // // rather than rather than "> ""> "Token t = ts.get();Token t = ts.get();while (t.kind ==print) t=ts.get();while (t.kind ==print) t=ts.get(); // // rather than rather than ==';'==';'if (t.kind == quit) {if (t.kind == quit) { //// rather thanrather than =='q' =='q'
keep_window_open();keep_window_open();return 0;return 0;
}}ts.putback(t);ts.putback(t);cout << result << expression() << endl;cout << result << expression() << endl;
}}
1818Stroustrup/ProgrammingStroustrup/Programming
Remove “magic constants”Remove “magic constants” But what’s wrong with “magic constants”?But what’s wrong with “magic constants”?
Everybody knows Everybody knows 3.141592653589793238462643.14159265358979323846264,, 12, -1 12, -1,, 365 365,, 24, 24, 2.71828182845902.7182818284590,, 299792458 299792458, , 2.542.54,, 1.61, 1.61, -273.15-273.15, , 6.6260693e-34, 6.6260693e-34, 0.5291772108e-10,0.5291772108e-10, 6.0221415e236.0221415e23 and and 42 42!!
No; they don’t.No; they don’t.
““Magic” is detrimental to your (mental) health!Magic” is detrimental to your (mental) health! It causes you to stay up all night searching for bugsIt causes you to stay up all night searching for bugs It causes space probes to self destruct (well … it can … sometimes …)It causes space probes to self destruct (well … it can … sometimes …)
If a “constant” could change (during program maintenance) or if If a “constant” could change (during program maintenance) or if someone might not recognize it, use a symbolic constant.someone might not recognize it, use a symbolic constant. Note that a change in precision is often a significant change Note that a change in precision is often a significant change 3.14!3.14!
=3.14159265=3.14159265 00 and and 11 are usually fine without explanation, are usually fine without explanation, -1-1 and and 22 sometimes (but sometimes (but
rarely) are.rarely) are. 1212 can be okay (the number of months in a year rarely changes), but can be okay (the number of months in a year rarely changes), but
probably is not (see Chapter 10).probably is not (see Chapter 10). If a constant is used twice, it should probably be symbolicIf a constant is used twice, it should probably be symbolic
That way, you can change it in one placeThat way, you can change it in one place
1919Stroustrup/ProgrammingStroustrup/Programming
So why did we use “magic constants”?So why did we use “magic constants”?
To make a pointTo make a point Now you see how ugly that first code wasNow you see how ugly that first code was
just look back to seejust look back to see
Because we forget (get busy, etc.) and write ugly codeBecause we forget (get busy, etc.) and write ugly code ““Cleaning up code” is a real and important activityCleaning up code” is a real and important activity
Not just for studentsNot just for students Re-test the program whenever you have made a changeRe-test the program whenever you have made a change
Ever so often, stop adding functionality and “go back” and Ever so often, stop adding functionality and “go back” and review codereview code
It saves time It saves time
2020Stroustrup/ProgrammingStroustrup/Programming
Recover from errorsRecover from errors
Any user error terminates the programAny user error terminates the program That’s not idealThat’s not ideal Structure of codeStructure of code
int main()int main()try {try {
// // … do “everything” …… do “everything” …}}catch (exception& e) {catch (exception& e) { // // catch errors we understand something catch errors we understand something
aboutabout// // ……
}}catch(…) {catch(…) { // // catch all other errorscatch all other errors
// // ……}}
2121Stroustrup/ProgrammingStroustrup/Programming
Recover from errorsRecover from errors
Move code that actually does something out of main()Move code that actually does something out of main() leave main() for initialization and cleanup onlyleave main() for initialization and cleanup only
int main()int main() // // step 1step 1try {try {
calculate();calculate();keep_window_open();keep_window_open(); // // cope with Windows console modecope with Windows console modereturn 0;return 0;
}}catch (exception& e) {catch (exception& e) { // // errors we understand something abouterrors we understand something about
cerr << e.what() << endl;cerr << e.what() << endl;keep_window_open("~~");keep_window_open("~~");return 1;return 1;
}}catch (...) {catch (...) { // // other errorsother errors
cerr << "exception \n";cerr << "exception \n";keep_window_open("~~");keep_window_open("~~");return 2;return 2;
}} 2222Stroustrup/ProgrammingStroustrup/Programming
Recover from errorsRecover from errors
Separating the read and evaluate loop out into Separating the read and evaluate loop out into calculate()calculate() allows us to allows us to simplify it simplify it no more ugly no more ugly keep_window_open()keep_window_open() ! !
void calculate()void calculate(){{
while (cinwhile (cin) {) {
cout << prompt;cout << prompt;Token t = ts.get();Token t = ts.get();while (t.kind == print) t=ts.get();while (t.kind == print) t=ts.get(); // // first discard all “prints”first discard all “prints”if (t.kind == quit) return;if (t.kind == quit) return; // // quitquitts.putback(t);ts.putback(t);cout << result << expression() << endl;cout << result << expression() << endl;
}}}}
2323Stroustrup/ProgrammingStroustrup/Programming
Recover from errorsRecover from errors
Move code that handles exceptions from which we can Move code that handles exceptions from which we can recover from recover from error()error() to to calculate()calculate()
int main()int main() // // step 2step 2try {try {
calculate();calculate();keep_window_open();keep_window_open(); // // cope with Windows console modecope with Windows console modereturn 0;return 0;
}}catch (...) {catch (...) { // // other errors (don’t try to recover)other errors (don’t try to recover)
cerr << "exception \n";cerr << "exception \n";keep_window_open("~~");keep_window_open("~~");return 2;return 2;
}}
2424Stroustrup/ProgrammingStroustrup/Programming
Recover from errorsRecover from errors
void calculate()void calculate(){{
while (cin) try {while (cin) try {cout << prompt;cout << prompt;Token t = ts.get();Token t = ts.get();while (t.kind == print) t=ts.get();while (t.kind == print) t=ts.get(); // // first discard all “prints”first discard all “prints”if (t.kind == quit) return;if (t.kind == quit) return; // // quitquitts.putback(t);ts.putback(t);cout << result << expression() << endl;cout << result << expression() << endl;
}}catch (exception& e) {catch (exception& e) {
cerr << e.what() << endl;cerr << e.what() << endl; // // write error messagewrite error messageclean_up_mess();clean_up_mess(); // // <<< <<< The tricky part!The tricky part!
}}}}
2525Stroustrup/ProgrammingStroustrup/Programming
Recover from errorsRecover from errors
First tryFirst tryvoid clean_up_mess()void clean_up_mess(){ {
while (true) {while (true) { // // skip until we find a printskip until we find a printToken t = ts.get();Token t = ts.get();if (t.kind == print) return;if (t.kind == print) return;
}}} }
Unfortunately, that doesn’t work all that well. Why not? Consider the Unfortunately, that doesn’t work all that well. Why not? Consider the input input 1@$z; 1+3;1@$z; 1+3; When you try toWhen you try to clean_up_mess() clean_up_mess() from thefrom the bad tokenbad token @ @,, you get ayou get a “Bad “Bad
token” token” error trying to get rid oferror trying to get rid of $ $ We always try not to get errors while handling errorsWe always try not to get errors while handling errors
2626Stroustrup/ProgrammingStroustrup/Programming
Recover from errorsRecover from errors
Classic problem: the higher levels of a program can’t recover Classic problem: the higher levels of a program can’t recover well from low-level errors (i.e., errors with bad tokens).well from low-level errors (i.e., errors with bad tokens). Only Only Token_streamToken_stream knows about characters knows about characters
We must drop down to the level of charactersWe must drop down to the level of characters The solution must be a modification of The solution must be a modification of Token_streamToken_stream::
class Token_stream {class Token_stream {bool full;bool full; // // is there a Token in the buffer?is there a Token in the buffer?Token buffer; // Token buffer; // here is where we keep a Token put back using putback()here is where we keep a Token put back using putback()
public:public:Token get();Token get(); // // get a Tokenget a Tokenvoid putback(Token t);void putback(Token t); // // put back a Tokenput back a TokenToken_stream();Token_stream(); // // make a Token_stream that reads from cinmake a Token_stream that reads from cinvoid ignore(char c);void ignore(char c); // // discard tokens up to and including a cdiscard tokens up to and including a c
};};2727Stroustrup/ProgrammingStroustrup/Programming
Recover from errorsRecover from errors
void Token_stream::ignore(char c)void Token_stream::ignore(char c)// // skip characters until we find a c; also discard that cskip characters until we find a c; also discard that c
{{// // first look in buffer:first look in buffer:if (full && c==buffer.kind) {if (full && c==buffer.kind) { // // && && meansmeans and and
full = false;full = false;return;return;
}}full = false;full = false; // // discard the contents of bufferdiscard the contents of buffer// // now search input:now search input:char ch = 0;char ch = 0;while (cin>>ch)while (cin>>ch)
if (ch==c) return;if (ch==c) return;}}
2828Stroustrup/ProgrammingStroustrup/Programming
Recover from errorsRecover from errors
clean_up_mess() now is trivialclean_up_mess() now is trivial and it worksand it works
void clean_up_mess()void clean_up_mess(){ {
ts.ignore(print);ts.ignore(print);}}
Note the distinction between what we do and how we do it:Note the distinction between what we do and how we do it: clean_up_mess()clean_up_mess() is what users see; it cleans up messes is what users see; it cleans up messes
The users are not interested in exactly how it cleans up messesThe users are not interested in exactly how it cleans up messes ts.ignore(print)ts.ignore(print) is the way we implement is the way we implement clean_up_mess()clean_up_mess()
We can change/improve the way we clean up messes without affecting usersWe can change/improve the way we clean up messes without affecting users
2929Stroustrup/ProgrammingStroustrup/Programming
FeaturesFeatures
We did not (yet) addWe did not (yet) add Negative numbersNegative numbers % (remainder/modulo)% (remainder/modulo) Pre-defined symbolic valuesPre-defined symbolic values VariablesVariables
Read about that in Chapter 7Read about that in Chapter 7 % and variables demonstrate useful techniques% and variables demonstrate useful techniques
Major PointMajor Point Providing “extra features” early causes major problems, delays, bugs, Providing “extra features” early causes major problems, delays, bugs,
and confusionand confusion ““Grow” your programsGrow” your programs
First get a simple working versionFirst get a simple working version Then, add features that seem worth the effortThen, add features that seem worth the effort
3030Stroustrup/ProgrammingStroustrup/Programming
Next lectureNext lecture
In the next two lectures, we’ll take a more In the next two lectures, we’ll take a more systematic look at the language features we systematic look at the language features we have used so far. In particular, we need to have used so far. In particular, we need to know more about classes, functions, know more about classes, functions, statements, expressions, and types.statements, expressions, and types.
3131Stroustrup/ProgrammingStroustrup/Programming