root/trunk/docsrc/variadic-function-templates.dd

Revision 659, 6.1 kB (checked in by andrei, 4 years ago)

Changed ddoc extensions to dd and changed linux.mak accordingly

  • Property svn:eol-style set to native
Line 
1 Ddoc
2
3 $(D_S Variadic Templates,
4
5     $(P The problem statement is simple: write a function that
6     takes an arbitrary number of values of arbitrary types,
7     and print those values out one per line in a manner
8     appropriate to the type. For example, the code:
9     )
10
11 ----
12 print(7, 'a', 6.8);
13 ----
14
15     $(P should output:
16     )
17
18 $(CONSOLE
19 7
20 'a'
21 6.8
22 )
23
24     $(P We'll explore how this can
25     be done in standard C++, followed by doing it using
26     the proposed variadic template C++ extension.
27     Then, we'll do it the various ways the D programming
28     language makes possible.
29     )
30
31 <h3>C++ Solutions</h3>
32
33 <h4>The Standard C++ Solution</h4>
34
35     $(P The straightforward way to do this in standard C++
36     is to use a series of function templates, one for
37     each number of arguments:
38     )
39
40 $(CCODE
41 #include &lt;iostream&gt;
42 using namespace::std;
43
44 void print()
45 {
46 }
47
48 template&lt;class T1&gt; void print(T1 a1)
49 {
50     cout &lt;&lt; a1 &lt;&lt; endl;
51 }
52
53 template&lt;class T1, class T2&gt; void print(T1 a1, T2 a2)
54 {
55     cout &lt;&lt; a1 &lt;&lt; endl;
56     cout &lt;&lt; a2 &lt;&lt; endl;
57 }
58
59 template&lt;class T1, class T2, class T3&gt; void print(T1 a1, T2 a2, T3 a3)
60 {
61     cout &lt;&lt; a1 &lt;&lt; endl;
62     cout &lt;&lt; a2 &lt;&lt; endl;
63     cout &lt;&lt; a3 &lt;&lt; endl;
64 }
65
66 ... etc ...
67 )
68
69     $(P This poses significant problems:
70     )
71
72     $(P One, the function
73     implementor must decide in advance what the maximum number
74     of arguments the function will have. The implementor will
75     usually err on the side of excess, and ten, or even twenty
76     overloads of print() will be written. Yet inevitably, some
77     user somewhere will require just one more argument.
78     So this solution is never quite thoroughly right.
79     )
80
81     $(P Two, the logic of the function template body must be
82     cut and paste duplicated, then carefully modified,
83     for every one of those function templates. If the logic
84     needs to be adjusted, all of those function templates must
85     receive the same adjustment, which is tedious and error
86     prone.
87     )
88
89     $(P Three, as is typical for function overloads, there is no
90     obvious visual connection between them, they stand independently.
91     This makes it more difficult to understand the code,
92     especially if the implementor isn't careful to place them
93     and format them in a consistent style.
94     )
95
96     $(P Four, it leads to source code bloat which slows down
97     compilation.
98     )
99
100 <h4>The C++ Extension Solution</h4>
101
102     $(P Douglas Gregor has proposed a
103     variadic template scheme [1]
104     for C++ that solves these problems.
105     The result looks like:
106     )
107
108 $(CCODE
109 void print()
110 {
111 }
112
113 template&lt;class T, class... U&gt; void print(T a1, U... an)
114 {
115     cout &lt;&lt; a1 &lt;&lt; newline;
116     print(an...);
117 }
118 )
119
120     $(P It uses recursive function template instantiation
121     to pick off the arguments one by one.
122     A specialization with no arguments ends the recursion.
123     It's a neat and tidy solution, but with one glaring problem:
124     it's a proposed extension, which means it isn't part
125     of the C++ standard, may not get into the C++ standard
126     in its current form, may not get into the standard
127     in any form, and even if it does, it may be many, many
128     years before the feature is commonly implemented.
129     )
130
131 <h3>D Programming Language Solutions</h3>
132
133 <h4>The D Look Ma No Templates Solution</h4>
134
135     $(P It is not practical to solve this problem in C++ without
136     using templates. In D, one can because D supports typesafe
137     variadic function parameters.
138     )
139
140 ------
141 import std.stdio;
142
143 void print(...)
144 {
145     foreach (arg; _arguments)
146     {
147     writefx(stdout, (&arg)[0 .. 1], _argptr, 1);
148     auto size = arg.tsize();
149     _argptr += ((size + size_t.sizeof - 1) & ~(size_t.sizeof - 1));
150     }
151 }
152 ------
153
154     $(P It isn't elegant or the most efficient,
155     but it does work, and it is neatly
156     encapsulated into a single function.
157     (It relies on the predefined parameters _argptr and _arguments
158     which give a pointer to the values and their types, respectively.)
159     )
160
161 <h4>Translating the Variadic C++ Solution into D</h4>
162
163     $(P Variadic templates in D enable a straightforward translation
164     of the proposed C++ variadic syntax:
165     )
166
167 ---
168 void print()()
169 {
170 }
171
172 void print(T, A...)(T t, A a)
173 {
174     writefln(t);
175     print(a);
176 }
177 ---
178
179     $(P There are two function templates. The first provides the
180     degenerate case of no arguments, and a terminus for the
181     recursion of the second. The second has two arguments:
182     t for the first value and a for the rest of the values.
183     A... says the parameter is a tuple, and implicit function
184     template instantiation will fill in A with the list of
185     all the types following t. So, print(7, 'a', 6.8) will
186     fill in int for T, and a tuple (char, double) for A.
187     The parameter a becomes an expression tuple of the arguments.
188     )
189
190     $(P The function works by printing the first parameter t,
191     and then recursively calling itself with the remaining arguments
192     a. The recursion terminates when there are no longer any
193     arguments by calling print()().
194     )
195
196 <h4>The Static If Solution</h4>
197
198     $(P It would be nice to encapsulate all the logic into a
199     single function. One way to do that is by using
200     static if's, which provide for conditional compilation:
201     )
202
203 ---
204 void print(A...)(A a)
205 {
206     static if (a.length)
207     {
208     writefln(a[0]);
209     static if (a.length > 1)
210         print(a[1 .. length]);
211     }
212 }
213 ---
214
215     $(P Tuples can be manipulated much like arrays.
216     So a.length resolves to the number of expressions
217     in the tuple a. a[0] gives the first expression
218     in the tuple. a[1 .. length] creates a new tuple
219     by slicing the original tuple.
220     )
221
222 <h4>The Foreach Solution</h4>
223
224     $(P But since tuples can be manipulated like arrays,
225     we can use a foreach statement to 'loop' over
226     the tuple's expressions:
227     )
228
229 ---
230 void print(A...)(A a)
231 {
232     foreach(t; a)
233     writefln(t);
234 }
235 ---
236
237     $(P The end result is remarkably simple, self-contained, compact
238     and efficient.
239     )
240
241 <h3>Acknowledgments</h3>
242
243     $(OL
244
245     $(LI Thanks to Andrei Alexandrescu for explaining to me how
246     variadic templates need to work and why they are so important.
247     )
248
249     $(LI Thanks to Douglas Gregor, Jaakko Jaervi, and Gary Powell
250     for their inspirational work
251     on C++ variadic templates.
252     )
253
254     )
255
256 <h3>References</h3>
257
258     $(OL
259
260     $(LI $(LINK2 http://www.osl.iu.edu/~dgregor/cpp/variadic-templates.pdf, Variadic Templates N2080))
261
262     )
263
264 )
265
266 Macros:
267     TITLE=Variadic Templates
268     WIKI=VariadicTemplates
Note: See TracBrowser for help on using the browser.