Chapter 7. Advanced Functions: Turn your functions up to 11

image with no caption

Basic functions are great, but sometimes you need more.

So far, you’ve focused on the basics, but what if you need even more power and flexibility to achieve what you want? In this chapter, you’ll see how to up your code’s IQ by passing functions as parameters. You’ll find out how to get things sorted with comparator functions. And finally, you’ll discover how to make your code super stretchy with variadic functions.

Looking for Mr. Right...

You’ve used a lot of C functions in the book so far, but the truth is that there are still some ways to make your C functions a lot more powerful. If you know how to use them correctly, C functions can make your code do more things but without writing a lot more code.

To see how this works, let’s look at an example. Imagine you have an array of strings that you want to filter down, displaying some strings and not displaying others:

int NUM_ADS = 7;
char *ADS[] = {
  "William: SBM GSOH likes sports, TV, dining",
  "Matt: SWM NS likes art, movies, theater",
  "Luis: SLM ND likes books, theater, art",
  "Mike: DWM DS likes trucks, sports and bieber",
  "Peter: SAM likes chess, working out and art",
  "Josh: SJM likes sports, movies and theater",
  "Jed: DBM likes theater, books and dining"
};
image with no caption

Let’s write some code that uses string functions to filter this array down.

Pass code to a function

What you need is some way of passing the code for the test to the find() function. If you had some way of wrapping up a piece of code and handing that code to the function, it would be like passing the find() function a testing machine that it could apply to each piece of data.

image with no caption

This means the bulk of the find() function would stay exactly the same. It would still contain the code to check each element in an array and display the same kind of output. But the test it applies against each element in the array would be done by the code that you pass to it.

You need to tell find() the name of a function

Imagine you take our original search condition and rewrite it as a function:

int sports_no_bieber(char *s)
{
  return strstr(s, "sports") && !strstr(s, "bieber");
}
image with no caption

Now, if you had some way of passing the name of the function to find() as a parameter, you’d have a way of injecting the test:

image with no caption

If you could find a way of passing a function name to find(), there would be no limit to the kinds of tests that you could make in the future. As long as you can write a function that will return true or false to a string, you can reuse the same find() function.

find(sports_no_bieber);
find(sports_or_workout);
find(ns_theater);
find(arts_theater_or_dining);

But how do you say that a parameter stores the name of a function? And if you have a function name, how do you use it to call the function?

Every function name is a pointer to the function...

You probably guessed that pointers would come into this somewhere, right? Think about what the name of a function really is. It’s a way of referring to the piece of code. And that’s just what a pointer is: a way of referring to something in memory.

That’s why, in C, function names are also pointer variables. When you create a function called go_to_warp_speed(int speed), you are also creating a pointer variable called go_to_warp_speed that contains the address of the function. So, if you give find() a parameter that has a function pointer type, you should be able to use the parameter to call the function it points to.

image with no caption
image with no caption

Let’s look at the C syntax you’ll need to work with function pointers.

...but there’s no function data type

Usually, it’s pretty easy to declare pointers in C. If you have a data type like int, you just need to add an asterisk to the end of the data type name, and you declare a pointer with int *. Unfortunately, C doesn’t have a function data type, so you can’t declare a function pointer with anything like function *.

image with no caption

Why doesn’t C have a function data type?

C doesn’t have a function data type because there’s not just one type of function. When you create a function, you can vary a lot of things, such as the return type or the list of parameters it takes. That combination of things is what defines the type of the function.

image with no caption

So, for function pointers, you’ll need to use slightly more complex notation...

How to create function pointers

Say you want to create a pointer variable that can store the address of each of the functions on the previous page. You’d have to do it like this:

image with no caption

That looks pretty complex, doesn’t it?

Unfortunately, it has to be, because you need to tell C the return type and the parameter types the function will take. But once you’ve declared a function pointer variable, you can use it like any other variable. You can assign values to it, you can add it to arrays, and you can also pass it to functions...

...which brings us back to your find() code...

Get it sorted with the C Standard Library

Lots of programs need to sort data. And if the data’s something simple like a set of numbers, then sorting is pretty easy. Numbers have their own natural order. But it’s not so easy with other types of data.

Imagine you have a set of people. How would you put them in order? By height? By intelligence? By hotness?

image with no caption

When the people who wrote the C Standard Library wanted to create a sort function, they had a problem:

How could a sort function sort any type of data at all?

Use function pointers to set the order

You probably guessed the solution: the C Standard Library has a sort function that accepts a pointer to a comparator function, which will be used to decide if one piece of data is the same as, less than, or greater than another piece of data.

This is what the qsort() function looks like:

image with no caption

The qsort() function compares pairs of values over and over again, and if they are in the wrong order, the computer will switch them.

And that’s what the comparator function is for. It will tell qsort() which order a pair of elements should be in. It does this by returning three different values:

image with no caption

To see how this works in practice, let’s look at an example.

Relax

Don’t worry if this exercise caused you a few problems.

It involved pointers, function pointers, and even a little math. If you found it tough, take a break, drink a little water, and then try it again in an hour or two.

Do this!

Great, it works.

Now try writing your own example code. The sorting functions can be incredibly useful, but the comparator functions they need can be tricky to write. But the more practice you get, the easier they become.

Automating the Dear John letters

Imagine you’re writing a mail-merge program to send out different types of messages to different people. One way of creating the data for each response is with a struct like this:

image with no caption

The enum gives you the names for each of the three types of response you’ll be sending out, and that response type can be recorded against each response. Then you’ll be able to use your new response data type by calling one of these three functions for each type of response:

void dump(response r)
{
  printf("Dear %s,
", r.name);
  puts("Unfortunately your last date contacted us to");
  puts("say that they will not be seeing you again");
}


void second_chance(response r)
{
  printf("Dear %s,
", r.name);
  puts("Good news: your last date has asked us to");
  puts("arrange another meeting. Please call ASAP.");
}


void marriage(response r)
{
  printf("Dear %s,
", r.name);
  puts("Congratulations! Your last date has contacted");
  puts("us with a proposal of marriage.");
}

So, now that you know what the data looks like, and you have the functions to generate the responses, let’s see how complex the code is to generate a set of responses from an array of data.

Create an array of function pointers

The trick is to create an array of function pointers that match the different response types. Before seeing how that works, let’s look at how to create an array of function pointers. If you had an array variable that could store a whole bunch of function names, you could use it like this:

replies[] = {dump, second_chance, marriage};

But that syntax doesn’t quite work in C. You have to tell the compiler exactly what the functions will look like that you’re going to store in the array: what their return types will be and what parameters they’ll accept. That means you have to use this much more complex syntax:

image with no caption

But how does an array help?

Look at that array. It contains a set of function names that are in exactly the same order as the types in the enum:

enum response_type {
DUMP, SECOND_CHANCE, MARRIAGE};

This is really important, because when C creates an enum, it gives each of the symbols a number starting at 0. So DUMP == 0, SECOND_CHANCE == 1, and MARRIAGE == 2. And that’s really neat, because it means you can get a pointer to one of your sets of functions using a response_type:

image with no caption

Let’s see if you can use the function array to replace your old main() function.

Make your functions streeeeeetchy

Sometimes, you want to write C functions that are really powerful, like your find() function that could search using function pointers. But other times, you just want to write functions that are easy to use. Take the printf() function. The printf() function has one really cool feature that you’ve used: it can take a variable number of arguments:

image with no caption

So how can YOU do that?

And you’ve got just the problem that needs it. Down in the Head First Lounge, they’re finding it a little difficult to keep track of the drink totals. One of the guys has tried to make life easier by creating an enum with the list of cocktails available and a function that returns the prices for each one:

enum drink {
  MUDSLIDE, FUZZY_NAVEL, MONKEY_GLAND, ZOMBIE
};

double price(enum drink d)
{
  switch(d) {
  case MUDSLIDE:
    return 6.79;
  case FUZZY_NAVEL:
    return 5.31;
  case MONKEY_GLAND:
    return 4.82;
  case ZOMBIE:
    return 5.89;
  }
  return 0;
}

And that’s pretty cool, if the Head First Lounge crew just wants the price of a drink. But what they want to do is get the price of a total drinks order:

image with no caption

They want a function called total() that will accept a count of the drinks and then a list of drink names.

Geek Bits

Functions vs. macros

A macro is used to rewrite your code before it’s compiled. The macros you’re using here ( va_start, va_arg, and va_end) might look like functions, but they actually hide secret instructions that tell the preprocessor how to generate lots of extra smart code inside your program, just before compiling it.

Your C Toolbox

You’ve got Chapter 7 under your belt, and now you’ve added advanced functions to your toolbox. For a complete list of tooltips in the book, see Appendix B.

image with no caption
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.138.175.180