Presentation is loading. Please wait.

Presentation is loading. Please wait.

PROGRAMMING TECHNIQUES Based on “Modern C++ Design” BY ALEXANDRESCU BOGDAN PENKOVSKY 2010.

Similar presentations


Presentation on theme: "PROGRAMMING TECHNIQUES Based on “Modern C++ Design” BY ALEXANDRESCU BOGDAN PENKOVSKY 2010."— Presentation transcript:

1 PROGRAMMING TECHNIQUES Based on “Modern C++ Design” BY ALEXANDRESCU BOGDAN PENKOVSKY 2010

2 PROGRAMMING TECHNIQUES 20102 WHAT’S ALL THIS STAFF ABOUT?

3 PROGRAMMING TECHNIQUES 20103 WHAT’S ALL THIS STAFF ABOUT? Through all the “Modern C++ design” some essential techniques are stretched. Bold knowledge of them will help to understand main ideas and… create powerful idioms. So, we’re going to review and discuss main PROGRAMMING TECHNIQUES

4 PROGRAMMING TECHNIQUES 20104 TECHNIQUES Compile-Time Assertions Partial Template Specialization Local Classes Mapping Integral Constants to Types Type-to-Type Mapping

5 PROGRAMMING TECHNIQUES 20105 TECHNIQUES Type Selection Detecting Convertibility and Inheritance at Compile Time A Wrapper Around type_info NullType and EmptyType Type Traits

6 PROGRAMMING TECHNIQUES 20106 The need for better static checking (and more customizable error messages) template To safe_reinterpret_cast(From from) { assert (sizeof(From) <= sizeof(To)); return reinterpret_cast(from); } int i=…; char*p=safe_reinterpret_cast (i);

7 PROGRAMMING TECHNIQUES 20107 Safe_reinterpret_cast function Problem: Runtime detecting. Detecting on compilation would be better. Reason: Porting the application to a new compiler might leave the bug dormant until it crashes the program in front of your customer.

8 PROGRAMMING TECHNIQUES 20108 Compile time assertion #define STATIC_CHECK(expr) { char unnamed[( expr)? :1 : 0]; } template To safe_reinterpret_cast(From from) { STATIC_CHECK(sizeof(From) <= sizeof(To)); return reinterpret_cast (from); } void* somePointer=…; char c=safe_reinterpret_cast (somePointer);

9 PROGRAMMING TECHNIQUES 20109 Static_check Non zero-length array approach Problem: Not terribly informative: "Cannot create array of size zero" does not suggest "Type char is too narrow to hold a pointer."

10 PROGRAMMING TECHNIQUES 201010 What we can improve? A better solution is to rely on a template with an informative name; the compiler will mention the name of that template in the error message

11 PROGRAMMING TECHNIQUES 201011 Template CompileTimeChecker template struct CompileTimeChecker; { CompileTimeChecker(…); }; template<> struct CompileTimeChecker {}; #define STATIC_CHECK(expr, msg) \ {\ class ERROR_##msg {}; \ (void)sizeof(CompileTimeChecker ((ERROR_##msg())));\ }

12 PROGRAMMING TECHNIQUES 201012 Let’s test Assume that sizeof(char) < sizeof(void*) (not guaranteed) template To safe_reinterpret_cast(From from) { STATIC_CHECK(sizeof(From) <= sizeof(To), Destination_Type_Too_Narrow); return reinterpret_cast (from); }... void* somePointer =...; char c = safe_reinterpret_cast (somePointer);

13 PROGRAMMING TECHNIQUES 201013 After macro preprocessing template To safe_reinterpret_cast(From from) {  { class ERROR_Destination_Type_Too_Narrow {}; (void)sizeof( CompileTimeChecker ( ERROR_Destination_Type_Too_Narrow ()));  } return reinterpret_cast (from); }

14 PROGRAMMING TECHNIQUES 201014 Template CompileTimeChecker Now here is a trick! The CompileTimeChecker specialization has a constructor that accepts anything. This means that if the expression checked evaluates to true, the resulting program is valid. If the comparison between sizes evaluates to false we can see a message: "Error: Cannot convert ERROR_Destination_Type_Too_Narrow to CompileTimeChecker."

15 PROGRAMMING TECHNIQUES 201015 NOT BAD? LET’S MOVE FORWARD!

16 PROGRAMMING TECHNIQUES 201016 ONLY ABOUT 50 SLIDES LEFT!

17 PROGRAMMING TECHNIQUES 201017 Partial Template Specialization template class Widget {.. Generic implementation... }; And explicit specialization: template <> class Widget {... Specialized implementation... };

18 PROGRAMMING TECHNIQUES 201018 Partial Template Specialization It allows to specialize a class template for subsets of that template's possible instantiations set. Typically, in a partial specialization of a class template, it is specified only some of the template arguments and the other ones are left generic.

19 PROGRAMMING TECHNIQUES 201019 Capabilities When you instantiate a template, the compiler does a pattern matching of existing partial and total specializations to find the best candidate This gives an enormous flexibility.

20 PROGRAMMING TECHNIQUES 201020 Partial Template Flexibility template class Widget, MyController> {... further specialized implementation... };

21 PROGRAMMING TECHNIQUES 201021 Limitations Although you can totally specialize member functions of a class template, you cannot partially specialize member functions. You cannot partially specialize namespace-level (nonmember) template functions. The only closest is overloading. So you have fine-grained specialization abilities only for the function parameters That is because “bad” compiler writers make their lives easier.

22 PROGRAMMING TECHNIQUES 201022 Partial Function Templates template T Fun(U obj); // primary template template void Fun (U obj); // illegal partial // specialization template T Fun (Window obj); //legal (overloading)

23 PROGRAMMING TECHNIQUES 201023 LOCAL CLASSES void Fun() { class Local {... methods... };... code used by local class... }

24 PROGRAMMING TECHNIQUES 201024 Limitations There’s no way to define static member variables No way to access nonstatic local variables Any idiom that uses a local class can be implemented using a template class outside the function

25 PROGRAMMING TECHNIQUES 201025 Advantages Local classes defined inside template functions can use the template parameters of the enclosing function Unique feature: local classes are final. Without them, you'd have to add an unnamed namespace in a separate translation unit.

26 PROGRAMMING TECHNIQUES 201026 LOCAL CLASSES INSIDE TEMPLATE FUNCTIONS (idiom) class Interface { public: virtual void Fun() =0; … };

27 PROGRAMMING TECHNIQUES 201027 INSIDE TEMPLATE FUNCTIONS template Interface* MakeAdapter(const T& obj, const P& arg) { class Local : public Interface { public: Local(const T& obj, const P& arg): obj_(obj), arg_(arg) {} virtual void Fun() { obj_.Call(arg_); } private: T obj_; P arg_; }; return new Local(obj, arg); }

28 PROGRAMMING TECHNIQUES 201028 Mapping Integral Constants to Types template struct lnt2Type { enum { value = v }; };

29 PROGRAMMING TECHNIQUES 201029 Mapping Integral Constants to Types Int2Type generates a distinct type for each distinct constant integral value passed. Can be used whenever there’s need to "typify" an integral constant quickly Achieves dispatching on a constant integral value.

30 PROGRAMMING TECHNIQUES 201030 Int2Type Typical use: You need to call one of several different functions, depending on a compile-time constant. You need to do this dispatch at compile time. Differently from else-if statement, Int2Type doesn’t require compilation of both code branches. I.E.:

31 PROGRAMMING TECHNIQUES 201031 NiftyContainer template class NiftyContainer { void DoSomething() { T* pSomeObj =...; if (isPolymorphic) { T* pNewObj = pSomeObj->clone();... polymorphic algorithm … } else { T* pNewObj = new T(*pSomeObj);... nonpolymorphic algorithm... } };

32 PROGRAMMING TECHNIQUES 201032 NiftyContainer The problem is that the compiler won't let you get away with this code. The compiler diligently tries to compile both branches, even if the optimizer will later eliminate the dead code. How to avoid: you can use Int2Type with simple overloading

33 PROGRAMMING TECHNIQUES 201033 NiftyContainer template class NiftyContainer { private: void DoSomething(T* pObj, Int2Type ) { T* pNewObj = pObj->Clone();... polymorphic algorithm... } …

34 PROGRAMMING TECHNIQUES 201034 NiftyContainer … void DoSomething(T* pObj, lnt2Type ) { T* pNewObj = new T(*pObj);... nonpolymorphic algorithm... } public: void DoSomething(T* pObj) { DoSomething(pObj, lnt2Type ()) ; } };

35 PROGRAMMING TECHNIQUES 201035 Type-to-Type Mapping Consider: template T* Create(const U& arg, T /* dummy */ ) { return new T(arg); } Now say there is Objects of type Widget that must take two arguments upon construction

36 PROGRAMMING TECHNIQUES 201036 Obvious solution template Widget* Create (const U& arg) { return new Widget(arg, -1); }

37 PROGRAMMING TECHNIQUES 201037 Obvious solution template Widget* Create (const U& arg) { return new Widget(arg, -1); } FAILS TO COMPILE!

38 PROGRAMMING TECHNIQUES 201038 Type-to-Type Mapping //Overloading only template T* Create(const U& arg, T /* dummy */) { return new T(arg); } template Widget* Create(const U& arg, Widget /* dummy */) { return new Widget(arg, -1); }

39 PROGRAMMING TECHNIQUES 201039 Type-to-Type Mapping Such a solution would incur the overhead of constructing an arbitrarily complex object that remains unused. We need a light vehicle for transporting the type information about T to Create. So let’s look forward:

40 PROGRAMMING TECHNIQUES 201040 Type-to-Type Mapping template struct Type2Type { typedef T OriginalType; }; Type2Type is devoid of any value, but distinct types lead to distinct Type2Type instantiations, which is what we need.

41 PROGRAMMING TECHNIQUES 201041 Create based on overloading & Type2Type template //better implementation T* Create(const U& arg, Type2Type ) { return new T(arg); } template Widget* Create(const U& arg, Type2Type ) { return new Widget(arg, -1); } String* pStr = Create("Hello", Type2Type ()); Widget* pW = Create(100, Type2Type ());

42 PROGRAMMING TECHNIQUES 201042 Type Selection Sometimes generic code needs to select one type or another, depending on a Boolean constant. template struct NiftyContainervalueTraits { typedef T* ValueType; };

43 PROGRAMMING TECHNIQUES 201043 Type Selection template struct NiftyContainervalueTraits { typedef T* ValueType; }; template class NiftyContainer { typedef NiftyContainerValueTraits Traits; typedef typename Traits::ValueType ValueType; };

44 PROGRAMMING TECHNIQUES 201044 Type Selection This way of doing things is unnecessarily clumsy. It doesn't scale: for each type selection, you must define a new traits class template. The library class template Select provided by Loki makes type selection available right on the spot. Its definition uses partial template specialization.

45 PROGRAMMING TECHNIQUES 201045 Template Select template struct Select { typedef T Result; }; Template struct Select { typedef U Result; };

46 PROGRAMMING TECHNIQUES 201046 Template Select How it works: If flag evaluates to true, the compiler uses the first (generic) definition, therefore Result evaluates to T. If flag is false, then the specialization enters into action and Result evaluates to U.

47 PROGRAMMING TECHNIQUES 201047 Moving on…

48 PROGRAMMING TECHNIQUES 201048 Detecting Convertibility and Inheritance at Compile Time Given two arbitrary types T and U that you know nothing about, how can you detect whether or not U inherits from T? How can you detect whether an arbitrary type T supports automatic conversion to an arbitrary type U?

49 PROGRAMMING TECHNIQUES 201049 Detecting Convertibility and Inheritance at Compile Time Solution relies on sizeof: it could be applied to any expression, no matter how complex, getting its size without actually evaluating that expression at runtime

50 PROGRAMMING TECHNIQUES 201050 Detecting Convertibility and Inheritance at Compile Time template class Conversion { typedef char Small; class Big { char dummy[2]; }; static Small Test(U); static Big Test(...); ….

51 PROGRAMMING TECHNIQUES 201051 Detecting Convertibility and Inheritance at Compile Time static T MakeT(); public: enum { exists = sizeof(Test(MakeT())) == sizeof(Small) }; };

52 PROGRAMMING TECHNIQUES 201052 Detecting Convertibility and Inheritance at Compile Time int main() { using namespace std; cout « Conversion ::exists « ' ' « Convesrion >::exists « ' '; }

53 PROGRAMMING TECHNIQUES 201053 Conversion We can implement one more constant inside Conversion: sameType, which is true if T and U represent the same type: template class Conversion {... as above... enum { sameType = false }; };

54 PROGRAMMING TECHNIQUES 201054 Conversion We implement sameType through a partial specialization of Conversion template class Conversion { public: enum { exists = 1, sameType = 1 }; };

55 PROGRAMMING TECHNIQUES 201055 SuperSubClass Macro //With the help of Conversion, it is now very //easy to determine inheritance #define SUPERSUBCLASS(T, U) \ (Conversion ::exists\ && \ !Conversion ::sameType);

56 PROGRAMMING TECHNIQUES 201056 SuperSubClass Macro SUPERSUBCLASS(T, U) evaluates to true if U inherits from T publicly, or if T and U are actually the same type. SUPERSUBCLASS does its job by evaluating the convertibility from a const U* to a const T*.

57 PROGRAMMING TECHNIQUES 201057 In the next slides: A wrapper around type_info NullType and EmptyType – the helpful helpers ;) …and finally: TypeTraits

58 PROGRAMMING TECHNIQUES 201058 Standard type_info class std::type_info gives the ability to investigate object types at runtime Hard to exploit: Disables the copy constructor and assignment operator, which makes storing type_info objects impossible. Does not guarantee that each invocation returns a reference to the same type_info object.

59 PROGRAMMING TECHNIQUES 201059 Loki wrapper class solution class TypeInfo { public: TypeInfo(); TypeInfo(const std::type_info&); TypeInfo(const TypeInfo&); TypeInfo& operator==(const TypeInfo&); bool before(const TypeInfo&) const; const char* name() const; private: const std::type_info* pInfo_; }

60 PROGRAMMING TECHNIQUES 201060 Loki wrapper class solution // Comparison operators bool operator==(const TypeInfo&, const TypeInfo&); bool operator!=(const TypeInfo&, const TypeInfo&); bool operator<(const TypeInfo&, const TypeInfo&); bool operator<=(const TypeInfo&, const TypeInfo&); bool operator>(const TypeInfo&, const TypeInfo&); bool operator>=(const TypeInfo&, const TypeInfo&);

61 PROGRAMMING TECHNIQUES 201061 Loki wrapper class solution // Now you can directly compare objects of type TypeInfo and std::type_info void Fun(Base* pObj) { TypeInfo info = typeid(Derived);... if (typeid(*pObj) == info) {... pBase actually points to a Derived object... }... }

62 PROGRAMMING TECHNIQUES 201062 Important features The ability to copy and compare TypeInfo objects is important in many situations. The cloning factory in and one double- dispatch engine provided in future seminars put TypeInfo to good use.

63 PROGRAMMING TECHNIQUES 201063 NullType & EmptyType helper Classes class NullType {}; Used for cases in which a type must be there syntactically but doesn't have a semantic sense. struct EmptyType {}; EmptyType is a legal type to inherit from, and you can pass around values of type EmptyType Will be used later in TypeLists

64 PROGRAMMING TECHNIQUES 201064 Type Traits Traits are a generic programming technique that allows compile-time decisions to be made based on types, much as you would make runtime decisions based on values “Extra level of indirection" that solves many software engineering problems Type-related decisions outside the immediate context in which they are made.

65 PROGRAMMING TECHNIQUES 201065 Copying application template OutIt Copy(Init first, Init last, OutIt result) { for (; first != last; ++first, ++result) *result = *first; }

66 PROGRAMMING TECHNIQUES 201066 Implementing Pointer Traits //T is not a pointer, and a pointee type doesn't apply. template class TypeTraits { private: template struct PointerTraits { enum(result = false }; typedef NullType PointeeType; }; …

67 PROGRAMMING TECHNIQUES 201067 Implementing Pointer Traits //A better match than the generic template for any pointer type. template struct PointerTraits { enum { result = true }; typedef U PointeeType; }; public: enum { isPointer = PointerTraits ::result }; typedef PointerTraits ::PointeeType PointeeType; };

68 PROGRAMMING TECHNIQUES 201068 Summary


Download ppt "PROGRAMMING TECHNIQUES Based on “Modern C++ Design” BY ALEXANDRESCU BOGDAN PENKOVSKY 2010."

Similar presentations


Ads by Google