Programming in C++

Function Pointers

Gerald Senarclens de Grancy

Purpose of Function Pointers

  • Allow treating functions as first class citizen
    • Being passed as an argument
    • Returned from a function
    • Assigned to a variable
  • Callback functions can be implemented
  • More functional programming techniques can be use
  • Functions can be members of C-structs
  • Prerequisite of object-like programming in C

Which Issues do Function Pointers Have?

  • Syntax can be hard to read and understand
  • Function pointers do not make C an object oriented language
    (advanced concepts like encapsulation and inheritance are missing)
  • When object oriented programming (OOP) is desired,
    use a proper OOP language like C++

Syntax

A function pointer is a pointer that points to a function

Type (*functionpointer)(Type arg1, Type arg2, ...);

The function pointer's type contains the arguments and return type of the function pointed to

Example

int sum(int a, int b);
int (*sum_ptr)(int,int);  // declares a function pointer named `sum_ptr`
sum_ptr = ∑  // `&` is not required

The function pointer can then be used by de-referencing it

// `*` is not required
std::cout << "The sum of 2 + 3 is " << (*sum_ptr)(2, 3) << "." << std::endl;
std::cout << "The sum of 2 + 3 is " << (sum_ptr)(2, 3) << "." << std::endl;
std::cout << "The sum of 2 + 3 is " << sum_ptr(2, 3) << "." << std::endl;

Complete Program

#include <iostream>

int sum(int a, int b) { return a + b; }

int main(void) {
  int (*sum_ptr)(int,int);  // declares a function pointer named `sum_ptr`
  sum_ptr = sum;  // same as `sum_ptr = &sum;`
  std::cout << "The sum of 2 + 3 is " << (*sum_ptr)(2, 3) << ".\n";
  std::cout << "The sum of 2 + 3 is " << (sum_ptr)(2, 3) << ".\n";
  std::cout << "The sum of 2 + 3 is " << sum_ptr(2, 3) << ".\n";
  return 0;
}

Visualize execution

Download basics.cpp

Application: Case Insensitive Sorting

qsort(.) in stdlib.h takes a function pointer as argument

void qsort(void* base, size_t nmemb, size_t size,
           int (*compar)(const void*, const void*));

strcasecmp(.) in <strings.h> (resp. <cstring>) performs case insensitive comparison

int strcasecmp(const char* s1, const char* s2);
#include <cstdlib>
#include <cstring>
#include <iostream>

int case_insensitive_compare(const void* a, const void* b) {
  return strcasecmp(*(const char**)a, *(const char**)b);
}

int main(void) {
  const char* names[] = {"Harry", "Anne", "Mary", "chris", "Pat", "eric", "Lily"};
  qsort(names, 7, sizeof(const char*), case_insensitive_compare);
  std::cout << "names (sorted ascending)" << std::endl;
  for (int i = 0; i < 7; ++i) {
    std::cout << names[i] << std::endl;
  }
  return 0;
}

Visualize execution

Application: Callback Functions

Download an internet resource without a callback (link against curl)

#include <iostream>
#include <curl/curl.h>

int main(void) {
  CURL* curl;
  CURLcode res;
  curl = curl_easy_init();
  if (curl) {
    curl_easy_setopt(curl, CURLOPT_URL, "https://study.find-santa.eu/");
    res = curl_easy_perform(curl);
    if (res != CURLE_OK)  // check of errors
      std::cerr << "curl_easy_perform() failed: "
                << curl_easy_strerror(res) << std::endl;
    curl_easy_cleanup(curl);
  }
  return 0;
}
Download get_request.cpp

#include <cstdio>
#include <curl/curl.h>
size_t write_response(void* ptr, size_t size, size_t nmemb, void* data) {
  FILE* f = (FILE*) data;
  puts("\033[32mwriting received data to `result.html`\033[0m");
  return fwrite(ptr, size, nmemb, f);
}
int main(void) {
  CURL* curl;
  CURLcode res;
  FILE* req_body;
  req_body = fopen("result.html", "wb");
  curl = curl_easy_init();
  if(curl) {
    curl_easy_setopt(curl, CURLOPT_URL, "https://study.find-santa.eu/");
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, req_body);
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_response);
    res = curl_easy_perform(curl);
    if(res != CURLE_OK)  // check of errors
      fprintf(stderr, "curl_easy_perform() failed: %s\n",
              curl_easy_strerror(res));
    curl_easy_cleanup(curl);
  }
  fclose(req_body);
  return 0;
}
Download get_with_callback.cpp

Application: Object Like Programming using C-structs

struct IntArray;
struct IntArray {
  size_t reserved;  // available storage
  size_t elements;  // current number of elements
  int* array;

  void (*push_back)(IntArray* self, int elem);
  int (*sum)(const IntArray* self);
  int (*count)(const IntArray* self, int elem);
  IntArray* (*destruct)(IntArray* self);
};
IntArray* construct();
#include <algorithm>
#include <iostream>
#include <stdlib.h>

using std::cout;

namespace ia {
struct IntArray;
struct IntArray {
  size_t reserved;  // available storage
  size_t elements;  // current number of elements
  int* array;

  void (*push_back)(IntArray* self, int elem);
  long int (*sum)(const IntArray* self);
  void (*print)(const IntArray* self);
  IntArray* (*destruct)(IntArray* self);
};

IntArray* destruct(IntArray* self) {
  delete[] self->array;
  self->array = (int*) NULL;
  delete self;
  return (IntArray*) NULL;
}

void push_back(IntArray* self, int elem) {
  if (self->elements == self->reserved) {
    int* old_data = self->array;
    self->reserved *= 2;
    self->array = new int[self->reserved];
    std::copy_n(old_data, self->elements, self->array);
    delete[] old_data;
  }
  self->array[self->elements] = elem;
  self->elements++;
}

long int sum(const IntArray* self) {
  long int sum = 0;
  for (size_t i = 0; i < self->elements; ++i) {
    sum += self->array[i];
  }
  return sum;
}

void print(const IntArray* self) {
  if (!self->elements) {
    cout << "[]\n" ;
  } else {
    cout << "[";
    for (size_t i = 0; i < self->elements - 1; ++i) {
      cout << self->array[i] << ", ";
    }
    cout << self->array[self->elements - 1] << "]\n";
  }
}

IntArray* construct() {
  IntArray* self = new IntArray;
  self->reserved = 100;
  self->elements = 0;
  self->array = new int[self->reserved];
  self->print = print;
  self->sum = sum;
  self->push_back = push_back;
  self->destruct = destruct;
  return self;
}
}  // namespace ia

int main(void) {
  ia::IntArray* a = ia::construct();
  a->push_back(a, 10);
  a->push_back(a, 3);
  a->push_back(a, 7);
  a->push_back(a, 12);
  a->push_back(a, 8);
  a->print(a);
  cout << "The sum of all elements is " << a->sum(a) << ".\n";
  a = a->destruct(a);
  return 0;
}

Visualize execution

Exercise

Implement or rewrite the entire integer array functionality (second task of Dynamic Memory Management (Free Store) exercise using function pointers.

Questions
and feedback...