forked from PhilippeSigaud/D-templates-tutorial
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtemplates_basics.tex
2212 lines (1660 loc) · 89.2 KB
/
templates_basics.tex
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
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
\newpage
\part{Basics}\label{basics}
A template is a recipe, a blueprint that will generate code at your command and according to compile-time parameters you'll give. Templates are \emph{parameterized code}. Each template definition is written once in a module and can then be instantiated many times with different parameters, possibly resulting in quite different code, depending on the arguments you used.
\section{Template Declarations}\label{declarations}
Here is the syntax for a template declaration:
\index{template!declaration}
\index{syntax!template declaration}
\begin{dcode}
template templateName(list, of, parameters)
{
// Some syntactically correct declarations here
// The arguments are accessible inside the template scope.
}
\end{dcode}
\DD{templateName} is your usual D identifier and the list of parameters is a comma-separated list of zero or more template parameters. These can be:
\begin{description}
\item[Types:]\index{template!parameters!type}
An \DD{identifier} alone by itself is considered a type name. The common D style is to use identifiers beginning with a capital letter (\DD{Range}, \DD{Rest}), as for any user-defined types. Many D templates use the C++\index{C++} tradition of one-capital-letter names for types, starting from \DD{T} (\DD{U}, \DD{V}, \ldots). Do not feel constrained by this, use what makes your templates most easy to understand.
\item[Aliases: ]\index{template!parameters!alias}
These will capture symbols: variable names, class names, even other template names. They will also accept many compile-time literals: strings, arrays, function literals, \ldots Mostly, if you need a widely-accepting template, use an alias parameter. They will \emph{not} accept built-in types as arguments, however. You declare them with \D{alias} \DD{identifier}.
\item[Literal values:]\index{template!parameters!literal value}
They can be integral values (\D{int}, \D{ulong}, \ldots), enum-based, strings, chars, floating-point values or boolean values. I think the rule is that any expression that can be evaluated at compile-time is OK. They are all declared like this: \D{typeName} \DD{iden\-ti\-fier}. For example : \D{int} \DD{depth} or \D{string} \DD{name}.
\item[Template parameters tuples:]\index{template!parameters!tuple}
Template parameters tuples will capture un\-der one id\-en\-ti\-fier an en\-ti\-re list of tem\-plate pa\-ra\-me\-ters (types, names, literals, \ldots). Tuples will store any template argument you will throw at them. If no argument is passed, you will just get a zero-length tuple. Really, as they can deal with types as well as symbols, these tuples are a bit of a mongrel type but they are wonderfully powerful and easy to use, as you will see in section \ref{tuples}. The syntax is \DD{identifier...} (yes, three dots) and the tuple must be the last parameter of a template.
\end{description}
Of those, types and aliases are the most common, while floating point values are fairly rare (their use as arguments for compile-time calculations have been superseded by D's Compile-Time Function Evaluation, aka CTFE\index{Compile-Time Function Evaluation|see{CTFE}}\index{CTFE}, described in section \ref{ctfe}). You'll see different uses of these parameters in this document.
Note that pointers, arrays, objects (instantiated classes), structs or functions are not part of this list. But as I said, alias parameters\index{template!parameters!alias} allow you to capture and use array, class, function or struct \emph{names} and then access their capacities.
\aparte{Aliases, Symbols and Names}{There is big difference between built-in types (like \D{int} or \D{double}\DD{[3]}) and user-defined types. A user-defined type, say a class called \DD{MyClass}, is a type name. So, it's \emph{both} a type (the class \DD{MyClass}, accepted by type templates arguments) and a name (\DD{MyClass}, accepted by \D{alias} template parameters). On the other hand, \D{int}, being a D keyword is not a symbol nor a name. It's just a type. You cannot pass it to an alias template parameter.}
The template body can contain any standard D declarations: variable, function, class, interface, other templates, alias declarations,\ldots The only exception I can think of is declaring a \D{module}\index{module}, as this is done at the top-level scope\index{scope!top-level}.
\aparte{Syntax and Semantics} {And then there is a catch: code inside a \D{template} declaration must only be syntactically correct D code (that is: code that looks like D code). The semantics are not checked until instantiation. That means you can code happily, writing templates upon templates and the compiler won't bat an eye if you do not exercise your templates by instantiating them.}
Inside the template body, the parameters are all accessible as placeholders for the future arguments. Also, the template's own name refers to its current instantiation when the code is generated. This is mostly used in struct (see section \ref{structtemplates}) and class (\ref{classtemplates}) templates.
Here are some template declaration examples:\index{template!definition}\label{templatedeclarationexamples}
\begin{ndcode}
module declaration;
template ArrayOf(T) // T is a type
{
alias T[] ArrayType;
alias T ElementType;
}
template Transformer(From, To) // From and To are types, too
{
To transform(From from)
{
import std.conv;
return to!(To)(from);
}
class Modificator
{
From f;
To t;
this(From f) { /*...*/ }
}
}
template nameOf(alias a)
{
enum string name = a.stringof; // enum: manifest constant
// determined at compile-time
}
template ComplicatedOne(T, string s, alias a, bool b, int i)
{ /* some code using T, s, a, b and i */ }
template Minimalist() {} // Zero-parameter template declaration.
template OneOrMore(FirstType, Rest...) // Rest is a tuple.
{ /*...*/ }
template ZeroOrMore(Types...) // Types is a tuple.
{ /*...*/ }
template Multiple(T) { /*...*/ } // One arg version.
template Multiple(T,U) { /*...*/ } // Two args,
template Multiple(T,U,V) { /*...*/ } // and three.
\end{ndcode}
The real syntax for template declarations is slightly more complex, I'll introduce more of it in the next sections (you'll see for example type restrictions in section \ref{specializations}, default values in section \ref{default}, instantiation constraints in \ref{constraints}, and more on tuples in section \ref{tuples}).
There is a limitation that's interesting to keep in mind: templates can be declared in almost any scope\index{scope}, except inside a (regular) function.
\aparte{\D{enum}}{In the previous code, see line 23? It defines a \D{string} called \DD{name} as a member of \DD{nameOf}. The \D{enum} placed right before means \DD{name} is a compile-time constant.\index{compile-time!constant (\D{enum})} You can see it as a kind of storage class,\index{storage class!enum as a kind of@\D{enum} as a kind of} in the line of \D{immutable} or \D{const}, one that means the value is totally defined and fixed at runtime. You'll see numerous examples of \D{enum} in this document.}
\section{Instantiating a Template}\label{instantiating}
To instantiate a template, use the following syntax:
\index{syntax!template instantiation}
\begin{dcode}
templateName!(list, of, arguments)
\end{dcode}
Note the `\DD{!}' before the comma-separated argument list. If the argument list contains only one argument (one token), you can drop the parenthesis:
\index{syntax!template instantiation}
\begin{dcode}
templateName!argument
\end{dcode}
\aparte{Templates as templates arguments\index{template!arguments!templates as}}{Arguments can themselves be the result of another template instantiation. If a template returns a type upon instantiation, it's perfectly OK to use it inside another template argument list. In this document you'll regularly see Matrioshka calls like this: \DD{firstTemp!(secondTempl!(Arguments), OtherArguments).}}
The compiler will have a look at the declarations (if more than one template were declared with the called name) and select the one with the correct number of arguments and the correct types to instantiate. If more than one template can be instantiated, it will complain and stop there (though, have a look on template specializations in section \ref{specializations} and template constraints in section \ref{constraints}).
When you instantiate a template, the global effect is that a new named scope\index{scope!named} is created in the template declaration scope\index{scope!template declaration}. The name of this new scope is the template name with its argument list: \DD{templateName!(args)}. Inside the scope, the parameters are now 'replaced' with the corresponding arguments (storage classes get applied, variables are initialized, \ldots). Here's what possible instantiations of the templates declared in section \ref{declarations} might look like:
\index{template!instantiation}
\begin{dcode}
module instantiation;
import declaration;
void main()
{
ArrayOf!(int).ArrayType myArray;
// From is an alias for the type double
// To for the type int
alias Transformer!(double,int) transfo;
struct MyStruct { /*...*/ }
// "MyStruct" is a identifier -> captured by alias
auto name = nameOf!(MyStruct).name;
alias
ComplicatedOne!( int[] // a type
, "Hello" // a string literal
, ArrayOf // a name (here the ArrayOf template)
, true // a boolean literal
, 1+2 // calculated to be the integral '3'.
) complicatedExample;
alias Minimalist!() min1;
alias
OneOrMore!( int // FirstType is int.
, double, string, "abc" // Rest is (double,string,"abc")
) oneOrMore;
alias ZeroOrMore!(int) zero1; // Types is a 1-element tuple: (int)
alias ZeroOrMore!(int,double,string) zero2; // Types is (int,double,string)
alias ZeroOrMore!() zero3; // Types is the empty tuple: ()
alias Multiple!(int) mult1; // Selects the one-arg version
alias Multiple!(int,double,string) mult2; // The three args version.
//alias Multiple!() mult3; // Error! No 0-arg version
}
\end{dcode}
Outside the scope\index{scope!template instantiation} (that is, where you put the template instantiation in your own code), the internal declarations are accessible by fully qualifying them:
\begin{dcode}
module internaldeclarations1;
import declaration;
// ArrayType is accessible (it's int[])
// array is a completly standard dynamic array of ints.
ArrayOf!(int).ArrayType array;
ArrayOf!(int).ElementType element; // the same, element is an int.
void main()
{
// the transform function is accessible. Instantiated like this,
// it's a function from double to string.
auto s = Transformer!(double,string).transform(3.14159);
assert(is(typeof(s) == string)); // s is a string
}
\end{dcode}
Obviously, using templates like this, with their full name, is a pain. The nifty D \D{alias} declaration is your friend:
\begin{dcode}
module internaldeclarations2;
import declaration;
alias Transformer!(double, string) StoD;
void main()
{
auto s = StoD.transform(3.14159);
auto m = new StoD.Modificator(1.618); // StoD.Modificator is a class
// storing a double and a string.
}
\end{dcode}
You must keep in mind that instantiating a template means generating code. Using different arguments at different places in your code will instantiate \emph{as many differently named scopes}\index{scope!multiple templates instantiations}. This is a major difference with \emph{generics}\index{generics} in languages like Java\index{Java} or C\#\index{C\#}, where generic code is created only once and type erasure is used to link all this together. On the other hand, trying to instantiate many times the `same' template (ie: with the same arguments) will only create one piece of code.
\begin{dcode}
module differentinstantiations;
import declaration;
alias Transformer!(string,double) StoD;
alias Transformer!(double,string) DtoS;
alias Transformer!(string,int) StoI;
// Now we can use three different functions and three different classes.
\end{dcode}
\aparte{\D{void}}{Note that \D{void}\index{void@\D{void}!as a template parameter}\index{template!parameters!void@\D{void}} is a D type and, as such, a possible template argument for a type parameter. Take care: many templates make no sense when \D{void} is used as a type. In the following sections and in the appendix, you'll see ways to restrict arguments to only certain types.}
\section{Template Building Blocks}\label{buildingblocks}
Up to now, templates may not seem that interesting to you, even with a simple declaration and instantiation syntax. But wait! D introduced a few nifty tricks that both simplify and greatly expand templates use. This section will introduce you to your future best friends, the foundations on which your templates will be built.
\subsection{The Eponymous Trick}\label{eponymous}
\index{eponymous trick}
If a template declares only one symbol with the same name (greek: \emph{epo-nymous}) as the enclosing template, that symbol is assumed to be referred to when the template is instantiated. This one is pretty good to clean your code:
\begin{dcode}
module pair;
template pair(T)
{
T[] pair(T t) { return [t,t];} // declares only pair
}
auto array = pair!(int)(1); // no need to do pair!(int).pair(1)
\end{dcode}
\begin{dcode}
module nameof1;
template nameOf(alias name)
{
enum string nameOf = name.stringof;
}
struct Example { int i;}
void main()
{
Example example;
auto s1 = nameOf!(Example);
auto s2 = nameOf!(example);
assert(s1 == "Example");
assert(s2 == "example");
}
\end{dcode}
A limitation is that the eponymous trick works \emph{only} if you define one (and only one) symbol. Even if the other symbols are private, they will break the eponymous substitution. This may change at a latter time, as the D developers have shown interest for changing this, but for the time being, you cannot do:
\begin{dcode}
module manyalias_error;
template ManyAlias(T, U)
{
T[] firstArray;
U[] secondArray;
T[U] ManyAlias; // Alas, ET substitution doesn't work here
}
void main()
{
// Error: Hoping for ManyAlias!(int,string).ManyAlias
ManyAlias!(int, string) = ["abc":0, "def":1];
ManyAlias!(int, string).firstArray = [0,1,2,3];
}
\end{dcode}
But, you will ask, what if I want client code to be clean and readable by using the eponymous trick, when at the same time I need to internally create many symbols in my template? In that case, create a secondary template with as many symbols as you need, and expose its final result through a (for example) \DD{result} symbol. The first template can instantiate the second one, and refer only to the \DD{.result} name:
\index{secondary template}
\label{impltrick}
\begin{dcode}
module record;
// primary template
template Record(T, U, V)
{
// eponymous trick activated!
alias RecordImpl!(T,U,V).result Record;
}
// secondary (hidden) template
template RecordImpl(T, U, V)
{
import std.typecons: Tuple, tuple;
// The real work is done here
// Use as many symbols as you need.
alias Tuple!(T,U) Pair;
alias Pair[V] AssocArray;
alias AssocArray[] result;
}
\end{dcode}
\begin{dcode}
module using_record;
import record;
Record!(int,string,double[]) recordslist;
/* ... */
\end{dcode}
You can find an example of this two-templates idiom\index{idiom!two templates} in Phobos, for example in \stdanchor{functional}{unaryFun} or \stdanchor{functional}{binaryFun}.
\subsection{\texorpdfstring{Inner \D{alias}}
{Inner alias}}
\label{inneralias}
A common use for templates is to do some type magics: deducing types, assembling them in new way, etc. Types are not first-class entities in D (there is no `\D{type}' type), but they can easily be manipulated as any other symbol, by aliasing them. So, when a template has to expose a type, it's done by aliasing it to a new name.
\index{exposing a type through an alias}
\begin{dcode}
module allarrays;
template AllArraysOf(T)
{
alias T Element;
alias T* PointerTo;
alias T[] DynamicArray;
alias T[1] StaticArray;
alias T[T] AssociativeArray;
}
\end{dcode}
\aparte{Exposing Template Parameters}{Though they are part of a template's name, its parameters are \emph{not} directly accessible externally. Keep in mind that a template name is just a scope name\index{scope}. Once it's instantiated, all the \DD{T}s and \DD{U}s and such do not exist anymore. If you need them externally, expose them through a template member, as is done with \DD{AllArraysOf.Element}. You will find other examples of this in section \ref{structtemplates} on struct templates and section \ref{classtemplates} on class templates.}
\subsection{\texorpdfstring{\D{static if}}
{static if}}
\label{staticif}
\subsubsection{Syntax}
The \D{static if} construct\footnote{ It's \emph{both} an statement and a declaration, so I'll call it a construct.}
lets you decide between two code paths at compile time. It's not specific to templates (you can use it in other part of your code), but it's incredibly useful to have your templates adapt themselves to the arguments. That way, using compile-time-calculated predicates based on the template arguments, you'll generate different code and customize the template to your need.
The syntax is:
\index{syntax!static if}
\begin{dcode}
static if (compileTimeExpression)
{
/* Code created if compileTimeExpression is evaluated to true */
}
else /* optional */
{
/* Code created if it's false */
}
\end{dcode}
Something really important here is a bit of compiler magic: once the code path is selected, the resulting code is instantiated in the template body, but without the curly braces. Otherwise that would create a local scope\index{scope!static if@\D{static if}}, hiding what's happening inside to affect the outside and would drastically limit the power of \D{static if}. So the curly braces are only there to group the statements together.
If there is only one statement, you can get rid of the braces entirely, something you'll see frequently in D code. For example, suppose you need a template that `returns' \D{true} if the passed type is a dynamic array and \D{false} otherwise (this kind of predicate template\index{template!predicate templates} is developed a bit more in section \ref{predicates}).
\begin{dcode}
module isdynamicarray;
template isDynamicArray(T)
{
static if (is(T t == U[], U))
enum isDynamicArray = true;
else
enum isDynamicArray = false;
}
\end{dcode}
As you can see, with no curly braces after \D{static if} and the eponymous trick\index{eponymous trick} (\DD{isDynamicArray} is the only symbol defined by the template and its type is automatically deduced by the compiler), results in a very clean syntax. The \D{is}\DD{()}\index{is expression@\D{is} expression} part is a way to get compile-time introspection\index{compile-time!introspection} which goes hand in hand with \D{static if}. There is a crash course on it at the end of this document (see \autoref{isexpression}).
\subsubsection{Optional Code}
A common use of \D{static if} is to enable or disable code: a single \D{static if} without an \D{else} clause will generate code only when the condition is true. You can find many examples of this idiom\index{idiom!enabling/disabling code} in \std{range}\index{range!higher-level ranges} where higher-level ranges (ranges wrapping other ranges) will activate some functionality if and only if the wrapped range can support it, like this:\index{enabling/disabling code}\index{optional code}
\begin{dcode}
/* We are inside a MyRange templated struct, wrapping a R. */
R innerRange;
/* Code that exists in all instantiations of MyRange */
(...)
/* optional code */
static if (hasLength!R) // does innerRange have a .length() method?
auto length() // Then MyRange has one also.
{
return innerRange.length;
}
static if (isInfinite!R) // Is innerRange an infinite range?
enum bool empty = false; // Then MyRange is also infinite.
// And so on...
\end{dcode}
\subsubsection{\texorpdfstring{Nested \D{static if}s}
{Nested static ifs}}
\D{static if}s can be nested: just put another \D{static if} after \D{else}. Here is a template selecting an alias:
\index{syntax!nested \D{static if}s}
\index{static if@\D{static if}!nested}
\begin{dcode}
module selector;
import std.traits: isIntegral, isFloatingPoint;
template selector(T, alias intFoo, alias floatFoo, alias defaultFoo)
{
static if (isIntegral!T)
alias intFoo selector;
else static if (isFloatingPoint!T)
alias floatFoo selector;
else // default case
alias defaultFoo selector;
}
\end{dcode}
If you need a sort of \D{static switch} construct, see section \ref{examples:staticswitch}.
\subsubsection{\texorpdfstring{Recursion with \D{static if}}
{Recursion with static if}}
\label{staticifrecursion}
\paragraph{Rank:}\label{rank}
Now, let's use \D{static if} for something a bit more complicated than just dispatching between code paths: recursion\index{recursion}. What if you know you will receive n-dimensional arrays\index{arrays} (simple arrays, arrays of arrays, arrays of arrays of arrays, \ldots), and want to use the fastest, super-optimized numerical function for the 1-dim array, another one for 2D arrays and yet another one for higher-level arrays. Abstracting this away, we need a template doing some introspection\index{compile-time!introspection} on types, that will return 0 for an element (anything that's not an array), 1 for a 1-dim array (\DD{T[]}, for some \DD{T}), 2 for a 2-dim array (\DD{T[][]}), and so on. Mathematicians call this the \emph{rank}\index{rank} of an array, so we will use that. The definition is perfectly recursive:
\index{rank!definition}
\index{template!recursion}
\begin{ndcode}
module rank1;
template rank(T)
{
static if (is(T t == U[], U)) // is T an array of U, for some type U?
enum rank = 1 + rank!(U); // then let's recurse down.
else
enum rank = 0; // Base case, ending the recursion.
}
\end{ndcode}
Line 6 is the most interesting: with some \D{is} magic, \DD{U} has been deduced by the compiler and is accessible inside the \D{static if} branch. We use it to peel one level of `\DD{[]}' off the type and recurse\index{recursion} downward, using \DD{U} as a new type for instantiating \DD{rank}. Either \DD{U} is itself an array (in which case the recursion will continue) or it will hit the base case and stop there.
\begin{dcode}
module using_rank1;
import rank1;
static assert(rank!(int) == 0);
static assert(rank!(int[]) == 1);
static assert(rank!(int[][]) == 2);
static assert(rank!(int[][][]) == 3);
/* It will work for any type, obviously */
struct S {}
static assert(rank!(S) == 0);
static assert(rank!(S[])== 1);
static assert(rank!(S*) == 0);
\end{dcode}
\aparte{\D{static assert}}{Putting \D{static} before an \D{assert} forces the \D{assert} execution at compile-time. Using an \D{is} expression as the test clause gives assertion on types.
One common use of \D{static assert} is to stop the compilation, for example if we ever get in a bad code path, by using \D{static assert}\DD{(0, someString)}. The string is then emitted as a compiler error message.}
\paragraph{Rank for Ranges:}\label{rankforranges}
D has an interesting sequence concept that's called \emph{range}\index{range} and comes with predefined testing templates in \std{range}. Why not extend \DD{rank}\index{rank} to have it deal with ranges and see if something is a range of ranges\index{range!of ranges} or more? A type can be tested to be a range with \DD{isInputRange} and its element type (the `\DD{U}') is obtained by applying \DD{ElementType} to the range type. Both templates are found in \std{range}. Also, since arrays are included in the range concept, we can entirely ditch the array part and use only ranges. Here is a slightly modified version of \DD{rank}:
\index{rank!for ranges}
\begin{dcode}
module rank2;
import std.range;
template rank(T)
{
static if (isInputRange!T) // is T a range?
enum rank = 1 + rank!(ElementType!T); // if yes, recurse
else
enum rank = 0; // base case, stop there
}
unittest
{
auto c = cycle([[0,1],[2,3]]); // == [[0,1],[2,3],[0,1],[2,3],[0,1]...
assert(rank!(typeof(c)) == 2); // range of ranges
}
\end{dcode}
\paragraph{Base Element Type:}\index{array!base element type}\index{range!base element type}
With \DD{rank}, we now have a way to get the number of \DD{[]}'s in an array type (\DD{T[][][]}) or the level of nesting in a range of ranges. The complementary query would be to get the base element type, \DD{T}, from any array of arrays \ldots of \DD{T} or the equivalent for a range. Here it is:
\begin{ndcode}
module baseelementtype;
import std.range;
import rank2;
template BaseElementType(T)
{
static if (rank!T == 0) // not a range
static assert(0, T.stringof ~ " is not a range.");
else static if (rank!T == 1) // simple range
alias ElementType!T BaseElementType;
else // at least range of ranges
alias BaseElementType!(ElementType!(T)) BaseElementType;
}
\end{ndcode}
Line 8 is an example of \D{static assert} stopping compilation if we ever get into a bad code path. Line 12 is an example of a Matrioshka call: a template using another template's call as its parameter.
\paragraph{Generating Arrays:}
Now, what about becoming more generative by inverting the process? Given a type \DD{T} and a rank \DD{r} (an \D{int}), we want to obtain \DD{T[][]...[]}, with \DD{r} levels of \DD{[]}'s. A rank of 0 means producing \DD{T} as the result type.
\begin{ndcode}
module ndim;
template NDimArray(T, int r)
{
static if (r < 0)
static assert(0, "NDimArray error: the rank must be positive.");
else static if (r == 0)
alias T NDimArray;
else // r > 0
alias NDimArray!(T, r-1)[] NDimArray;
}
\end{ndcode}
Here, recursion\index{recursion} is done on line 8: we instantiate \DD{NDimArray!(T,r-1)}, which is a type, then create an array\index{arrays} of them by putting \DD{[]} at the end and expose it through an alias. This is also a nice example of using an integral value\index{template!parameters!integral value} as a template parameter.
\begin{dcode}
module using_ndim;
import ndim;
alias NDimArray!(double, 8) Level8;
static assert(is(Level8 == double[][][][][][][][]));
static assert(is(NDimArray!(double, 0) == double));
\end{dcode}
\paragraph{Repeated composition:}
As a last example, we will use an alias template parameter\index{template!parameters!alias} in conjunction with some \D{static if} recursion\index{recursion}\index{static if@\D{static if}} to define a template that creates the `exponentiation' of a function, its repeated composition. Here is what I mean by this:
\begin{dcode}
module using_power;
import repeatedcomposition;
string foo(string s) { return s ~ s;}
Arr[] makeArray(Arr)(Arr array) { return [array,array];}
void main()
{
// power!(foo, n) is a function.
assert(power!(foo, 0)("a") == "a"); // identity function
assert(power!(foo, 1)("a") == foo("a")); // "aa"
assert(power!(foo, 2)("a") == foo(foo("a"))); // "aaaa"
assert(power!(foo, 3)("a") == foo(foo(foo("a")))); // "aaaaaaaa"
// It's even better with function templates:
assert(power!(makeArray, 0)(1) == 1);
assert(power!(makeArray, 1)(1) == [1,1]);
assert(power!(makeArray, 2)(1) == [[1,1],[1,1]]);
assert(power!(makeArray, 3)(1) == [[[1,1],[1,1]],[[1,1],[1,1]]]);
}
\end{dcode}
First, this is a template that `returns' (becomes, rather) a function. It's easy to do with the eponymous trick:\index{eponymous trick} just define inside the template a function with the same name. Secondly, it's clearly recursive in its definition, with two base cases: if the exponent is zero, then we shall produce the identity function and if the exponent is one, we shall just return the input function itself. That being said, \DD{power} writes itself:
\index{function composition}
\begin{ndcode}
module repeatedcomposition;
template power(alias fun, uint exponent)
{
static if (exponent == 0) // degenerate case -> id function
auto power(Args)(Args args) { return args; }
else static if (exponent == 1) // end-of-recursion case -> fun
alias fun power;
else
auto power(Args...)(Args args)
{
return .power!(fun, exponent-1)(fun(args));
}
}
\end{ndcode}
\aparte{.power}{If you are wondering what's with the \DD{.power} syntax on line 12, it's because by defining an eponymous template, we hide the parent template's name. So inside \DD{power(Args...)} it refers to \DD{power(Args...)} and not \DD{power(}\D{alias}\DD{ fun, }\D{uint}\DD{ exponent)}. Here we want a new \DD{power} to be generated so we call on the global \DD{power} template with the `global scope'\index{scope!global scope operator}\index{operator!global scope operator (\DD{.})} operator (\DD{.}).}
In all three branches of \D{static if}, \DD{power} exposes a \DD{power} member, activating the eponymous template trick\index{eponymous trick} and allowing for an easy use by the client. Note that this template will work not only for unary (one argument) functions but also for n-args functions\footnote{ Except for the degenerate, $n = 0$ case, since the identity function defined above accepts only one arg. A version with more than one argument is possible, but would need to return a tuple.}, for delegates and for structs or classes that define the \DD{()}(ie, \D{opCall}) operator\index{operator!opCall, ()@\DD{opCall}, \DD{()}} and for function templates\ldots\footnote{ I cheated a little bit there, because the resulting function accepts any number of arguments of any type, though the standard function parameters checks will stop anything untowards to happen. A cleaner (but longer, and for template functions, more complicated) implementation would propagate the initial function parameter typetuple.}
Now, are you beginning to see the power of templates?
\aparte{Curried Templates?}{\index{template!curried templates}No, I do not mean making them spicy, but separating the template's arguments, so as to call them in different places in your code. For \DD{power}, that could mean doing \D{alias }\DD{power!2 square;} somewhere and then using \DD{square!fun1}, \DD{square!fun2} at your leisure: the \DD{exponent} parameter and the \DD{fun} alias are separated. In fact, \DD{power} is already partially curried: \DD{fun} and \DD{exponent} are separated from \DD{Args}. For more on this, see section \ref{templatesintemplates}.
Given a template \DD{temp}, writing a \DD{curry} template that automatically generates the code for a curried version of \DD{temp} is \emph{also} possible, but outside the scope of this document.\index{scope!outside the scope of this document}}
\subsection{Templates Specializations}\label{specializations}
\index{template!specialization}
Up to now, when we write a \DD{T} in a template parameter list, there is no constraint on the type that \DD{T} can become during instantiation. Template specialization is a small `subsyntax', restricting templates instantiations to a subset of all possible types and directing the compiler into instantiating a particular version of a template instead of another. If you've read \autoref{isexpression} on the \D{is} expression, you already know how to write them. If you didn't, please do it now, as it's really the same syntax. These specializations are a direct inheritance from C++\index{C++} templates, up to the way they are written and they existed in D from the very beginning, long before \D{static if} or templates constraints were added.
The specializations are added in the template parameter list, the \DD{(T, U, V)} part of the template definition. \index{syntax!templates specialization}\DD{Type : OtherType} restricts \DD{Type} to be implicitly convertible into \DD{OtherType}.
\index{template!specialization}
\begin{dcode}
module specialization1;
template ElementType(T : U[], U) // can only be instantiated with arrays
{
alias U ElementType;
}
template ElementType(T : U[n], U, size_t n) // only with static arrays
{
alias U ElementType;
}
class Array { alias int ElementType;}
template ElementType(T : Array)
{
alias Array.ElementType ElementType;
}
\end{dcode}
Now, the idea may seem strange to you: if you know you want to restrict \DD{Type} to be \DD{AnotherType}, why make it a template parameter? It's because of templates specializations' main use: you can write different implementations of a template (with the same name, obviously), and when asked to instantiate one of them, the compiler will automatically decide which one to use, taking the `most adapted' to the provided arguments. `Most adapted' obeys some complicated rules you can find on the D Programming Language website, but they act in a natural way most of the time. The neat thing is that you can define the most general template \emph{and} some specialization. The specialized ones will be chosen when it's possible. For
\index{template!specialization}
\begin{dcode}
module specialization2;
template InnerType(T : U*, U) // Specialization for pointers
{
alias U InnerType;
}
template InnerType(T : U[], U) // Specialization for dyn. arrays
{ /*...*/ }
template InnerType(T) // Standard, default case
{ /*...*/ }
void main()
{
int* p;
int i;
alias InnerType!(typeof(p)) Pointer; // pointer spec. selected
alias InnerType!(typeof(i)) Default; // standard template selected
}
\end{dcode}
This idiom\index{idiom!template specialization} is used frequently in C++\index{C++}, where there is no (built-in) \D{static if} construct or template constraints. Oldish D templates used it a lot, too, but since other ways have been around for some years, recent D code seems to be more constraint-oriented: have a look at heavily templated Phobos modules, for example \std{algorithm} or \std{range}.
\aparte{Specializations or \D{static if} or Templates Constraints?}{Yes indeed. Let's defer this discussion for when we have seen all three subsystems.}
\subsection{Default Values}\label{default}
Like functions parameters, templates parameters can have default values. The syntax is the same: \DD{Param = defaultValue}. The default can be anything that makes sense with respect to the parameter kind: a type, a literal value, a symbol or another template parameter.
\begin{dcode}
module def;
template Default(T = int, bool flag = false)
{
static if (flag)
alias T Default;
else
alias void Default;
}
alias Default!(double) D1; // Instantiate Default!(double, false)
alias Default!(double, true) D2; // Instantiate Default!(double, true) (Doh!)
alias Default!() D3; // Instantiate Default!(int, false)
\end{dcode}
In contrast to function parameters, thanks to specializations (\ref{specializations}) or IFTI (\ref{ifti}), some template parameters can be automatically deduced by the compiler. So, default template parameters are not required to be the final parameters in the list:
\begin{dcode}
module deduced;
import std.typecons: Tuple;
template Deduced(T : U[], V = T, U)
{
alias Tuple!(T,U,V) Deduced;
}
alias Deduced!(int[], double) D1; // U deduced to be int. Force V to be a double.
alias Deduced!(int[]) D2; // U deduced to be int. V is int, too.
\end{dcode}
\aparte{Specialization and Default Value?}{Yes you can. Put the specialization first, then the default value. Like this: \DD{(T : U[] = int[], U)}. It's not commonly used, though.}
As for functions, well-chosen defaults can greatly simplify standard calls. See for example the \stdanchor{algorithm}{sort}. It's parameterized on a predicate and a swapping strategy, but both are adapted to what most people need when sorting. That way, most client uses of the template will be short and clean, but customization to their own need is still possible.
\TODO{Maybe something on template dummy parameters, like those used by \stdanchor{traits}{ReturnType}. Things like \DD{dummy == }\D{void}.}
\section{Function Templates}\label{functiontemplates}
\index{template!function templates}
\subsection{Syntax}\label{functiontemplatessyntax}
If you come from languages with generics\index{generics}, maybe you thought D templates were all about parameterized classes and functions and didn't see any interest in the previous sections (acting on types?). Fear not, you can also do type-generic functions and such in D, with the added generative power of templates.
As we have seen in section \ref{eponymous}, if you define a function inside a template and use the template's own name, you can call it easily:
\index{syntax!function templates}
\begin{dcode}
module function_declaration1;
import std.conv : to;
// declaration:
template myFunc(T, int n)
{
auto myFunc(T t) { return to!int(t) * n;}
}
void main()
{
// call:
auto result = myFunc!(double,3)(3.1415);
assert(result == to!int(3.1415)*3);
}
\end{dcode}
Well, the full story is even better. First, D has a simple way to declare a function template: just put a template parameter list before the argument list:
\index{syntax!function templates}
\index{function template}
\begin{dcode}
module function_declaration2;
import std.conv:to;
string concatenate(A,B)(A a, B b)
{
return to!string(a) ~ to!string(b);
}
Arg select(string how = "max", Arg)(Arg arg0, Arg arg1)
{
static if (how == "max")
return (arg0 < arg1) ? arg1 : arg0;
else static if (how == "min")
return (arg0 < arg1) ? arg0 : arg1;
else
static assert(0,
"select: string 'how' must be either \"max\" or \"min\".");
}
\end{dcode}
Nice and clean, uh? Notice how the return type can be templated too, using \DD{Arg} as a return type in \DD{select}.
\subsection{\texorpdfstring{\D{auto} return}
{auto return}}
\label{autoreturn}
Since you can select among code paths, the function return type can vary widely, depending on the template parameters you passed it. Use \D{auto} to simplify your code:\footnote{ \D{auto} return functions used to have some limitations, like for example not appearing in docs. I remember having to code type-manipulating templates to get correct the return type.}
\index{auto return@\D{auto} return}
\begin{dcode}
module morph;
// What morph will return will heavily depend on T and U
auto morph(alias f, T, U)(U arg)
{
static if (is(T == class))
return new T(f(arg));
else static if (is(T == struct))
return T(f(arg));
else
return; // void-returning function.
}
\end{dcode}
\aparte{\D{auto ref}}{ A function template can have an \D{auto ref} return type. That means that for templates where the returned values are lvalues, the template will get the \D{ref}ed version. And the non-\D{ref} version if not.}
\subsection{IFTI}\label{ifti}
Even better is Implicit Function Template Instantiation (IFTI)\index{IFTI}\index{Implicit Function Template Instantiation|see{IFTI}}, which means that the compiler will generally be able to automatically determine a template's parameters\index{template!parameters!IFTI} by studying the function arguments. If some template arguments are pure compile-time parameters, just provide them directly:
\begin{dcode}
module ifti;
import function_declaration2;
struct Foo {}
void main()
{
string res1 = concatenate(1, 3.14); // A is int and B is double
string res2 = concatenate("abc", Foo()); // A is string, B is Foo
auto res3 = select(3,4); // how is "max", Arg is int.
auto res4 = select!"min"(3.1416, 2.718); // how is "min", Arg is double.
}
\end{dcode}
As you can see, this results in very simple calling code. So we can both declare function templates and call them with a very clean syntax. The same can be done with structs or classes and such, as you will see in the next sections. In fact, the syntax is so clean that, if you are like me, you may forget from time to time that you are \emph{not} manipulating a function (or a struct, etc.): you are manipulating a template, a parameterized piece of code.
\aparte{A Mantra}{\label{mantra}\index{mantra}\DD{XXX} templates are not \DD{XXX}s, they are templates. With \DD{XXX} being any of (function, struct, class, interface, union). Templates are parameterized scopes\index{scope!scopes are not first-class in D} and scopes are not first-class in D: they have no type, they cannot be assigned to a variable, they cannot be returned from functions. That means, for example, that you \emph{cannot} return function templates, you cannot inherit from class templates and so on. Of course, \emph{instantiated} templates are perfect examples of functions, classes, and such. Them you can inherit, return\ldots}
We may encounter The Mantra again in this tutorial.
\subsection{Example: Flattening Arrays and Ranges}\label{functionflatten}
Let's use what we have just seen in a concrete way. In D, you can manipulate 2D and 3D arrays\index{arrays}, but sometimes need to process them linearly. As of this writing, neither \std{algorithm} nor \std{range} provide a \DD{flatten} function. Beginning with simple arrays, here is what we want:
\begin{dcode}
module using_flatten1;
import flatten1;
void main()
{
assert( flatten([[0,1],[2,3],[4]]) == [0,1,2,3,4] );
assert( flatten([[0,1]]) == [0,1] );
assert( flatten([0,1]) == [0,1] );
assert( flatten(0) == 0 );
assert( flatten([[[0,1],[]], [[2]], [[3], [4,5]], [[]], [[6,7,8]]])
== [0,1,2,3,4,5,6,7,8] );
}
\end{dcode}
So, studying the examples, we want a simple array (rank == 1) or a non-array (rank == 0) to be unaffected by \DD{flatten}: it just returns them. For arrays of rank\index{rank} 2 or higher, it collapses the elements down to a rank-1 array. It's classically recursive:\index{recursion} we will apply \DD{flatten} on all sub-arrays with \stdanchor{algorithm}{map} and concatenate the elements with \stdanchor{algorithm}{reduce}:
\begin{dcode}
module flatten1;
import std.algorithm;
import rank2;
auto flatten(Arr)(Arr array)
{
static if (rank!Arr <= 1)
return array;
else
{
auto children = map!(.flatten)(array);
return reduce!"a~b"(children); // concatenate the children
}
}
\end{dcode}
We make good use of D \D{auto} return parameter for functions there. In fact, a single call to \DD{flatten} will create one instance per level, all with a different return type.
Note that \DD{flatten} works perfectly on ranges\index{range} too, but is not lazy: it eagerly concatenates all the elements down to the very last one in the innermost range. Ranges being lazy, a good \DD{flatten} implementation for them should itself be a range that delivers the elements one by one, calculating the next one only when asked to (and thus, would work on infinite or very long ranges too, which the previous simple implementation cannot do). Implementing this means creating a struct template (\ref{structtemplates}) with a factory function (\ref{factory}). You will find this as an example in section \ref{structflatten}.
From our current \DD{flatten}, it's an interesting exercise to add another parameter: the number of levels you want to flatten. Only the first three levels or last two innermost, for example. Just add an integral template parameter that gets incremented (or decremented) when you recurse and is another stopping case for the recursion. Positive levels could mean the outermost levels, while a negative argument would act on the innermost ones. A possible use would look like this:
\begin{dcode}
flatten!1([[[0,1],[]], [[2]], [[3], [4,5]], [[]], [[6,7,8]]]);
// == [[[0,1],[],[2],[3], [4,5],[],[6,7,8]]
flatten!2([[[0,1],[]], [[2]], [[3], [4,5]], [[]], [[6,7,8]]]);
// == [0,1,2,3,4,5,6,7,8]
flatten!0([[[0,1],[]], [[2]], [[3], [4,5]], [[]], [[6,7,8]]]);
// ==[[[0,1],[]], [[2]], [[3], [4,5]], [[]], [[6,7,8]]]
flatten!(-1)([[[0,1],[]], [[2]], [[3], [4,5]], [[]], [[6,7,8]]]);
// ==[[[0,1]], [[2]], [[3,4,5]], [[]], [[6,7,8]]]
\end{dcode}
\subsection{Anonymous Function Templates}\label{anonymousfunctions}
In D, you can define anonymous functions\index{anonymous!functions} (delegates even, that is: closures):
\begin{dcode}
module anonymous_function1;
auto adder(int a)
{
return (int b) { return a+b;};
}
unittest
{
auto add1 = adder(1); // add1 is an int delegate(int)
assert(add1(2) == 3);
}
\end{dcode}
In the previous code, \DD{adder} returns an anonymous delegate. Could \DD{Adder} be templated? Ha! Remember The Mantra (page \pageref{mantra}):\index{mantra} function templates are templates and cannot be returned. For this particular problem, there are two possible solutions. Either you do not need any new type and just use \DD{T}:
\index{returning a function}
\begin{dcode}
module anonymous_function2;
auto adder(T)(T a)
{
return (T b) { return a+b;};
}
unittest
{
auto add1f = adder(1.0); // add1f is an float delegate(float)
assert(add1f(2.0) == 3.0);
import std.bigint;
// addBigOne accepts a BigInt and returns a BigInt
auto addBigOne = adder(BigInt("1000000000000000"));
assert(addBigOne(BigInt("1")) == BigInt("1000000000000001"));
// But:
// auto error = add1(3.14); // Error! Waiting for an int, getting a double.
}
\end{dcode}
In the previous example, the returned anonymous delegate\index{anonymous!delegates} is \emph{not} templated. It just happens to use \DD{T}, which is perfectly defined once instantiation is done. If you really need to return something that can be called with any type, use an inner struct (see section \ref{innerstructs}).
Now, it may come as a surprise to you that D \emph{does} have anonymous function templates\index{function templates!anonymous function templates}\index{template!function templates!anonymous function templates}\index{anonymous!function templates}. The syntax is a purified version of anonymous functions:
\index{syntax!anonymous function templates}
\begin{dcode}
(a,b) { return a+b;}
\end{dcode}
Yes, the previous skeleton of a function is an anonymous template. But, remember The Mantra:\index{mantra} you cannot return them. And due to (to my eyes) a bug in \D{alias} grammar, you cannot alias them to a symbol:
\begin{dcode}
alias (a){ return a;} Id; // Error
\end{dcode}
So what good are they? You can use them with template alias parameters, when these stand for functions and function templates:
\begin{dcode}
module calltwice;
template callTwice(alias fun)
{
auto callTwice(T)(T t)
{
return fun(fun(t));
}
}
unittest
{
alias callTwice!( (a){ return a+1;}) addTwo;
assert(addTwo(2) == 4);
}
\end{dcode}
Since they are delegates, they can capture local symbols:
\begin{dcode}
module using_calltwice;
import calltwice;
unittest
{
enum b = 3; // Manifest constant, initialized to 3
alias callTwice!( (a){ return a+b;}) addTwoB;
assert(addTwoB(2) == 2 + 3 + 3);
}
\end{dcode}
\subsection{Closures are a Poor Man's Objects}\label{closuresareapoormansobjects}
D closures can wrap runtime environment and keep them near their hot little hearts. We can of course create them with a template. To obtain the equivalent of an object, let's create a function that returns a tuple (a \stdanchor{typecons}{Tuple}) with named arguments.
\begin{dcode}
module makecounter;
import std.traits;
import std.typecons;
auto makeCounter(T)(T _counter = T.init) if (isNumeric!T)
{
bool sense = true;
auto changeSense = () { sense = !sense;};
auto inc = (T increment) { _counter += (sense ? increment : -increment); };
auto dec = (T decrement) { _counter += (sense ? -decrement : decrement); };
auto counter = () { return _counter;};
return Tuple!( typeof(changeSense), "changeSense"
, typeof(inc), "inc"
, typeof(dec), "dec"
, typeof(counter), "counter")(changeSense, inc, dec, counter);
}
\end{dcode}
The \D{if} part after the argument list is just sanity-check template constraint (they are described in \autoref{constraints}). The returned \DD{Tuple} is a bit heavy for my taste, but using named tuple fields gives us a nice object-like call syntax.\footnote{ See the example in section \ref{namedfieldstuples} for a way to extend Phobos' \DD{tuple}.} Otherwise, a simple \D{return }\DD{tuple(changeSense, inc, dec, counter);} could have been used but then the inner closures would have to be accessed by their index and not a name.
Here is how it's used:
\begin{dcode}
module using_makecounter;
import makecounter;
void main()
{