Non intrusive, non "vtable" polimorphism in C.

button.h


#ifndef BUTTON_H
#define BUTTON_H

struct button
{
  int y;
};

struct button* button_create();
void button_destroy(struct button*);
void button_draw(struct button*);


#endif /*BUTTON_H*/

button.c

#include "button.h"
#include <stdlib.h>
#include <stdio.h>

struct button* button_create()
{
  struct button* p = (struct button*)malloc(sizeof(struct button));
  return p;
}

void button_draw(struct button* btn)
{
  printf("button_draw\n");
}

void button_destroy(struct button* btn)
{
  printf("button_destroy\n");
}

edit.h


#ifndef EDIT_H
#define EDIT_H

struct edit
{
  int y;
};

struct edit* edit_create();
void edit_draw(struct edit*);
void edit_destroy(struct edit*);


#endif /*EDIT_H*/

edit.c


#include "edit.h"
#include <stdlib.h>
#include <stdio.h>


struct edit* edit_create()
{
  return (struct edit*)malloc(sizeof(struct edit));
}

void edit_destroy(struct edit* edt)
{
  printf("edit_destroy\n");
}

void edit_draw(struct edit* edt)
{
  printf("edit_draw\n");
}

This file defines what is "control". In this case, the control set is { button , edit }

controls.h


#ifndef CONTROLS_H
#define CONTROLS_H

/*Include your headers here*/
#include "button.h"
#include "edit.h"

/*include your types here*/
#define TYPES(p, f, ...) \
  X(button, p, f, __VA_ARGS__)\
  X(edit, p, f, __VA_ARGS__)


#define X(a, obj, f, ...) a##_id,
enum
{
  TYPES(p, f)
};
#undef X

#define X(T, obj, func, ...) \
  case T##_id: T##_##func((struct T*)((obj).pointer), __VA_ARGS__);  break;

#define dynamic_call(obj, func, ...)\
    switch ((obj).type)\
    {\
     TYPES(obj, func, __VA_ARGS__)\
    }


#endif /*CONTROLS_H*/



sample.c

#include <stdlib.h>
#include "controls.h"
#include "type_ptr_array.h"

void controls_destroy(struct type_ptr* p)
{
  dynamic_call(*p, destroy);
}

int main(int argc, char* argv[])
{
  struct type_ptr_array controls = TYPE_PTR_INIT;

  type_ptr_array_push(&controls, button_id, button_create());
  type_ptr_array_push(&controls, edit_id, edit_create());

  for (size_t i = 0; i < controls.size; i++)
  {
    dynamic_call(controls.data[i], draw);
  }

  type_ptr_array_destroy(&controls, &controls_destroy);

  return 0;
}

type

#ifndef TYPE_PTR_ARRAY_H
#define TYPE_PTR_ARRAY_H

struct type_ptr
{
  int type;
  void* pointer;
};

struct type_ptr_array
{
  size_t    size;
  size_t    capacity;
  struct type_ptr* data;
};

#define TYPE_PTR_INIT {0,0,0}

void type_ptr_array_destroy(struct type_ptr_array* p,
                           void(*destroy)(struct type_ptr*));

void type_ptr_array_push(struct type_ptr_array* p, int type, void* item);


#endif /*TYPE_PTR_ARRAY_H*/

type


#include <stdlib.h>
#include "type_ptr_array.h"

static size_t type_ptr_array_reserve(struct type_ptr_array* p, size_t nelements)
{
  void *pnew = 0;
  if (nelements > p->capacity)
  {
    pnew = realloc((void*)p->data, nelements * sizeof(p->data[0]));
    if (pnew)
    {
      p->data = (struct type_ptr*)pnew;
      p->capacity = nelements;
    }
  }

  return (pnew != 0) ? nelements : 0;
}

size_t type_ptr_array_grow(struct type_ptr_array* p, size_t nelements)
{
  if (nelements > p->capacity)
  {
    size_t newCap = p->capacity == 0 ? 4 : p->capacity;
    while (newCap < nelements)
    {
      newCap *= 2;
      if (newCap < nelements ||
        newCap >(size_t)(UINT_MAX / sizeof(p->data[0])))
      {
        newCap = (size_t)(UINT_MAX / sizeof(p->data[0]));
      }
    }
    return type_ptr_array_reserve(p, newCap);
  }
  return p->capacity;
}

void type_ptr_array_destroy(struct type_ptr_array* p,
                            void (*destroy)(struct type_ptr*))
{
  for (size_t i = 0; i < p->size; i++)
  {
    destroy(&p->data[i]);
  }
}

void type_ptr_array_push(struct type_ptr_array* p,
                         int type,
                         void* item)
{
  size_t result = type_ptr_array_grow(p, p->size + 1);

  if (result == 0)
  {
    exit(1);
    return;
  }
  p->data[p->size].type = type;
  p->data[p->size].pointer = item;
  p->size += 1;
}