Purpose of Pointers to Functions

In a previous post, I mentioned something about pointers to functions. Why would anyone need pointers to functions? In C/C++, they’re called function pointers. They also exist in other languages but the implementations are slightly different. In C#, they’re called delegates. Many OOP languages have interfaces with overrideable methods. In others, there’s the concept of lambda (anonymous) functions.

One common application of function pointers are Event Handlers. Event handlers are quite straight forward. Assign a function as an event handler and when that event is triggered, execute the corresponding function. See W3Schools for more info on JavaScript Event Handling. These functions are referred to as Callbacks.

In C#, they allow adding/subtracting of delegates. Adding a delegate simply adds that method to the list of methods that will be executed, one by one in the order they were added, when the event is triggered. Subtracting removes that method from the list. This allows having multiple listeners to a single event. See MSDN for more information.

What’s the point of pointers to functions?

Sorting

Let’s go back to C. Let’s say you wanted to make a generic quicksort for arrays of basic data types.

This would be ideal if ever you wanted to sort your lists in increasing order every time. But of course, that won’t always be the case. The line that determines the order of the elements is line #16.

If I wanted to make a quicksort that would yield a list in decreasing order, I just need to replace that “less than” to a “greater than”. That means, I need to copy the whole code except for that line. This defeats one of the purposes of functions which is modularity.

Callbacks to the Rescue

This problem can be easily solved by using callbacks. We pass an additional parameter, a pointer to a function taking 2 values and returning non-zero (true) if the former should come before the latter. Our quicksort now looks like this:

This way, we could have multiple comparators such as the following:

And quicksort could be invoked like this:

Download the Source Codes.

These callbacks allow for Forward Compatibility. Meaning, I have my algorithms now and there may be new data types in the future. I implement a generic algorithm and you supply the comparator so my algorithm can work with data types that didn’t exist when the algorithm was created. To be even more generic, instead of having a DATA_TYPE define (or typedef), we could do everything in terms of pointers. See QuickSort in WikiBooks for a more generic solution.

Challenge

Create a comparator such that sorting a list would result in the following criteria:

  • all odd numbers come before even numbers
  • odd numbers are sorted in increasing order
  • even numbers are sorted in decreasing order

Here’s an example of a sorted list: 1 3 3 5 7 9 9 11 20 14 12 10 10 2 0

Have fun!

C-style “OOP”

Before C++, C somewhat had an implementation of classes and methods by simply using structs. Of course, this is not truly OOP since there would be no inheritance or encapsulation. But it implements abstraction and polymorphism naturally.

Let’s say we want to have a Rectangle class with 2 methods: (1) compute for the area (2) compare the area with another Rectangle. The header file would contain the following:

The Rect_handle typedef there is just for convenience. It’s simply a pointer to a CRectangle structure.

The Memory Allocation is similar to the “new” keyword with a constructor, hence the function name. The Memory Deallocation is similar to memory allocation except for the parameter which is a pointer to the structure you wish to delete.

All the properties and methods are public. Unfortunately, encapsulation is not possible.

All the methods are pointers to functions where the first parameter is always a pointer to the structure under consideration. But if all the methods are just pointers, where are the implementations? Let’s check out the implementation file.

As you can see, this may be an implementation file but there are 2 function declarations there. By having functions declared inside an implementation file, we restrict those functions to be used only in this file. Therefore, those functions are not visible outside. Instead, we attach these functions to the structure so they can be used beyond the scope of this file.

You can see in the new_CRectangle() function that a memory allocation and initialization of properties and methods are executed. This is how the new keyword in many OOP languages work. It allocates memory then calls the constructor.

In the delete_CRectangle() function, it first releases any memory borrowed before deallocating the structure itself. This is how the delete keyword in many OOP languages work. It calls the destructor then it deallocates the memory.

Now, how do we use all this? The main file looks something like the following.

The usage is quite similar to what is commonly done in C++, with the exception of the first parameter of every method is a pointer to the object itself. One might ask, how does C++ actually implement these methods without the need to pass the a pointer to itself?

In C++, methods are still defined similarly as above but are not attached to the structure. Instead, whenever you invoke methods, the corresponding function is called with the first parameter being the invoking instance.

(Note: name mangling is not shown)

OOP In C
OOP In C
OOP_in_C.zip
2.6 KiB
30 Downloads
Details