File: multi.yo

package info (click to toggle)
c%2B%2B-annotations 13.02.02-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 13,576 kB
  • sloc: cpp: 25,297; makefile: 1,523; ansic: 165; sh: 126; perl: 90; fortran: 27
file content (83 lines) | stat: -rw-r--r-- 5,021 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
To illustrate template meta programming concepts the template class ti(Multi)
is now developed. The class template tt(Multi) em(creates) a new class from a
template template parameter tt(Policy) defining the data storage policy and a
series of types from which tt(Multi) is eventually derived. It does so by
passing its template parameters to its base class tt(MultiBase) that in turn
creates a final class inheritance tree. Since we don't know how many types are
going to be used tt(Multi) is defined as a variadic class template using a
template pack tt(...Types).

    In fact, the types that are specified with tt(Multi) aren't that
interesting. They primarily serve to `seed' the class tt(Policy). Therefore,
rather than forwarding tt(Multi)'s types to tt(MultiBase) they are passed to
tt(Policy) and the sequence of tt(Policy<Type>) types is then forwarded to
tt(MultiBase). tt(Multi)'s constructor expects initialization values for its
various tt(Policy<Type>)s which are perfectly forwarded to tt(MultiBase).

    The class tt(Multi) (implementing its constructor in-class to save some
space) shows how a
    hi(template pack: and template template parameters)
    hi(template template parameter: and template packs)
 template pack can be wrapped into a policy. Here is tt(Multi)'s definition:
    verbinclude(//MULTI examples/multi.h)

Unfortunately, the design as described contains some flaws.
    itemization(
    it() As the tt(Policy) template template parameter is defined as
tt(template <typename> class Policy) it can only accept policies expecting one
type argument. Contrary to this, tt(std::vector) is a template expecting two
template arguments, the second one defining the allocation scheme used by
tt(std::vector). This allocation scheme is hardly ever changed, and most
applications merely define objects of types like tt(vector<int>,
vector<string>) etc.. Template template parameters must, however, be specified
with the correct number and types of required template parameters so
tt(vector) can't be specified as a policy for tt(Multi). This can be solved by
    hi(class template: wrapped in simpler template)
em(wrapping) a more complex template in a simpler wrapper template, like so:
        verbinclude(//VECTOR examples/multi.h)
    Now tt(Vector) provides tt(std::vector)'s second parameter using its
default template argument. Alternatively, a em(template using declaration)
could be used.
    it() If the tt(TypeList) contains two types like tt(int) and tt(double)
and the policy class is tt(Vector), then the tt(MultiBase) class
eventually inherits from tt(vector<int>) and tt(vector<double>). But if the
tt(TypeList) contains identical types, like two tt(int) type specifications
tt(MultiBase) would inherit from em(two) tt(vector<int>) classes. Classes
cannot be derived from identical base classes as that would make it impossible
to distinguish among their members. Regarding this, Alexandrescu (2001)
    hi(Alexandrescu, A.) writes (p.67):
    quote(
   em(There is one major source of annoyance...: you cannot use it when you
    have duplicate types in your tt(TypeList).nl()
   .... There is no easy way to solve the ambiguity, [as the eventually
    derived class/FBB] ends up inheriting [the same base class/FBB] twice.)
   )
    )
    There is a way around the problem of duplicate base class types. If
instead of inheriting directly from base classes these base classes are first
wrapped in unique type defining classes, then these unique classes can be used
to access the base classes using principles of inheritance. As these unique
type-defining wrapper classes are merely classes that are derived from the
`real' base classes they inherit (and thus: offer) the functionality of their
base classes. A unique type defining wrapper class can be designed after the
class link(IntType)(INTTYPE), defined earlier.  The wrapper class we're
looking combines class derivation with the uniqueness offered by
tt(IntType). The class template tt(UWrap) has two template parameters: one
non-type parameter tt(idx) and one type parameter. By ensuring that each
tt(UWrap) definition uses a unique tt(idx) value unique class types are
created. These unique class types are then used as base classes of the derived
class tt(MultiBase):
    verbinclude(//UWRAP examples/multi.h)
    Using tt(UWrap) it's easy to distinguish, e.g., two tt(vector<int>)
classes: tt(UWrap<0, vector<int>>) could refer to the first tt(vector<int>),
tt(UWrap<1, vector<int>>) to the second vector.

    Uniqueness of the various tt(UWrap) types is assured by the class
template tt(MultiBase) as discussed in the next section.

    It must also be possible to initialize a  tt(Multi) class object. Its
constructor therefore expects the initialization values for all its tt(Policy)
values. So if a tt(Multi) is defined for tt(Vector, int, string) then its
constructor can receive the matching initialization values. E.g.,
        verb(    Multi<Vector, int, string> mvis({1, 2, 3}, {"one", "two", "three"});)