Edsger W. Dijkstra wrote an influential paper back in 1988 called On the cruelty of really teaching computing science, which advocated an approach strongly grounded in the study of formal systems.

While I would be the first to admit that I am not fit to carry the late Dr.’s punch cards, I would say that if he really wanted to see cruelty, he might have tried his hand at the undergraduate course I taught this semester: C/C++ Programming in a UNIX Environment. This class was cruelty personified.

How long did it take you to learn C++? Do you think you could squeeze that into a single undergraduate semester, giving it only the 20% of your attention span that is due?

Now throw into the mix the requirement that you have to learn C as well.

And to keep things interesting, all your work has to be done on a UNIX or Linux machine. You’ve used Windows since you were 8, and think that cat is a four-legged pet and grep is some sort of gastrointestinal complaint.

Yep, you are in trouble.

Tricky Assignments

The good news in all this was the I didn’t make any of Andrew Koenig’s egregious errors when teaching the computer programming class. The bad news is that every homework assignment I created looked to my students like two impassable mountain ranges instead of one: an incomprehensible C++ problem to be implemented on an inscrutable O/S, using an IDE that was decidedly not Visual Studio.

Just as an example, for a recent assignment, I had the class warm up with a pure C++ implementation of mergesort, reading a string of words from standard input and writing the sorted list to standard output. Using all the facilities at hand in the C++ standard library meant that the mergesort implementation was a breeze - about the only piece of the algorithm that required much thought was merging the two subcontainers after dividing and conquering.

After the warmup part of the assignment came the meatier portion: I asked the students to implement the mergesort algorithm by passing the subproblems to child processes. This meant they needed to solve a few very common problems encountered when programming on *IX:

  • Using fork() to create child processes
  • Managing the lifetime of parent and child processes
  • Managing unnamed pipes for communication between a parent and child process
  • Serializing and deserializing C++ containers so they can be transmitted through a de-objectifying pipe

Admittedly, this is not a perfect demonstration of a way to parcel a problem out multiple processes. In fact, if done using a straightforward implementation of the problem you can create a beautiful example of a fork bomb, bringing your system to its knees. But I thought it would be a good way to get the hang of working with child processes in a somewhat realistic way. (And this could actually be a good way to distribute a sorting process - if you only forked a limited number times at the top of the merge.)

By fooling around a bit with process names I was even able to do a poor man’s animation of the process using pstree.

A screen capture showing the output of pstree while the a forking program is running. It shows a test-based tree that maps out the head processes and children.
Figure 1 - A pstree animation

When I worked up the assignment, it seemed like a reasonable assignment to tackle over the course of a week. Alas, at the one week deadline, my inbox was empty.

Lessons Learned

This wasn’t the first assignment that turned out to be a semi-disaster. My Scrabble game board manager saw a similar fate, as did my Scrabble word generator assignment.

As part-time non-tenure track faculty, I don’t have a lot of say in curriculum development. But after going through this course, I will definitely be passing along a strong recommendation: if we are going to ask students to learn a new language using new tools with a new O/S, we need to modify the class structure so that at least half of the hours are spent in the lab.

When I worked through these problems in a lab environment, it was easy to provide the gentle nudge to help someone who was stuck trying to get Eclipse or NetBeans to do the expected, instead of whatever perverse path they were on. I could help with the C++ compiler errors, which over a decade after standardization are still a travesty. And I could help coax the debugger into providing usable information when looking at standard C++ objects - which the IDEs will do only grudgingly. In other words, help with the undocumented tips and tricks that experienced C++ programmers take for granted.

At the end of the semester, I do feel pretty good about the class’s mastery of C++ - they soaked up as much of this huge language as was humanly possible. UNIX/Linux expertise didn’t seem to get the same level of commitment - which is unfortunate but understandable. And I have to admit that C programming took a back seat to C++. There are lot of C++ haters out there, and I don’t really need to get them riled up, but once you master C++, there aren’t many times when it makes sense to back down to the much-less capable C language. Unless you’re getting paid by the hour. Or working for Linus.