llwfp

Documentation
Login

Documentation

/*
  Compile:
  make readme-ex03-using-callbacks
*/

#define _BSD_SOURCE /* snprintf() */

#include <string.h> /* memcpy(), strerror() */
#include <stdio.h>  /* printf() */
#include <stdlib.h> /* calloc() */
#include <errno.h>  /* errno */

#define MAXSIZE 8192

/* Generic node struct, using a void pointer for data */
typedef struct node_t
{
  struct node_t *next;
  void *data; /* GENERIC */
} node_t;

/* Definitions for the struct example */
/* HTML header levels */
typedef enum header_level
{
  h1 = 1,
  h2,
  h3,
  h4,
  h5,
  h6
} header_level;
/* HTML header typedef */
typedef struct html_header
{
    header_level level;
    char *title;
} html_header;

/* Definition for the double array example */
typedef struct double_arr_t
{
  double *arr_data;
  size_t arr_len;
} double_arr_t;

/* Typedef:s for function pointers */
typedef void *(*add_func_t)(void *dest, const void *src);
typedef void (*print_func_t)(void *data);
typedef void (*cleanup_func_t)(void *data);


/* -------------------------------------------------------------------------------- */
/* Data functions                                                                   */
/* -------------------------------------------------------------------------------- */

/* ------------- */
/* Add functions */
/* ------------- */
/* Add int */
void *add_int(void *dest, const void *src)
{
  size_t alloc_size = sizeof(int);
  if ((dest = calloc(1, alloc_size)) == NULL)
  {
    fprintf(stderr, "ERROR: add_int(): calloc() failed: %s\n", strerror(errno));
    return NULL;
  }
  return memcpy(dest, src, alloc_size);
}
/* Add string */
void *add_string(void *dest, const void *src)
{
  char buf[MAXSIZE] = {0};
  size_t buf_size = 0;
  int i = *(int *)src;
  snprintf(buf, MAXSIZE, "%d", i);
  buf_size = sizeof(char) * (strlen(buf) + 1);
  if ((dest = calloc(1, buf_size)) == NULL)
  {
    fprintf(stderr, "ERROR: add_string(): calloc() failed: %s\n", strerror(errno));
    return NULL;
  }
  return memcpy(dest, buf, buf_size);
}
/* Add struct */
void *add_struct(void *dest, const void *src)
{
  html_header *header = NULL;
  char buf[MAXSIZE] = {0};
  size_t buf_size = 0;
  int i = *(int *)src;
  header_level level = h1 + i % h6;
  char *title = NULL;
  snprintf(buf, MAXSIZE, "This is title %d", i);
  buf_size = sizeof(char) * (strlen(buf) + 1);
  if ((title = calloc(1, buf_size)) == NULL)
  {
    fprintf(stderr, "ERROR: add_struct(): calloc() failed for header title string: %s\n", strerror(errno));
    return NULL;
  }
  memcpy(title, buf, buf_size);
  if ((header = calloc(1, sizeof(*header))) == NULL)
  {
    fprintf(stderr, "ERROR: add_struct(): calloc() failed for header struct: %s\n", strerror(errno));
    return NULL;
  }
  header->level = level;
  header->title = title;
  dest = header;
  return dest;
}
/* Add array of double */
void *add_double_arr(void *dest, const void *src)
{
  double_arr_t *arr = NULL;
  double arr_data[] = {0, 97.0, 33.0, 31.0, 96.0, 30.0, 36.0, 92.0};
  size_t arr_len = sizeof(arr_data)/sizeof(arr_data[0]);
  int i = *(int *)src;
  arr_data[0] = (double)i;
  arr = calloc(1, sizeof(*arr));
  arr->arr_data = calloc(1, sizeof(arr_data));
  memcpy(arr->arr_data, arr_data, sizeof(arr_data));
  arr->arr_len = arr_len;
  dest = arr;
  return dest;
}

/* --------------- */
/* Print functions */
/* --------------- */
/* Print int */
void print_int(void *data) {printf("data=%d\n", *(int *)data);}
/* Print string */
void print_string(void *data) {printf("data='%s'\n", (char *)data);}
/* Print struct */
void print_struct(void *data)
{
  printf("data->level=%d, \t", ((html_header *)data)->level);
  printf("data->title='%s'\n", ((html_header *)data)->title);
}
/* Print array of double */
void print_double_arr(void *data)
{
  int i = 0;
  printf("data={");
  for (i = 0; i < (int)((double_arr_t *)data)->arr_len; ++i)
  {
    if (i) printf(", ");
    printf("%.1f", (double)((double_arr_t *)data)->arr_data[i]);
  }
  printf("}\n");
}

/* ---------------------- */
/* Cleanup data functions */
/* ---------------------- */
/* No additional cleanup is needed for simple types like int and char * */
/* void cleanup_int(void *data); */
/* void cleanup_string(void *data); */
void cleanup_struct(void *data)
{
  html_header *header = (html_header *)data;
  /* Free data, assume calloc() was called both for node data and struct member */
  if ((header) && (header->title)) free(header->title);
}
void cleanup_double_arr(void *data)
{
  double_arr_t *arr = (double_arr_t *)data;
  /* Free data, assume calloc() was called both for node data and the array struct member */
  if ((arr) && (arr->arr_data)) free(arr->arr_data);
}

/* -------------------------------------------------------------------------------- */
/* Node functions                                                                   */
/* -------------------------------------------------------------------------------- */
/* Add node with data, return error code - generic */
int node_add(node_t **toc, void *data, add_func_t add_func)
{
  node_t *new_node = calloc(1, sizeof(node_t));
  if (!new_node)
  {
    return -1;
  }

  new_node->next = NULL;
  if (add_func) new_node->data = add_func(new_node->data, data);

  if (!*toc)
  {
     *toc = new_node;
  }
  else
  {
    (*toc)->next = new_node;
    *toc = (*toc)->next;
  }

  return 0;
}

/* Get a node by index */
node_t *node_get_by_index(node_t *head, int node_index)
{
  node_t *node = head;
  int i = 0;
  for (i = 0; i < node_index; ++i)
  {
    node = node->next;
  }
  return node;
}

/* Print node data - generic */
typedef void (*data_print_t)(void *data);
void node_print(node_t *node, data_print_t print_data)
{
  print_data(node->data);
}

/* Delete all nodes - generic */
void node_cleanup(node_t **head, cleanup_func_t cleanup_func)
{
  node_t *current = *head, *next = NULL;
  while (current)
  {
    /* The 'cleanup' call is only needed for the 'node_struct' and 'node_double_arr' lists */
    if (cleanup_func) cleanup_func(current->data);
    if (current->data) free(current->data);
    next = current->next;
    free(current);
    current = next;
  }
   /* Set variable to NULL *before* free(). */
   /* Valgrind doesn't like it the other way around! */
  *head = NULL;
  free(*head);
}


/* -------------------------------------------------------------------------------- */
/* MAIN                                                                             */
/* -------------------------------------------------------------------------------- */
int
main(int argc, char *argv[])
{
  node_t *node_int        = NULL;
  node_t *node_string     = NULL;
  node_t *node_struct     = NULL;
  node_t *node_double_arr = NULL;

  /* Head references for all lists */
  node_t *head_int         = NULL;
  node_t *head_string      = NULL;
  node_t *head_struct      = NULL;
  node_t *head_double_arr  = NULL;

  node_t *node = NULL;

  int i = 0;

  printf("\nREADME EX03\n");
  printf("----------------------------------------\n");

  printf("\nEX03, int list:\n");
  printf("---------------------------------------------------------------------------------------\n");
  for (i = 0; i < 100; ++i)
  {
    /* For now, ignore error handling */
    node_add(&node_int, &i, add_int);
    if (i == 0) head_int = node_int;
  }
  /* Print node 42 of 100 - should print 42 */
  node = node_get_by_index(head_int, 42);
  node_print(node, print_int);

  printf("\nEX03, char * list:\n");
  printf("---------------------------------------------------------------------------------------\n");
  for (i = 0; i < 100; ++i)
  {
    /* For now, ignore error handling */
    node_add(&node_string, &i, add_string);
    if (i == 0) head_string = node_string;
  }
  /* Print node 42 of 100 - should print '42' */
  node = node_get_by_index(head_string, 42);
  node_print(node, print_string);

  printf("\nEX03, struct list:\n");
  printf("---------------------------------------------------------------------------------------\n");
  for (i = 0; i < 100; ++i)
  {
    /* For now, ignore error handling */
    node_add(&node_struct, &i, add_struct);
    if (i == 0) head_struct = node_struct;
  }
  /* Print node 42 of 100 - should print 'This is title 42' */
  node = node_get_by_index(head_struct, 42);
  node_print(node, print_struct);

  printf("\nEX03, double[] list:\n");
  printf("---------------------------------------------------------------------------------------\n");
  for (i = 0; i < 100; ++i)
  {
    /* For now, ignore error handling */
    node_add(&node_double_arr, &i, add_double_arr);
    if (i ==0) head_double_arr = node_double_arr;
  }
  /* Print node 42 - should print "node42->data= {42.0, 97.0, 33.0, 31.0, 96.0, 30.0, 36.0, 92.0}" */
  node = node_get_by_index(head_double_arr, 42);
  node_print(node, print_double_arr);


  /* Cleanup all lists */
  /* No additional cleanup is needed for simple types like int and char *, so */
  /* the cleanup function pointer is set to NULL in these cases. */
  node_cleanup(&head_int, NULL);
  node_cleanup(&head_string, NULL);
  node_cleanup(&head_struct, cleanup_struct);
  node_cleanup(&head_double_arr, cleanup_double_arr);

  printf("\nTIP: MEMLEAK CHECK:\n");
  printf("valgrind -v --track-origins=yes --leak-check=full --show-leak-kinds=all %s\n", argv[0]);
  (void)argc;

  printf("----------------------------------------\n");

  return 0;
}