Section Notes 2023-02-28

Table of Contents

1. Topics

1.1. Adaptability to Change

One of the ways to make code more adaptable is to use pointers to functions to generalize or abstract some behavior.

// Example of a function type, which can be used below to declare pointers
// to functions.
//
typedef int AccumFunc(int running_total, int item);

// A function taking a pointer to the above function type.
//
int loopThroughArray(const int *array,
		     size_t     count,
		     AccumFunc *func)
{
    int accum = 0;
    for (const int *ap = array; ap < array + count; ++ap) {
	  // Calling a pointer to function is just like calling a function
	  // in modern C. In old C you would need to dereference it first.
	accum = func(accum, *ap);
    }
    return accum;
}

// You can declare a function using the function type.
//
AccumFunc sumFunc;

// However, because parameter names are treated as comments in a declaration,
// to define the function you must do so in the normal way.
//
int sumFunc(int run, int item)
{
    return run + item;
}

// Another function type
//
typedef int TransformFunc(int value);

// It can be useful to bundle a set of these functions together into a
// structure in order to pass them, especially when several functions need
// to pass these function pointers along.
//
typedef struct funcs {
    AccumFunc     *accum;
    TransformFunc *xform;
} Funcs;

int foo()
{
      // You can use the C99 designated initializer syntax to initialize
      // such a structure (or any structure). Any field which is not so
      // designated gets 0-initialized by default. You don't have to give
      // these initializers in the same order but it is usually less confusing
      // to keep it.
      //
      // Note that when dealing with an initializer list you're allowed to
      // have a comma after the last entry; this makes it easier to move
      // lines around and less complicated to edit the list later.
      //
    Funcs myfuncs = {
	.accum = sumFunc,
	.xform = negateFunc,
    };

    Funcs *myfuncs;

      // In order to use the designated initializer syntax outside of a
      // definition, you need to cast the initializer to the correct struct
      // type.
      //
      // (Note the extra leading comma before "*myfuncp" is a meta-quoting
      // character for Org mode, and not part of the C code.)
      //
    *myfuncp = (Funcs) {
	.accum = productFunc,
	.xform = add1To,
    };

      // Designated initializers can also be used for arrays but this is
      // not very common.
      //
    int array[10] = { [2] = 4, [0] = 2 };

      // I often use the extra comma trick for enums as well but there is
      // a common trick of having a fake enumerator as the last one which
      // is used to store the number of items in the enumeration.
      //
    typedef enum colors {
	RED,
	GREEN,
	BLUE,
	BLACK,
	NUM_COLORS
    } Colors;

      // This can be used to declare a parallel array of names for the
      // enumerators which can be used to print them in messages for example.
      // Strictly speaking the size is not needed but the compiler will
      // warn if the size is different from the number of initializers.
      //
    const char *color_names[NUM_COLORS] = {
	"red",
	"green",
	"blue",
	"black",
    };
}

Author: Alexis Layton

Created: 2023-03-01 Wed 19:55

Validate