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].
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;}
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?
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;
}