The Craft Of Coding

CPP 1st Feb pipes and files in systems

Posted by Pete Sun, 31 Jan 2016 02:45:00 GMT

From The Art of Unix Programming by Eric Raymond Basics of the Unix Philosophy

This is the Unix philosophy: Write programs that do one thing and do it well. Write programs to work together. Write programs to handle text streams, because that is a universal interface.

pipes

Although the way pipes are implemented on Unix and Windows are different, they work the same way at the command line, you can join the output of one program to the input of another with a Pipe symbol |. Examples below from Windows command line and Unix – find all the files in the current directory, sort the filenames in reverse and then feed the output to the pager more

> dir /b | sort /r | more
> ls | sort -r | more

The way that these piped programs work is that they either a file name as the first parameter of the command line, but if it is not there the program reads from the standard input cin. The output is then sent to standard output cout which can then be piped to another program.

Implementing Pipes

To implement this type of functionality in your programs, you need to pass a istream& to a function that does the work of the program. The below example adds line numbers to whatever it reads and then send it to standard output.To do this work please create a new project called filter, so that at the end you have a program called filter

void addLineNumbers(istream& is) {
    int lineNo = 0;
    string s;
    while (getline(is, s) ) { // getline does not return end of line chars
        lineNo++;
        cout << setw(4) << lineNo << ": " << s << endl; // need to add back the end of line
    }
}

Then inside main

    if (argc == 2) {
        // create ifstream input using argv[1] and test it exists etc...
        addLineNumbers(input);
    }
    else {
        addLineNumbers(cin);
    }

So in this case main is just the entry point for the program and the program does all of the work elsewhere. The main function is just used for checking the command line parameters and then invoking the worker function correctly.

Once this is working you can use your program to add line numbers to any text file, either by piping the text to the filter.exe program, OR by specifying the file name as the first argument to the program.

>type main.cpp | bin\Debug\filter
>bin\Debug\filter main.cpp 
>dir | bin\Debug\filter

Please note, you do not need to specify the .exe in windows, the command line processor will find the executable program. In Unix executable files are denoted by file attributes, not the file name.

Perhaps it would be simpler if you just did what you’re told and didn’t try to understand things. — Terry Pratchett, Sourcery (channeling Instructor Alberto)

Your current program probably has some code like

    while (cin >> name >> quiz >> total >> score ) {
           // do something with the inputs
   }

But it would be much nicer to be able to write something like

 cout << "Enter Name quiz total and score " << endl;
 QuizResult qr;
 while (cin >> qr ) {
     output << qr;
 }

That way the multiple places in your code that need to know something about how to read and write the variables associated with the quiz can just reuse existing code. To do this you just need to define the two operators >> and <<

In QuizResult.h, INSIDE the class declaration

    friend ostream& operator<<(ostream& output, const QuizResult& qr);
    friend istream& operator>>(istream& output, QuizResult& qr);

and in QuizResult.cpp you need to define these operators - please note that they are NOT member functions of the class, just friends that can see the private data inside the QuizResult objects.

  ostream& operator<<(ostream& output, const QuizResult& qr)
  {
       output << qr.Name << "\t" << qr.Quiz << "\t" << qr.Total << "\t" << qr.Score << endl;
      return output;
  }

  istream& operator>>(istream& input, QuizResult& qr)
  {
      input >> qr.Name >> qr.Quiz >> qr.Total >> qr.Score ;
     return input;
  }

Formatting the output for Floating point numbers

When you have calculated the averages or percentages, you might see that you are getting a few too many decimal digits. This can be addressed by including<iomanip> and sending setprecision(3) to only show three decimal digits (and it will round the output appropriately so that 33.333 becomes 33.3 but 66.6666 will become 66.7).

   output << setprecision(3) << percentage << "%" ;

Simplifying code by adding more classes

You can simplify code by partitioning the functionality between different classes. In the above example, the complexity of reading and writing the QuizResult was handled by a class, meaning that wherever you had to read or write the result, it was a simple statement.

Similarly we can simplify the summing of scores for a Quiz by creating a Quiz class that takes the name of the quiz as an argument to the constructor. Then you can define an add method that takes a QuizResult object. Inside that method you can add the score if the quiz name matches or ignore the score if it does not match. You can also count how many scores have been added so that when you ask the Quiz for the average, it is easy to calculate. Similarly you could also track the Min and the Max scores as they are being added.

Similarly you could define a StudentGrade class to encapsulate that functionality.

© 2016 Pete McBreen Why bother with a cunning plan when a simple one will do? — Terry Pratchett, Thud!

CPP Jan 18th Input and Output for Text files

Posted by Pete Mon, 18 Jan 2016 00:37:00 GMT

We have already covered iostream and cout and cin, that deal with standard output and standard input from the console. There is also cerr that deals with standard error - which is similar to cout but is written to a different file handle so that it is a separate stream of characters. These three correspond to STDOUT, STDIN and STDERR. On the DOS command line you can use

 > program.exe argument < input.txt  > output.txt  2> error.txt

Remember for this to work program.exe MUST either be in the current directory or on the PATH environment variable.

Writing sequential files

Writing files requires that you use the fstream header file and the you can create an output stream as follows

ofstream output( argv[1], ios::out);

or if you want to append to the end of the file use

ofstream output( argv[1], ios::ate | ios::app );

in both cases output is the name of the output file stream object.

Once you have created the output file stream object, you need to check that you can successfully use the file

if ( !output ) {
    cerr << "error opening file " << errno << " " << strerror(errno) << endl;
    exit( EXIT_FAILURE );
}

You need to include cstdlib for exit and cstring for strerror. In combination this if statement causes the program to exit if the file is not writable for any reason e.g. you forgot to specify the command line argument or the file is not writable. Once you are sure the file is writable, it can be used just as you would use cout.

output << "Test " << 6.0 << endl;

When you are finished writing to the file it can be closed using

output.close()

Please note that the output streams are buffered, so you will not see the output immediately in the file unless you use endl or flush.

Sequential reading of files

To read files use istream objects.

ifstream input( argv[1], ios::in );

The resulting input object needs to be tested in the same way as the output file object to make sure that it is readable. At that point it can be used the same way as cin.

string buffer;
double d;
while( input >> buffer >> d) {
    cout << buffer << ":" << d << endl;
}

The only challenge when using this type of stream input and output is that you MUST make sure that you are reading the variables from the file in the same sequence that they were written.

If you want to re-read a file once you get to the end you can either close and reopen it, or alternatively just seek to the beginning of the file (or any other position). Unfortunately the seek call fails silently if you are at the end of the file, so you first need to clear the error state on the file, at which the seek call will succeed.

input.clear();
input.seekg(0, input.beg); // seekp is for PUT to file, seekg is for GET

Tasks for the week

  1. Create an input program that takes a Name, Test, Total and Score from user input where name is the person taking the test, test is an identifier for the test, total is an integer of the maximum points available on the test and score is the individual’s score.
    1. Make sure you APPEND to the existing file so that you do not erase the previous contents of the file
    2. Allow the score to have decimal points in it, score can be an integer
  2. Create another program to handle the processing of the values that are saved by the first program, Use command line arguments to get the program to switch between printing the following different outputs. Please note that you may need to re-read the input file to get the desired output.
    1. Calculate and print the percentage mark for each score in the input file
    2. Calculate the overall percentage for each named person in the input file
    3. Calculate the average mark for each test and then indicate in the output what percentage the person got compared to the average mark for that test

Remember to ask questions early and often for any part of this task that may be unclear or underspecified.

CPP Jan 11th Object Lifetimes

Posted by Pete Sun, 10 Jan 2016 16:23:00 GMT

Last week we covered Destructors, sample Tracker.h and Tracker.cpp for those who were missing last week.You will need to put those 2 files into a project and then use the Tracker object to observer the lifetime of the object.

Tracker a("a");
int main() {
      cout << "in main()" << endl;
}

will print

  • Created: a
  • in main()
  • Destroyed: a

Lifetime of Objects

Normally objects are created when they are first seen in the path of code execution and deleted when they fall out of scope and are no longer accessible. So when you define a Tracker object outside of main, it is instantiated before main runs and is destroyed after main has finished. Similarly if you declare an object inside a function, it is instantiated immediately after the function starts to run and is destroyed on function exit.

 void test1() {
     static Tracker t1("test one");
     cout << "in test1" << endl;
 }

static objects

If you declare an object to be static, that means the object will not be deleted until the program is terminating. Unlike defining the object outside of the main function however, static does not affect when the object is created, only when it is destroyed.

 void test2() {
    static Tracker st("static");
    cout << "in test2" << endl;
 }

Question: What will this print when called from main?

static members of classes

When you declare an attribute of a class to be static, the attribute effectively becomes an attribute of the Class, not an attribute of *Objects of that Class.

in Tracker.h

    private:
         string name; // existing
         static int count; // new static count (will be used to count number of objects created

in Tracker.cpp

 int Tracker::count = 0; // have to initialize this outside of constructor 

 Tracker::Tracker(string name) {
       count++
       this->name = name + " " + char(count + 48); // append count to the name (only works up to 9)
       cout<< "Created: " << this->name << endl;
 }

in main.cpp, inside main()

 Tracker a("a");
 Tracker b = a;

Questions:

  1. with the code above Tracker b = a;, is the assignment operator used or the copy constructor?
  2. At the end of your program is the count of objects back to 0?

Dynamic creation of Objects

C++ allows you do dynamically create Objects, but when you do you become responsible for controlling the lifetime of the object.

in main.cpp inside main()

 Tracker *pointedTo ;
 pointedTo = new Tracker("new one");
 delete pointedTo;

Questions:

  1. What happens if you forget to call delete?
  2. What happens if you call delete twice on the same pointer?

CPP Jan 4th - Destructors

Posted by Pete Sat, 02 Jan 2016 21:57:00 GMT

Session starts with a short test to see what has been forgotten since last term.

Please remember to bring a printed copy of your notes.

Destructors

Destructors are the opposite of Constructors, the Destructor runs when the object is destroyed by the system, either when it goes out of scope or is explicitly deleted from within your code. An example that did nothing was in the Counter class we used earlier.

class Counter
{
    public:
        Counter(unsigned short int initial = 0, unsigned short int limit = 10);
        ~Counter();
       // rest of counter.h omitted

Counter::~Counter()
{
    //destructor -- not needed in this case
}

Using Constructors and Destructors to track object lifetimes

Create a simple class Tracker that takes a single string in the constructor, it should save the string in the object and then print Hello from string, the destructor should print Goodbye from string.

Things to check out

  1. What happens if you create a Tracker object outside of main?
  2. What happens if you create a Tracker object inside main?
  3. What happens if you create a Tracker object in a function that is called by main?
  4. What happens if you pass a Tracker object by value to a function? (Copy constructor will be needed for this)
  5. What happens if you create an array of Tracker objects?

© 2016 Pete McBreen. ‘It would seem that you have no useful skill or talent whatsoever,’ he said. ‘Have you thought of going into teaching?’ — Terry Pratchett (Mort)

CPP 6

Posted by Pete Sat, 02 Jan 2016 21:56:00 GMT

C++ week 6

More on Classes in C++

Strings in C++ are instance of the string class and as such have a lot of specific behaviours that are implemented by the class.

Using the string Class

There are more string constructors than we needed for our Odometer class, but that is because there are many ways in which it can be useful to create a string.

  • default constructor to build an empty string
  • copy constructor
  • substring constructor - build string from part of another string
  • from C string char * s = "abc"; - either up to null terminator or n chars
  • fill constructor
  • range constructor (similar to substring)
  • 2011 C++ from initializer_list {'a', 'b', 'c'}
  • 2011 C++ move constructor

Coding Task

Use the string class to break up a sentence into separate strings for each word. Punctuation and white space should be considered as word delimiters. Use the Terry Pratchett quote at the bottom of this page as the sentence that you need to break up into individual words.

  1. What questions do you need to ask before starting this task?
  2. Hint: What is underspecified?
  3. Which string methods will you need to use?
  4. Do you need to ask any questions about those methods?
  5. A reminder of the control structures in C++ in case you have forgotten any of them.

© 2015 Pete McBreen. Some humans would do anything to see if it was possible to do it. If you put a large switch in some cave somewhere, with a sign on it saying 'End-of-the-World Switch. PLEASE DO NOT TOUCH', the paint wouldn't even have time to dry. — Terry Pratchett

CPP 5

Posted by Pete Sat, 02 Jan 2016 21:53:00 GMT

C++ week 5

Classes in C++

Strings in C++ are instance of the string class and as such have a lot of specific behaviours that are implemented by the class. To get an understanding of classes in C++ it is easier to start with a simpler example — a Counter class that cam be incremented up to a limit and then resets back to zero.

Counter.h and Counter.cpp are a simple implementation for this concept. The Counter class has a constructor with default values (so it also acts as the default constructor), a destructor that does nothing special, a copy constructor and an assignment operator. It also has a val() method that returns the current value of the Counter, and a next() method that increments the counter.

Validating the Counter class

Download the two files into an new project and use the counter inside main() to validate that it works correctly, specifically

  1. Create a counter without passing any values - should start at 0 and rollover at 10.
  2. Create a counter with initial value of 9.
  3. Create a counter with an initial value of 1 and specify that it rolls over at 6.
  4. Confirm that the counters roll over at the correct value.
  5. Confirm that you cannot set the initial value out of the specified range.
  6. Confirm that the value and limit are unsigned numbers.
  7. Confirm that the assignment operator works correctly.
  8. Confirm that the copy constructor works correctly.

Hint. You might need to ask questions to do some or all of these. Work on these in sequence and do not progress to the next one until you have confirmed the results of each one. You are expected to help each other out...

Using the Counter Class

Use the Counter class as a component in an Odometer class, the constructor for the Odometer class must take the number of digits (Counters) and base (limit). So Odometer(3,10) would specify an odometer that runs from "000" to "999", Odometer(5,2) would specify an odometer that runs from "00000" to "11111".

The Odometer class should have a value method that returns a string containing the digits for the current Odometer value. To convert the result returned by the Counter::val() method into a character you can use the following

	Counter c;
	char digit = char('0' + c.val());     	

Test your Odometer to ensure it correctly displays each value in sequence and rolls over back to zeros correctly when the limit is reached.

© 2015 Pete McBreen. I'll be more enthusiastic about encouraging thinking outside the box when there's evidence of any thinking going on inside it. — Terry Pratchett

CPP 4

Posted by Pete Sat, 02 Jan 2016 21:51:00 GMT

C++ week 4

More functions - pass by pointer

Pointers hold the address of a variable, so can be used to point to other variables.

Pass by pointer is different than by value and by reference, because you can have interesting failures when passing by pointer.

	int size(int* px) {  // pass by pointer
		return sizeof(px);
	}     	

Pointers give C++ a lot of power, but they also make it possible to completely crash your program by having a pointer point to an invalid location.

	int* px; // declare pointer to integer, but leave it uninitialized
	cout << "size(px)" << size(px) << endl; 	// what will this print????
	cout << "px = " << px << endl; // print out value of px
	cout << "*px = " << *px << endl; // print out what px points to 

Last line of that code will cause program to crash in DEBUG mode, might work OK if run in Release mode. Reason is that in DEBUG mode variables are initialized to 0, including pointers. When you try to deference *px a null pointer (value 0) the program will crash. In release mode variable are not initialized unless done explicitly by your code, so px will contain a random value that may, or may not, be valid.

To initialize a pointer you need to assign the address of a variable of the SAME type to the pointer.

	int x = 6;
	int *px = &x;
	cout << "*px = " << *px << endl; // prints out 6
  1. Note. Always prefix your pointer variables with p to help your self remember it is a pointer.
  2. Remember that it is always possible to have pointers to pointers...
  3. Arrays in C++ are effectively just pointers to the start of a contiguous set of values.

Reading the command line arguments

Command line arguments are available in C++ by using the extended version of the main() function

	int main(int argc, char **argv, char **envp)
	{
		cout << "argc = " << argc << endl; // count of arguments always >= 1
		return 0;
	}
  • argv[0] is the program that is running (remember C++ counts from Zero).
  • envp is a pointer to an array of environment variables, last pointer in array is null pointer.
  • All arguments and environment variables are just char arrays, you have to convert if you want anything else.

To Do

  1. Write a short program that prints out all the command line arguments and environment variables.
  2. Rework your sort program to use pass by pointer not pass by reference.

© 2015 Pete McBreen. I'll be more enthusiastic about encouraging thinking outside the box when there's evidence of any thinking going on inside it. — Terry Pratchett

CPP 3

Posted by Pete Sat, 02 Jan 2016 21:50:00 GMT

C++ week 3

More functions - pass by reference

A reference is a way of having an alternate name for the same variable, and are mainly used as function parameters to allow a function to modify the passed in parameter values.

Pass by Value and Pass by Reference are competing options that the compiler has trouble telling apart, so as a programmer you should only use one or the other, not a combination. If you have

	int size(int x);      	// pass by value
	int size(int& x); 	// pass by reference

The compiler will complain if you try to make a call like

	int x = 12;
	cout << "size(x)" << size(x) << endl; 	// call of overloaded 'size(int&)' is ambiguous
	cout << "size(12)" << size(12) << endl; // no problem since 12 is not a variable
	

Main guideline for the use of pass by value vs pass by reference it to use pass by value for simple, small variables and const reference for everything else. If you need to be able to modify the variable from within the function then you have to use reference.

Note. C++ does not give any warning if you modify a pass by value parameter in a function, but the change does not go back to the caller.

	// swapper.h 
	void transpose(int a, int b);
	void chg(int a, int& b);
	
	// main.cpp
	#include <iostream>
	using namespace std;
	#include "swapper.h"

	int main()
	{
	    int x = 10;
	    int y = 20;
		cout << "before transpose " << x << "," << y << endl;
		transpose(x,y);
		cout << "after transpose " << x << "," << y << endl;
		chg(x,y);
		cout << "after chg " << x << "," << y << endl;
	    return 0;
	}

	// swapper.cpp
	#include "swapper.h"
	void transpose(int a, int b) {
	    int temp = a;
	    a = b;
	    b = temp;
	}

	void chg(int a, int& b) {
	    a = 100;
	    b = 200;
	}	
		
  1. What is the deliberate error in the above code?
  2. What would happen if you assigned a different value to f inside the size function?

To Do

use the transpose function to implement a sort of numbers in an array int arr[10] = {9,8,7,6,5,4,3,2,1,0}. The main program should have the structure...

  • Any necessary includes
  • Declaration of needed variables
  • Print the array before the sort
  • Sort the array - smallest to largest
  • Print the array after the sort

Hint. Use a for loop to handle the looping over the array.

	for(int i = 0; i < limit ; i++) {
		// your code goes here arr[i] is how you access content of array in position i
	}

© 2015 Pete McBreen. Questions do not have to make sense, but answers do. — Terry Pratchett

CPP 2

Posted by Pete Sat, 02 Jan 2016 21:48:00 GMT

C++ week 2

Looking into possibility of making this a for credit course, most likely option is the AP course frameworks Computer Science A (uses Java but we can tailor) or Computer Science Principles - framework only, coming in 2016.

C++ Core Guidelines are under development by Bjarne Stroustrup Cpp Core Guidelines, there are also some associated videos ISO Cpp org videos

Understanding functions

Looking at functions this week.

Key thing to note is that C++ differentiates between Function Definitions and Functions Implementation. IN order to be able to use a function, the definition of the function must be seen before the function can be used without any compiler error.

Main parameter passing method is pass by value.

	#include <iostream>

	using namespace std;
	
	int size(char x);
	int size(int x);
	int size(double x);

	int main()
	{
		float f = 12.0;
	    cout << "Hello world!" << endl;
		cout << "size('s')" << size('s') << endl;
		cout << "size(12)" << size(12) << endl;
		cout << "size(12.0)" << size(12.0) << endl;
		cout << "sizeof(f)" << sizeof(f) << endl;
		cout << "size(f)" << size(f) << endl;
	    return 0;
	}


	int size(char x) { return sizeof(x); }
	int size(int x)  { return sizeof(x); }
	int size(double x)  { return sizeof(x); }	
		
  1. Why is sizeof(f) not the same as size(f)?
  2. What would happen if you assigned a different value to f inside the size function?
  3. Why have if and unless?

Normally related functions are declared in a header file (extension .h) and the related implementations are declared in a .cpp file. so we could have a sizes.h and sizes.cpp files with the relevant code in them - and at that point the code can be deleted from main.cpp. But to use the code you need to have a #include line at the top of main.cpp.

	#include "sizes.h"

Without the #include you will get an error message when compiling main.cpp "'size' was not declared in this scope"

  1. Why separate the implementation from the implementation into two separate files?
  2. Why separate the functions into separate files?

To Do

Implement Euler Problem 6(Sum Square Difference) using functions for each distinct part of the calculation. The main program should have the structure...

  • Any necessary includes
  • Declaration of needed variables
  • Read inputs from user
  • Process the input, doing necessary calculations
  • Print the output nicely formatted with the inputs

© 2015 Pete McBreen. Questions do not have to make sense, but answers do. — Terry Pratchett