22
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

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,

Embed Size (px)

Citation preview

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

4

Vehicle

TruckCar

Emergency

PoliceCar FireEngine

EmergencyVehicle

Example 1

5

ios

iostream

ostreamistream

fstreambase

ifstream ofstream

fstream

virtual virtual

virtual

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

8

OpEval

PlusCheckPlusEval

Operator

Plus

OpCheck OpDisplay

PlusDisplay

Example 2 (Harold Ossher)

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

[email protected]

[email protected]

[email protected]

Download source from http://gsd.web.elte.hu