Lab 14: Forcing C to be Object-Oriented

...and then [Men] would or could be made to mate with Orcs, producing new breeds, often larger and more cunning. There is no doubt that long afterwards, in the Third Age, Saruman rediscovered this, or learned of it in lore, and in his lust for mastery committed this, his wickedest deed: the interbreeding of Orcs and Men, producing both Men-orcs large and cunning, and Orc-men treacherous and vile.
--- J.R.R. Tolkien, Morgoth's Ring

How can we tolerate these indignities [being forced to program in Fortran or C equivalents because more creative programming languages are suppressed]? The frustratingly simple answer is the universal nature of programming languages---the fact that one can program in any one of them what can be programmed in any other. We are able to clever our way out of any programming box. The more difficult the task, the more pride we can take in the accomplishment.
--- Richard Gabriel and Ron Goldman, "Mob Software: The Erotic Life of Code"

The goal of this lab is to practice using function pointers to emulate in C the object oriented features of encapsulating (data and functionality together), subtyping, and polymorphism.

Refer to the pre-lab reading as necessary for the problem background.

1. Set up

Copy the following files from course public directory.

cp -r ~tvandrun/Public/cs245/lab14/* .

As described in the pre-lab, in this lab you will make a collection of structs that will model various kinds of mathematical functions and will carry with them function pointers that simulate methods for doing various operations on them--- specifically, evaluation at an x value, computing a derivative, and computing a definite integral. The Java solution will help you remember the formulas and such for that. (It wouldn't be too hard for you to figure this all out from scratch, as long as you've had calculus and remember it, but that would take too long. I'd rather your attention be focused on the new ideas of function pointers etc.)

2. function.h

Open function.h. This is equivalent to Function.java in that its purpose is to define a type according to a set of operations, like an interface. Open functiondriver.c and notice how this type is used.

As we know, C's facility for making new types is the struct. Structs cannot have behavioral members (ie, methods), but we can emulate methods by letting it have function pointers, as we saw with the "book" example in class.

In the present example of implementing different kinds of mathematical function objects, we have an extra problem: We don't know what data member function should have, since that will vary among the subtypes. To handle this, we give struct function_t a field data of type void *, which is C's way of saying "pointer to a value of an unknown type" (compare with Java's Object class). We will set data to refer to structs polynomial, step, and exponential.

Two more things about function.h: First, it also has a function pointer called destroy, to refer to a function that deallocates the structure. Second, the #ifndef FUNCTION ... #endif stuff prevents this file from being included more than once.

3. Polynomial

Inspect the type definition for polynomial in polynomial.h, and understand how the implementation of an evaluate function works in polynomial.c. The function is called evaulate_p() ("p" is for "polynomial") to distinguish it from the evaluate() function for the function struct.

For convenience (to avoid lots of casts), you will probably want almost all of the functions you write today to include a line like

  polynomial* data = (polynomial*) this->data;

differentiate_p() and integrate_p() also are done for you. Your task Your task is to finish this file by filling in the functions to make a new polynomial object and destroy an object. Although the destroy_p() function appears first in the file, I recommend you write the newPolynomial() function first, because then you can think about the destroy_p() function in terms of undoing everything you do in newPolynomial().

Deallocate everything you allocate! For every use of malloc or calloc, be able to identify a call to free which will undo the allocation.

Then compile (using the provided makefile) and test using the given driver.

4. Step and exponential

Then look at the types for step and exponential functions in their respective header files. Then finish the functions in their implementation files. Specifically, for step functions...

And for exponential functions...


Thomas VanDrunen
Last modified: Wed Dec 9 10:22:29 CST 2015