#include <stdlib.h>
#include <stdio.h>
#include <string.h>

/*** Object types */

typedef struct s_obj s_obj;
typedef struct s_string s_string;
typedef struct s_integer s_integer;

/*** Function types */

typedef void f_any();
typedef f_any * f_interface(s_obj *, f_any *);

typedef char * f_tostring(s_obj *);

/*** Function declarations */

f_tostring tostring;

void init_string(s_string *);
f_interface string_interface;
f_tostring string_tostring;

void init_integer(s_integer *);
f_tostring integer_tostring;
f_interface integer_interface;

/*** Struct/union definitions */

struct s_obj {
    f_interface * get_interface;
  };

struct s_string {
    s_obj obj[1];
    char * text;
  };

struct s_integer {
    s_obj obj[1];
    int i;
  };

/*** Function definitions */

int main(void) {
    char * tmp;
    s_string my_string;
    s_integer my_integer;

    init_string(&my_string);
    my_string.text = "My string!";
    tmp = tostring(my_string.obj);
    puts(tmp);
    free(tmp);

    init_integer(&my_integer);
    my_integer.i = 42;
    tmp = tostring(my_integer.obj);
    puts(tmp);
    free(tmp);

    return EXIT_SUCCESS;
  }

/* You must 'free' the storage allocated by 'tostring'! */
char * tostring(s_obj * obj) {
    f_tostring * func;

    func = (f_tostring *) obj->get_interface(obj, (f_any *) tostring);
    return func(obj);
  }

void init_string(s_string * string) {
    string->obj->get_interface = string_interface;
    string->text = NULL;
    return;
  }

f_any * string_interface(s_obj * obj, f_any * func) {
    /* Unused */
    (void) obj;

    if (0);
    /* 'tostring' case */
    else if (func == (f_any *) tostring)
      return (f_any *) string_tostring;
    /* ...Other cases... */
    /* Unsupported interface */
    exit(EXIT_FAILURE);
  }

char * string_tostring(s_obj * obj) {
    s_string * this = (void *) obj;
    char * new;
    int len;

    len = strlen(this->text) + 1;
    new = malloc(len);
    if (!new)
      return new;

    memcpy(new, this->text, len);
    return new;
  }

void init_integer(s_integer * integer) {
    integer->obj->get_interface = integer_interface;
    integer->i = 0;
    return;
  }

f_any * integer_interface(s_obj * obj, f_any * func) {
    /* Unused */
    (void) obj;

    if (0);
    /* 'tostring' case */
    else if (func == (f_any *) tostring)
      return (f_any *) integer_tostring;
    /* ...Other cases... */
    /* Unsupported interface */
    exit(EXIT_FAILURE);
  }

char * integer_tostring(s_obj * obj) {
    s_integer * this = (void *) obj;
    char tmp[50];
    char * new;
    int len;

    sprintf(tmp, "%d", this->i);
    len = strlen(tmp) + 1;
    new = malloc(len);
    if (!new)
      return new;

    memcpy(new, tmp, len);
    return new;
  }
