210 likes | 214 Views
Implementation of Dynamic Binding. RTTI & Dynamic Binding Virtual Functions Table Location of VPTR Inline and Dynamic Binding?. Binding Time. Inclusion Polymorphism + Overriding = Binding Question. Binding : linking between messages and methods
E N D
Implementation of Dynamic Binding • RTTI & Dynamic Binding • Virtual Functions Table • Location of VPTR • Inline and Dynamic Binding?
Binding Time Inclusion Polymorphism + Overriding = Binding Question • Binding: linking between messages and methods • The same entity may refer to objects of different classes, each of which has a different implementation of the same method More generally, binding time is the time when an attribute of some portion of a program, or the meaning of a particular construct is determined • Compile Time • Link Time • Execution Time: • Program Init • Procedure/function begin • Statement Execution • Static Binding (AKA Early Binding): the compiler uses the type of variables to do the binding at a compile (link) time • Dynamic Binding (AKA Late Binding): the decision is made at run time based on the type of the actual values
Reminder: Static vs. Dynamic Binding • Static Binding: binding based on static type. • More efficient • Less flexible • Static type checking leads to safer programs • Dynamic Binding: binding based on dynamic type. • Less efficient • More flexible • May need dynamic type checking!
Run Time Type Information (RTTI) • Always exists in OOP: a prerequisite for dynamic binding • Accessible to programmer? • Not necessarily in statically typed languages • Many things can be done without it! • Almost always in dynamically typed languages • Without it, it is impossible to be sure that an object will recognize a message! • In LST, RTTI is the information accessible from the instance_of pointer
Shapes in C++ class Shape {public:virtualvoid draw(void);virtualvoid hide(void);virtualvoid rotate(int deg); ... protected:int x, y; ... /* Other common fields */}; class Circle: public Shape {public:virtualvoid draw(void) { ... }virtualvoid hide(void) { ... } //...}; class Line: public Shape {public:virtualvoid draw(void) { ... }virtualvoid hide(void) { ... } //...}; class Rectangle: public Shape {public:virtualvoid draw(void) { ... }virtualvoid hide(void) { ... } //...};
RTTI in Smalltalk Obtain class of an object: (x class == Circle) ifTrue: [ ... ] Determine if an object belongs to a class: (x isMemberOf: Shape) ifTrue: [ ... ] Determine if an object belongs to a subclass: (x isKindOf: Shape) ifTrue: [ ... ] Does an object understand a message? (x respondsTo: #draw) ifTrue: [ ... ]
RTTI in Object Pascal Var x: Shape; c: Circle; Begin ... x = c; ... If member(x, Circle) then Begin Writeln('x is a Circle'); x.draw(); { Draw must be defined for class Shape!} { This will call Circle.draw! } endelse Writeln('x is not a circle');
RTTI in C++ class typeinfo {public:virtual ~typeinfo(void);booloperator==(const typeinfo&) const;booloperator!=(const typeinfo&) const; bool before(const typeinfo&) const; const char *name(void) const;private: typeinfo(const typeinfo&); typeinfo& operator= (const typeinfo&); //.. Implementation dependent fields}; No RTTI in early versions of the language. No feature should incur a cost if not used. Even now is very limited class Base { ...}; void f(Base *p){const typeinfo& a = typeid(p); // Type information for Base *const typeinfo& a = typeid(*p); // Actual run time type of *p}
Downcasting vs. Dynamic Binding void draw(Shape *p){ if (Circle *q = dynamic_cast<Circle *>p) { // Draw circle ... } elseif (Line *q = dynamic_cast<Line *>p) { // Draw line ... } elseif (Rectangle *q = dynamic_cast<Rectangle *>p) { // Draw rectangle ... } ... } This pattern is considered harmful: • Order of classes in the if chains is significant • Module must change whenever new class is added to the hierarchy
Programming with Dynamic Binding • Given a polymorphic pointer: Shape* s = ...; • Then, how can we determine if a s is a circle or not? • Dynamic Binding Answer: • The question is wrong!!! • Usually there's no need to determine the dynamic type of an object. • Differences between objects: • Different state • Differences between classes: • Different implementation of methods • Usage of RTTI in all but very special cases indicates a misunderstanding of the power of dynamic binding.
Dynamic Binding in C? void rotate(Shape *p) {switch (p->type) {case rectangle: ... case circle: ... case line: ... ... }} enum Shape_type { rectangle, circle, line, ... }; Coupling (Deja-vu) struct Shape {int x, y; ... /* Other common fields */ Shape_type type;union {struct Rectangle { ... } r; struct Circle { ... } c; struct Line { ... } l; ... /* Other shape kinds */ } data;}; pointers to functions can be used instead. void draw(Shape *p) {switch (p->type) { case rectangle: ... case circle: ... case line: ... ... }}
Disadvantages of the C Solutions • Implicit assumption: consistency between value of the type tag and usage of the union field data. • Programmer’s responsibility: no compiler aid or checking • Dispersed coding: the code for different operations on the same shape is spread all over the program. • Difficult to understand • Insusceptibility to change: whenever a new kind of shape is introduced, the following must be changed: • Definition of Shape_type • Definition of Shape • Function rotate • Function draw • All other functions
Implementation of Virtual Functions Ellipse draw+ hide+ rotate+ Circle rotate++ centre+ class Ellipse { // ... public: virtualvoid draw() const; virtualvoid hide() const; virtualvoid rotate(int); } E1, E2, *P; E1 E2 P class Circle: public Ellipse { //... public: virtual void rotate(int); virtual Point centre(); } C1, C2, C3; C1 C2 C3
The Virtual Methods Table (AKA vtbl) E1 E2 C1 C2 C3 Ellipse VMT Circle VMT draw hide rotate draw hide rotate centre P C++ Jargon:vptr and vtbl Ellipse :: draw Circle :: centre Ellipse :: hide Ellipse :: rotate Circle :: rotate
P->rotate() E1 E2 C1 C2 C3 Ellipse VMT Circle VMT draw hide rotate draw hide rotate centre P Ellipse :: draw Ellipse :: hide Circle :: centre Ellipse :: rotate Circle :: rotate
Location of VPTR #1/2 Borland Style: at the beginning of an object. • Intuitive and simple (usually) • Problematic, if the base class does not have any virtual functions: • When converting a pointer to the derived class into a pointer to the base, the compiler must add an offset to skip over the vptr. • This offset must be subtracted in downcasting. • The compiler must also do a null check, because the offset should not be added in case of a null pointer.
Location of VPTR #2/2 Gnu Style: when first virtual function is encountered • Not so simple or intuitive. • Compiler must have a deterministic algorithm for locating the vptr: • If the function called is not virtual in the static type of the pointer - use static binding • If the function is virtual - add to the pointer the offset corresponding to the size of the most derived “virtual-free” super-class of the static type of the pointer • Casting is so much simpler. • No need to add or subtract any offset in up or down casting.
Dynamic Binding and Efficiency Dynamic binding: compute method address at run time. • There must be a run time penalty in calling a method vs. calling an ordinary routine! • In C++ and other statically typed languages, the penalty is small: • 2-3 pointers references • Few clock cycles • In Smalltalk, CLOS and other dynamically typed languages, the penalty might be larger: • Inheritance hierarchy might change in run-time • Cache and other sophisticated algorithms may help • In Eiffel and other non-separate compilation languages, the penalty might be smaller: • More opportunities for optimization
Inline Virtual Functions? • The real performance hit (up to x 25 ratio) occurs when opportunities for inline are not used. • When can a call to an inline virtual function be made inline? • Not inside function members! void SomeClass::some_function_member(void) { some_virtual_function_member(); } • Virtual calls on a non-reference type (e.g.: by-value variable): void f(SomeClass a) { a.some_virtual_function_member(); } • Explicit callsvoid f(SomeClass& a) { a.SomeClass::some_virtual_function_member(); } • Inside constructors/destructors • C++ employs static binding for this in constructors/destructors • In Java, C# the binding of this is always dynamic
Dispatch Tables • Used in dynamic type systems • Support: • Runtime introduction of new types • Runtime changes to type hierarchy • “Method not found” error messages • Space Efficiency: optimal! • Time Efficiency: lousy; mitigated by a cache of triples: • Class where search started • Selector searched • Address of method found
Binding and Typing in OOP Languages Language Typing Binding ADA CLOS Smalltalk Objective-C Object Pascal Turbo Pascal C++ Eiffel Static Untyped Dynamic Weak/Dynamic Strong Strong Weak/Static Strong/Static Static Dynamic Dynamic Dynamic Dynamic Dynamic Static/Dynamic Dynamic