Please send questions to st10@humboldt.edu .

CIS 130 - Week 15, Monday, May 3, 2010

*   C++ provides the general while loop, usable for
    just about any kind of repetition situation,

    as well as the more-specialized for loop,
    especially good for the special repetition case
    of count-controlled loops;

*   C++ provides the general if statement, usable
    for just about any kind of branching situation,

    as well as a more-specialized branching statement,
    the switch statement

*   Let's say I have letter grades expressed as char's.
    and I want a function that expects such a letter grade
    and produces a string message corresponding to that
    grade:

    'A' or 'B' ---> "High pass"
    'C' or 'D' ---> "Pass"
    'F' or 'I' ---> "Try again"
    anything else ---> "Huh?"

contract: grade_msg: char -> string

purpose:
   expects a char corresponding to a letter grade,
   and produces the appropriate string for that grade,
   according to the following:

    'A' or 'B' ---> "High pass"
    'C' or 'D' ---> "Pass"
    'F' or 'I' ---> "Try again"
    anything else ---> "Huh?"

string grade_msg(char grade)

examples:
    grade_msg('A') == "High pass"
    grade_msg('D') == "Pass"
    grade_msg('F') == "Try again"
    grade_msg('Q') == "Huh?"

string grade_msg(char grade)
{
    if ( (grade == 'A') || (grade == 'B') )
        return "High pass";
    else if ( (grade == 'C') || (grade == 'D') )
        return "Pass";
    else if ( (grade == 'F') || (grade == 'I') )
        return "Try again";
    else 
        return "Huh?";
}

*   when a switch may be a good choice:
    *   when you are making exactly 1 of 3 or more choices
    *   your choices can be represented as either int, char,
        bool, or other "integral" (integer-based) types

*   syntax: things in [] are OPTIONAL below...

    switch(int_or_char_or_bool_expr)
    {
        case int_or_char_or_bool_value1:
            statement;
            ...
            [break;]

        case int_or_char_or_bool_value2:
            statement;
            ...
            [break;]
 
        ...
        [default:
            statement;
            ...]
    }

*   semantics: the switch's expression is evaluated,
    and compared to each case's value,
    and as soon as one matches, execution begins at the statement
        following that case,
	and merrily KEEPS GOING through all the statements
	    until you either hit a break or the end of the
	    switch statement;
            ...then you continue after the switch statement

    string desired_msg;
    if ( (grade == 'A') || (grade == 'B') )
        desired_msg = "High pass";
    else if ( (grade == 'C') || (grade == 'D') )
        desired_msg = "Pass";
    else if ( (grade == 'F') || (grade == 'I') )
        desired_msg = "Try again";
    else
        desired_msg = "Huh?";
    return desired_msg;

    string desired_msg;
    switch(grade)
    {
        case 'A':
        case 'B':
            desired_msg = "High pass";
            break;   

        case 'C':
        case 'D':
            desired_msg = "Pass";
            break;

        case 'F':
        case 'I':
            desired_msg = "Try again";
            break;

        default:
            desired_msg = "Huh?";
    }
    return desired_msg;
        
*   CAREFUL -- if you leave out a break, you "fall through" to the
    remaining cases' actions! 

*   NOTE: a switch is a BRANCHING statement -- it is not a loop!
    (it can go IN a loop, though -- often quite usefully!)

    it is a SYNTAX error to use a non-integer-based switch
    expression (int, bool, char are all integer-based)

    it is a LOGIC/SEMANTIC error to leave out needed breaks

    you'll get SOME kind of error -- sometimes syntax, sometimes
    logic/semantic -- if you try to stick a bool condition as
    the case value...

File input/output

*   One of the ways of doing file input/output in C++ is with
    the help of the fstream C++ standard library;

    We've been using cin and cout from the C++ iostream standard
    library to do interactive input/output;

    We can use fstream to do a similar style of input from files
    and output to files

*   Here's how you do it:

#include <fstream>  

// fun fact: fstream includes iostream! So you don't HAVE to #include
//    <iostream> when you #include <fstream> ... it's OK if you do, though,
//    because #ifndef keeps that from being problematic...

*   IF you want to read from a file,
    you need to set up an INPUT file stream:

    ifstream my_infile_stream_name;

    If you want to WRITE to a file,
    you need to set up an OUTPUT file stream:

    ofstream my_outfile_stream_name;

*   then, you CONNECT these "logical" file streams to "physical"
    files in your operating system (on your computer) 
    by using an open *method*

    (oh! Preview of CIS 230 -- these are OBJECTS, and we are
    calling their open method -- like a function for an object...)

    <name_of_logical_file_stream>.open("string name of file to open");

    for our streams above,

    // this tries to open the file infile.txt in the current
    //    directory (where this C++ program is running)
    //    for reading...
    // (I THINK you get a run-time error if this file doesn't
    //    exist or isn't allowed to be read)

    my_infile_stream_name.open("infile.txt");

    // this tries to open the file outfile.txt in the current 
    //    directory -- if outfile.txt doesn't exist, it will
    //    create it. (If it already exists, its current contents
    //    are NUKED -- look up opening an output stream for appending
    //    if you want to add to the end of a file...)

    my_outfile_stream_name.open("outfile.txt");

*   once these are open --

    read from an input file stream like you read from cin!

    my_infile_stream_name >> data1 >> data2;

    write to an output file stream like you write to cout!

    my_outfile_stream_name << "something" << spiciness_level(10000) << endl;

*   when you are done reading/writing, it is considered GOOD STYLE
    (and sometimes it is quite important!!) to CLOSE your file
    stream using its close method:

    my_infile_stream_name.close();
    my_outfile_stream_name.close();

*   say we want a function that expects a string and simply
    writes it to a file named "looky.txt"

    (yes, it is a little silly)

contract: write_to_looky: string -> void

purpose: expects a string, and returns nothing,
	 but it has the side-effect of writing that
	 string to the file in the current working
	 directory named looky.txt (nuking whatever
	 previous contents it had)

void write_to_looky(string msg)

examples:
    If I call:

    write_to_looky("Hi there! Lookit me!!");

    ...this should have the side effect of causing the
    file in the current directory named looky.txt to
    contain:
Hi there! Lookit me!!

#include <fstream>
using namespace std;

void write_to_looky(string msg)
{
    // create an output file stream

    ofstream outstream;

    // open it for writing, to a file named looky.txt

    outstream.open("looky.txt");

    // write the desired message to that file using outstream
    //    (I'm choosing to add a newline to the end of the message)

    outstream << msg << endl;

    // close the output file stream when you're done

    outstream.close();
}

(after-class note)
*   see posted example sum_from_file for an example of reading from a
    file
    *   aha -- the ifstream and ofstream open method expects an
        old-style char* string!!

    *   BUT: string has a method c_str() that returns that string
        in char* form!

    *   So, for a string name, 
    	    name.c_str()
	...is an expression of type char*, containing the char* string
	   equivalent to the new-style string in name

*   I've posted two examples of simple C++ formatting, also:
    *   these require you to include the iomanip standard library
        ALONG WITH iostream:

        #include <iostream>
        #include <iomanip>
        using namespace std;

    *   setw(int) --- when put into an output stream, causes the
        NEXT value to be right-justified in a field at least that
        wide (if the value is wider, though, it does not TRUNCATE it)

        example: setw_play

    *   to format a double to, say, 3 fractional places, 
        here is Savitch's "magic formula" (pp. 56-57) for getting all
        doubles displayed using cout to format to a fixed number
        of fractional places (it does NOT affect int values, note!)

        cout.setf(ios::fixed);
        cout.setf(ios::showpoint);
        cout.precision(3);   // or however many fractional places you want

        // should stay in effect, IN THAT FUNCTION, until you set
        //    it precision to some other value -- I think! 8-)

        example: precision_play

        *   note: this does NOT actually change those values! It just
            DISPLAYS them to that precision;