Polymorphism C and Data Structures Baojian Hua [email protected].

28
Polymorphism C and Data Structures Baojian Hua [email protected]
  • date post

    22-Dec-2015
  • Category

    Documents

  • view

    240
  • download

    3

Transcript of Polymorphism C and Data Structures Baojian Hua [email protected].

Polymorphism

C and Data StructuresBaojian Hua

[email protected]

An old dream in CS

Famous slogans: “Write code once, used everywhere!” “Never write same code twice!”

Polymorphism Poly + morphism

Elegant and amazing combination of theory and practice in CS!

Why polymorphism?// a list of integer:struct List_t_int{ int data; struct List_t_int *next;};// “length”int length (List_t_int l) { int size=0; List_t_int temp = l->next; while (temp) { size++; temp=temp->next; } return size;}

the “data” field?

Why polymorphism?// what about if we want a list of string?struct List_t_string{ char *data; struct List_t_string *next;};// function “length” remains the same?int length (List_t_string l) { int size=0; List_t_string temp = l->next; while (temp) { size++; temp=temp->next; } return size;}

Outline

Ad-hoc poly’ Parametric poly’ Subtype poly’

Ad-hoc poly’

Functions operate on different but a limited set of data type e.g., “+” in C

int * int, double * double, char * * int but not: char * * char *

First introduced by Strachey in 1967 Evolve into a feature called “overloa

ding” in modern OO languages

Ad-hoc poly’ Overloading in modern OO languages:

Same printing function name with different argument types:

void print (int x); void print (char *s);

Compilers will dispatch function calls print (5); print (“hello”);

Bad news is that C does not support overloading! We’d have to do some hacking. :-( Or we can write unique names, as:

void Int_print (int x); void String_print (char *s);

Parametric poly

Basic idea: decorating data types (data structure + functions) with type variables

Polymorphic data structures Polymorphic functions

we start with this

E.g.int max (int x, int y){ return x>y? x: y;}// but we’ve observed the problem:double max (double x, double y){ return x>y? x: y;}// What about factor out the type names:X max (X x, X y){ return x>y? x: y;}// but this function does not type check!

Old trick#define max(x, y) x>y? x: y// So we can write:int m = max(3, 4);// ordouble n = max (3.14, 2.71);// but this is very good! Why?

// We can do better by hoisting “X” further:#define max_X(X) \X max (X x, X y) \{ \ return x>y? x: y; \}

size

Client code#define max_X(X) \X max (X x, X y) \{ \ return x>y? x: y; \}

max_X(int)

int main (){ max (3, 4);}

Questions:

1: what about we write this?

max_X (double)

does this program still type check?

2: what about we write

max_X (char *)

is char * comparable? is this algorithm correct?

Client code#define max_X(X) \X max (X x, X y, int (*m)(X, X)) \{ \ if (1==m(x, y)) \ return x; \ return y; \}

max_X(int)

int int_compare (int x, int y) {…}

int main (){ max (3, 4, int_compare);}

List revisit#define List_X(X) struct List_t { X data; struct List_t *next;};// “length”int length (List_t l) { int size=0; List_t temp = l->next; while (temp) { size++; temp=temp->next; } return size;}

the “data” field?

Client code// we want a list of integer:List_X(int)

// we want a list of double:List_X(double)

Summary so far

Parametric poly: data structures and functions take extra t

ype parameters (hence the name) instation at compile-time

Monomorphinization: code eventually becomes monomorphic faithfully models the feature of template

in C++

Summary so far Pros:

very powerful and elegant no runtime overhead (code is eventually mono) of course, require some C hack

Cons: code explosion (due to monomorphinization)

but seldom observed in practical code the poly code itself is not type checked compilation may be slow

Subtype polystruct List_t { X data; struct List_t *next;};

// Key idea: what if we can invent a most general // type “X”, such that any value is compatible // with “X”.

// In C, this type is “void *”.

List revisitstruct List_t

{

void *data;

struct List_t *next;

};

// Leave it an exercise to write function “length”

Client codeint main (){ // we want a list of integer: for (int i=0; i<10; i++) { int *p = malloc (sizeof (*p)); *p = i; List_insertHead (l, p); } // we want a list of double: for (int i=0; i<10; i++) { double *p = malloc (sizeof (*p)); *p = i; List_insertHead (l, p); }}

Subtype poly

All pointer types can be treated as a subtype of void * so we’d have to heap-allocate all

objects to accompany this type Java or C# go even further: all

objects are heap-allocated the most general type is Object this models Java generic honestly

Subtype poly Pros:

single piece of code no compilation overhead

Cons: Safety issue

ugly type cast, and must be veryyyyyyyyyyyyyy careful efficiency issue

allocation and garbage collection complexity issue

Think abstractlystruct List_t { int data; struct List_t *next;};

// Is it a good idea to write this function?int exists (List_t l, int x); // Or this function?int sum (List_t l, int init);// Or this one?void print (List_t l);

Think abstractlyvoid print (List_t l)

{

List_t temp = l->next;

while (temp) {

printf (“%d, ”, temp->data)

temp = temp->next;

}

return;

}

Think abstractly// But what if we make it parametric poly?

#define List_X(X)

struct List_t

{

X data;

struct List_t *next;

};

void print (List_t l);

1st tryvoid print (List_t l)

{

List_t temp = l->next;

while (temp) {

printf (“%???”, temp->data);

temp = temp->next;

}

return;

}

Think abstractly// To make it parametric poly:

#define List_X(X)

struct List_t

{

X data;

struct List_t *next;

};

// so we’d have to make “print” more abstract!

void print (List_t l, void (*p)(X));

2nd tryvoid print (List_t l, void (*p)(X))

{

List_t temp = l->next;

while (temp) {

p (temp->data);

temp = temp->next;

}

return;

}

Client code// instantiate an “int” list:List_X(int)

void f (int x){ printf (“%d, ”, x);}

int main (){ List_t list = …; // cook a list of int List_print (list, f);}