1 an extension to the subtype relationship in c++ istván zólyomi, zoltán porkoláb and tamás...
TRANSCRIPT
1
An extension to the subtype relationship in C++
István Zólyomi, Zoltán Porkoláb and Tamás Kozsik
{scamel | gsd | kto}@elte.hu
Eötvös Loránd University, Budapest, Hungary
2
The structure of this presentation
• Motivation with examples
• Loki::Typelist (short reminder)
• Creating the Family hierarchy
• Converting between families
• Inclusion polymorphism (FamilyPtr and FamilyRef)
• Practical experiences
• Summary
3
Motivation
• Maximal separation of functionalities
• Collaboration based design
o Better maintenance of code
o Easier to create classes with required behavior
• Create and handle classes as a collection of
independent components
6
Possible solutions
Vehicle
TruckCar
Emergency
PoliceCar FireEngine
EmergencyVehicle
• Virtual inheritance
• Intrusive: specified in base classes
• More concerns implies exponential number of virtual bases
• Traits
• No subtype relationship using hierarchies in traits
• AOP, MDSC, CF
• are for slightly different problems
• are extensions to standard C++
• Families: subtype relationship implemented with metaprogramming
• Non-intrusive solution based on the C++ standard
7
• Class: implementation of a concern
– Orthogonal concerns in independent hierarchies
• Collaboration-based design: combining concerns
– Collect concerns to gain required behavior
• Family: instrument to express collaboration of concerns
– Implemented as a single class template
– Collects concern classes as a mixin using multiple inheritance
– OO languages support conversions to a single concern (base)
– do not support conversions between families (collections of concerns)
Terminology
9
Loki::Typelist
• Introduced by Andrei Alexandrescu (and others)
• Handles meta-information (types) in compile time like
conventional containers do with data in runtime
• Any number of types can be listed using recursion
<class Head, class Tail> struct Typelist { … };
• The last element of every list is Loki::NullType by convention (like \0 for C-strings)
typedef Typelist<char,Typelist<short,
Typelist<int, NullType> > >Scalars;
10
• Typelist has many operations in Loki: appending, indexed access, removing duplicates, etc
typedef TYPELIST_3(char,short,int) TheSameList;
Loki::Typelist (cont.)
• We can make the previous class definition linear with predefined macros with NullType included
typedef Append<Scalars, TYPELIST_1(long)>::Result ExtendedList;
11
Source code of family construction
template <class List> struct Family;
template <class Head, class Tail> struct Family< Typelist<Head,Tail> > : public Head, public Family<Tail>{ // --- Type name shortcuts typedef Family<Tail> Rest; typedef Family< Typelist<Head,Tail> > MyType;
// --- Copy constructor Family(const MyType& mt) : Head(mt), Rest(mt) {} // --- "Recursive" constructor Family(const Head& head, const Rest& rest): Head(head), Rest(rest) {}};
template <class Head>struct Family< Typelist<Head,NullType> > : public Head{ // --- All in one constructor Family(const Head& head) : Head(head) {}};
12
OpCheck OpEval OpDisplay
FAMILY_1(OpCheck)
FAMILY_2(OpEval,OpCheck)
FAMILY_3(OpDisplay,OpEval,OpCheck)
OpEval
PlusCheckPlusEval
Operator
Plus
OpCheck OpDisplay
PlusDisplay
FAMILY_3(OpDisplay, OpEval, OpCheck) op;
13
Conversions between families
• Initialize the head class for each recursion step
• Template constructors provide conversion steps (the
same for operator=)
FAMILY_3(PlusEval,PlusDisplay, PlusCheck) sum;FAMILY_2(OpEval,OpCheck) calculate;
calculate = sum;
• Example of usage:
template <class Head, class Tail> template <class FromType>Family< Typelist<Head,Tail> > ::Family(const FromType& f): Head(f), Family<Tail>(f) {}
14
Advantages
• Type safe: based on builtin language conversions
• Efficient: no temporal families or objects are used,
objects are initialized directly
• General and transparent: not restricted to families only,
any user object can be converted without explicit
conversion call.
struct Minus: public MinusEval, public MinusDisplay,public MinusCheck {};
Minus subtract;
calculate = subtract;
15
Limitations
• No duplicates in typelists or compile error
• Keyword explicit is suppressed because of explicit
constructor calls during conversion
• Compilation fails if there is no default or copy constructor
and assignment operator
16
Smart pointers
• Problems:
• Conversion copies objects by value (slicing)
• No dynamic binding
• Solution: smart pointers
• Implementation and usage similar to those of Family
FAMILY_3(PlusDisplay, PlusEval, PlusCheck) sum;FAMILYPTR_2(OpDisplay, OpEval) opPtr(sum);
// --- Function call with explicit caststatic_cast<OpDisplay*>(opPtr)->show();
// --- In longer form with implicit castOpDisplay *displayPtr = opPtr;displayPtr->show();
17
FAMILYPTR_2(OpDisplay,OpEval)
OpDisplay *head
FAMILYPTR_1(OpEval)
OpEval *head
PlusDisplay PlusCheck PlusEval
points to
points to
FAMILYPTR_2(OpDisplay, OpEval) opPtr;
head: OpDisplay*head: OpEval*
18
• Similar to FamilyPtr
• Store references instead of pointers
• Consequences:• A FamilyRef object must be initialized
• Initializes by reference but copies by value during assignment
FamilyRef
FAMILY_3(PlusDisplay, PlusEval, PlusCheck) sum;
// --- Initializes by referenceFAMILYREF_3(OpDisplay, OpEval, OpCheck) exprRef(sum);
// --- Copies by valueexprRef = sum;
19
#include <iostream>#include "family.h"
struct Shape { virtual void f() { std::cout << "Shape"; } };struct Circle : public Shape { void f() { std::cout << "Circle"; } };
struct Colored {};struct Filled {};
void main(){
FAMILY_3(Circle, Colored, Filled) extCircle;FAMILY_3(Colored, Filled, Shape) extShape(extCircle);FAMILY_2(Filled, Colored) extensions(extCircle);
extensions = extShape = extCircle;
FAMILYPTR_3(Colored, Filled, Shape) extShapePtr(extCircle);Shape* shapePtr = extShapePtr;shapePtr->f(); // --- Prints “Circle”
FAMILYPTR_3(Filled, Colored, Circle) extCirclePtr(extCircle);
extShapePtr = extCircle; // --- Object -> PointerextShapePtr = extCirclePtr; // --- Der. Pointer -> Base PointerextShape = extShapePtr; // --- Pointer -> Object (by value)
}
20
Experiences
CompilerNr. of
classesCause of limitation
g++ 3.2 45Macro parameter limit of precompiler
g++ 2.96 17 Recursion depth limit reached
Intel 7.1 25 Unacceptable compile time
VC++ .NET 0Loki does not compile
(compilers are not compliant to standards)
Borland 6.0 0
OpenWatcom 1.0 0
21
Summary
• Support for collaboration based design
• Implementation uses Loki::Typelist
• Non-intrusive solution
• Easy to understand and manage
• No extension to C++
• Bad experience with compilers vs standards
• Features:
• Composition of concerns in a single class (Family)
• Conversion between related families
• Dynamic binding with smart pointers and references
22
István Zólyomi
Zoltán Porkoláb
Tamás Kozsik
Eötvös Loránd University, Budapest, Hungary
Download source from http://gsd.web.elte.hu