Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
branch: master
Fetching contributors…

Cannot retrieve contributors at this time

file 271 lines (213 sloc) 6.266 kb
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 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271
Ddoc

$(D_S Variadic Templates,

$(P The problem statement is simple: write a function that
takes an arbitrary number of values of arbitrary types,
and print those values out one per line in a manner
appropriate to the type. For example, the code:
)

----
print(7, 'a', 6.8);
----

$(P should output:
)

$(CONSOLE
7
'a'
6.8
)

$(P We'll explore how this can
be done in standard C++, followed by doing it using
the proposed variadic template C++ extension.
Then, we'll do it the various ways the D programming
language makes possible.
)

<h3>C++ Solutions</h3>

<h4>The Standard C++ Solution</h4>

$(P The straightforward way to do this in standard C++
is to use a series of function templates, one for
each number of arguments:
)

$(CCODE
#include &lt;iostream&gt;
using namespace::std;

void print()
{
}

template&lt;class T1&gt; void print(T1 a1)
{
    cout &lt;&lt; a1 &lt;&lt; endl;
}

template&lt;class T1, class T2&gt; void print(T1 a1, T2 a2)
{
    cout &lt;&lt; a1 &lt;&lt; endl;
    cout &lt;&lt; a2 &lt;&lt; endl;
}

template&lt;class T1, class T2, class T3&gt; void print(T1 a1, T2 a2, T3 a3)
{
    cout &lt;&lt; a1 &lt;&lt; endl;
    cout &lt;&lt; a2 &lt;&lt; endl;
    cout &lt;&lt; a3 &lt;&lt; endl;
}

... etc ...
)

$(P This poses significant problems:
)

$(P One, the function
implementor must decide in advance what the maximum number
of arguments the function will have. The implementor will
usually err on the side of excess, and ten, or even twenty
overloads of print() will be written. Yet inevitably, some
user somewhere will require just one more argument.
So this solution is never quite thoroughly right.
)

$(P Two, the logic of the function template body must be
cut and paste duplicated, then carefully modified,
for every one of those function templates. If the logic
needs to be adjusted, all of those function templates must
receive the same adjustment, which is tedious and error
prone.
)

$(P Three, as is typical for function overloads, there is no
obvious visual connection between them, they stand independently.
This makes it more difficult to understand the code,
especially if the implementor isn't careful to place them
and format them in a consistent style.
)

$(P Four, it leads to source code bloat which slows down
compilation.
)

<h4>The C++ Extension Solution</h4>

$(P Douglas Gregor has proposed a
variadic template scheme [1]
for C++ that solves these problems.
The result looks like:
)

$(CCODE
void print()
{
}

template&lt;class T, class... U&gt; void print(T a1, U... an)
{
    cout &lt;&lt; a1 &lt;&lt; newline;
    print(an...);
}
)

$(P It uses recursive function template instantiation
to pick off the arguments one by one.
A specialization with no arguments ends the recursion.
   It's a neat and tidy solution, but with one glaring problem:
it's a proposed extension, which means it isn't part
of the C++ standard, may not get into the C++ standard
in its current form, may not get into the standard
in any form, and even if it does, it may be many, many
years before the feature is commonly implemented.
)

<h3>D Programming Language Solutions</h3>

<h4>The D Look Ma No Templates Solution</h4>

$(P It is not practical to solve this problem in C++ without
using templates. In D, one can because D supports typesafe
variadic function parameters.
)

------
import std.stdio;

void print(...)
{
    foreach (arg; _arguments)
    {
writefx(stdout, (&arg)[0 .. 1], _argptr, 1);
auto size = arg.tsize();
_argptr += ((size + size_t.sizeof - 1) & ~(size_t.sizeof - 1));
    }
}
------

$(P It isn't elegant or the most efficient,
but it does work, and it is neatly
encapsulated into a single function.
(It relies on the predefined parameters _argptr and _arguments
which give a pointer to the values and their types, respectively.)
)

<h4>Translating the Variadic C++ Solution into D</h4>

$(P Variadic templates in D enable a straightforward translation
of the proposed C++ variadic syntax:
)

---
void print()()
{
}

void print(T, A...)(T t, A a)
{
    writefln(t);
    print(a);
}
---

$(P There are two function templates. The first provides the
degenerate case of no arguments, and a terminus for the
recursion of the second. The second has two arguments:
t for the first value and a for the rest of the values.
A... says the parameter is a tuple, and implicit function
template instantiation will fill in A with the list of
all the types following t. So, print(7, 'a', 6.8) will
fill in int for T, and a tuple (char, double) for A.
The parameter a becomes an expression tuple of the arguments.
)

$(P The function works by printing the first parameter t,
and then recursively calling itself with the remaining arguments
a. The recursion terminates when there are no longer any
arguments by calling print()().
)

<h4>The Static If Solution</h4>

$(P It would be nice to encapsulate all the logic into a
single function. One way to do that is by using
static if's, which provide for conditional compilation:
)

---
void print(A...)(A a)
{
    static if (a.length)
    {
writefln(a[0]);
static if (a.length > 1)
print(a[1 .. length]);
    }
}
---

$(P Tuples can be manipulated much like arrays.
So a.length resolves to the number of expressions
in the tuple a. a[0] gives the first expression
in the tuple. a[1 .. length] creates a new tuple
by slicing the original tuple.
)

<h4>The Foreach Solution</h4>

$(P But since tuples can be manipulated like arrays,
we can use a foreach statement to 'loop' over
the tuple's expressions:
)

---
void print(A...)(A a)
{
    foreach(t; a)
writefln(t);
}
---

$(P The end result is remarkably simple, self-contained, compact
and efficient.
)

<h3>Acknowledgments</h3>

$(OL

$(LI Thanks to Andrei Alexandrescu for explaining to me how
variadic templates need to work and why they are so important.
)

$(LI Thanks to Douglas Gregor, Jaakko Jaervi, and Gary Powell
for their inspirational work
on C++ variadic templates.
)

)

<h3>References</h3>

$(OL

$(LI $(LINK2 http://www.osl.iu.edu/~dgregor/cpp/variadic-templates.pdf, Variadic Templates N2080))

)

)

Macros:
TITLE=Variadic Templates
WIKI=VariadicTemplates
CATEGORY_ARTICLES=$0


Something went wrong with that request. Please try again.