1 module it.cpp.templates;
2 
3 import it;
4 
5 @("simple")
6 @safe unittest {
7     shouldCompile(
8         Cpp(
9             q{
10                 template<typename T>
11                 struct vector {
12                 public:
13                     T value;
14                     void push_back();
15                 };
16 
17                 template<typename U, int length>
18                 struct array {
19                 public:
20                     U elements[length];
21                 };
22 
23             }
24         ),
25         D(
26             q{
27                 auto vi = vector!int(42);
28                 static assert(is(typeof(vi.value) == int));
29                 vi.value = 33;
30 
31                 auto vf = vector!float(33.3);
32                 static assert(is(typeof(vf.value) == float));
33                 vf.value = 22.2;
34 
35                 auto vs = vector!string("foo");
36                 static assert(is(typeof(vs.value) == string));
37                 vs.value = "bar";
38 
39                 auto ai = array!(int, 3)();
40                 static assert(ai.elements.length == 3);
41                 static assert(is(typeof(ai.elements[0]) == int));
42             }
43         ),
44    );
45 }
46 
47 @("template nameless type")
48 @safe unittest {
49     shouldCompile(
50         Cpp(
51             q{
52                 // none of the template parameters have names, which is allowed
53                 // in C++ but not in D
54                 template<bool, bool, typename>
55                 struct Foo {
56 
57                 };
58             }
59         ),
60         D(
61             q{
62                 auto f = Foo!(true, false, int)();
63             }
64         ),
65     );
66 }
67 
68 @("struct full specialisation")
69 @safe unittest {
70     shouldCompile(
71         Cpp(
72             q{
73                 // this is a ClassTemplate
74                 template<bool, bool, typename>
75                 struct __copy_move {
76                     enum { value = 42 };
77                 };
78 
79                 // This is a StructDecl
80                 template<>
81                 struct __copy_move<false, true, double> {
82                     enum { value = 33 };
83                 };
84             }
85         ),
86         D(
87             q{
88                 import std.conv: text;
89 
90                 // FIXME: libclang bug - templates don't have proper
91                 // EnumConstantDecl values for some reason
92                 // auto c1 = __copy_move!(true, true, int)();
93                 // static assert(c1.value == 42, text(cast(int) c1.value));
94 
95                 auto c2 = __copy_move!(false, true, double)();
96                 static assert(c2.value == 33, text(cast(int) c2.value));
97             }
98         ),
99     );
100 }
101 
102 // struct/class keyword could end up in different code paths
103 @("class full specialisation")
104 @safe unittest {
105     shouldCompile(
106         Cpp(
107             q{
108                 // this is a ClassTemplate
109                 template<bool, bool, typename>
110                 class __copy_move {
111                 public:
112                     enum { value = 42 };
113                 };
114 
115                 // This is a ClassDecl
116                 template<>
117                 class __copy_move<false, true, double> {
118                 public:
119                     enum { value = 33 };
120                 };
121             }
122         ),
123         D(
124             q{
125                 import std.conv: text;
126 
127                 // FIXME: libclang bug - templates don't have proper
128                 // EnumConstantDecl values for some reason
129                 // auto c1 = __copy_move!(true, true, int)();
130                 // static assert(c1.value == 42, text(cast(int) c1.value));
131 
132                 auto c2 = __copy_move!(false, true, double)();
133                 static assert(c2.value == 33, text(cast(int) c2.value));
134             }
135         ),
136     );
137 }
138 
139 @("struct partial specialisation")
140 @safe unittest {
141     shouldCompile(
142         Cpp(
143             q{
144                 // just structs to use as template type parameters
145                 struct Foo; struct Bar; struct Baz; struct Quux;
146 
147                 // this is a ClassTemplate
148                 template<typename, typename, bool, typename, int, typename>
149                 struct Template { using Type = bool; };
150 
151                 // this is a ClassTemplatePartialSpecialization
152                 template<typename T, bool V0, typename T3, typename T4>
153                 struct Template<Quux, T, V0, T3, 42, T4> { using Type = short; };
154 
155                 // this is a ClassTemplatePartialSpecialization
156                 template<typename T, bool V0, typename T3, typename T4>
157                 struct Template<T, Quux, V0, T3, 42, T4> { using Type = double; };
158             }
159         ),
160         D(
161             q{
162                 import std.conv: text;
163 
164                 auto t1 = Template!(Foo,  Bar,  false, Baz,  0, Quux)(); // full template
165                 auto t2 = Template!(Quux, Bar,  false, Baz, 42, Quux)(); // partial1
166                 auto t3 = Template!(Foo,  Quux, false, Baz, 42, Quux)(); // partial2
167 
168                 static assert(is(t1.Type == bool),   t2.Type.stringof);
169                 static assert(is(t2.Type == short),  t2.Type.stringof);
170                 static assert(is(t3.Type == double), t3.Type.stringof);
171             }
172         ),
173     );
174 }
175 
176 
177 
178 // as seen in stl_algobase.h
179 @("__copy_move")
180 @safe unittest {
181     shouldCompile(
182         Cpp(
183             q{
184                 namespace std {
185                     struct random_access_iterator_tag;
186 
187                     template<bool, bool, typename>
188                     struct __copy_move {};
189 
190                     template<typename _Category>
191                     struct __copy_move<true, false, _Category> {};
192 
193                     template<>
194                     struct __copy_move<false, false, random_access_iterator_tag> {};
195 
196                     template<>
197                     struct __copy_move<true, false, random_access_iterator_tag> {};
198 
199                     template<bool _IsMove>
200                     struct __copy_move<_IsMove, true, random_access_iterator_tag> {};
201                 }
202             }
203         ),
204         D(
205             q{
206                 struct RandomStruct {}
207                 auto c1 = __copy_move!(false, true, int)();
208                 auto c2 = __copy_move!(true, false, RandomStruct)();
209                 auto c3 = __copy_move!(false, false, random_access_iterator_tag)();
210                 auto c4 = __copy_move!(true, false, random_access_iterator_tag)();
211                 auto c5 = __copy_move!(false, true, random_access_iterator_tag)();
212                 auto c6 = __copy_move!(true, true, random_access_iterator_tag)();
213             }
214         ),
215     );
216 }
217 
218 
219 @("constexpr struct variable")
220 @safe unittest {
221     shouldCompile(
222         Cpp(
223             q{
224                 template<typename _Tp, _Tp __v>
225                 struct integral_constant {
226                     public: // FIXME #76
227                     static constexpr _Tp value = __v;
228                 };
229             }
230         ),
231         D(
232             q{
233                 static assert(integral_constant!(int, 42).value == 42);
234                 static assert(integral_constant!(int, 33).value == 33);
235             }
236         ),
237     );
238 }
239 
240 @("typedef to template type parameter")
241 @safe unittest {
242     shouldCompile(
243         Cpp(
244             q{
245                 template<typename _Tp, _Tp __v>
246                 struct integral_constant {
247                     public: // FIXME #76
248                     typedef _Tp value_type;
249                 };
250             }
251         ),
252         D(
253             q{
254                 static assert(is(integral_constant!(short, 42).value_type == short));
255                 static assert(is(integral_constant!(long, 42).value_type == long));
256             }
257         ),
258     );
259 }
260 
261 @("typedef to template struct")
262 @safe unittest {
263     shouldCompile(
264         Cpp(
265             q{
266                 template<typename _Tp, _Tp __v>
267                 struct integral_constant {
268                     public: // FIXME #76
269                     typedef integral_constant<_Tp, __v>   type;
270                 };
271             }
272         ),
273         D(
274             q{
275                 static assert(is(integral_constant!(int, 33).type == integral_constant!(int, 33)));
276             }
277         ),
278     );
279 }
280 
281 @("opCast template type")
282 @safe unittest {
283     shouldCompile(
284         Cpp(
285             q{
286                 template<typename _Tp, _Tp __v>
287                 struct integral_constant
288                 {
289                     public: // FIXME #76
290                     static constexpr _Tp value = __v;
291                     typedef _Tp value_type;
292                     constexpr operator value_type() const noexcept { return value; }
293                 };
294             }
295         ),
296         D(
297             q{
298                 integral_constant!(int , 42) i;
299                 auto j = cast(int) i;
300             }
301         ),
302     );
303 }
304 
305 
306 // as seen in type_traits
307 @("integral_constant")
308 @safe unittest {
309     shouldCompile(
310         Cpp(
311             q{
312                 template<typename _Tp, _Tp __v>
313                 struct integral_constant
314                 {
315                     public: // FIXME #76
316                     static constexpr _Tp value = __v;
317                     typedef _Tp value_type;
318                     constexpr operator value_type() const noexcept { return value; }
319                     constexpr value_type operator()() const noexcept { return value; }
320                 };
321             }
322         ),
323         D(
324             q{
325             }
326         ),
327     );
328 }
329 
330 
331 @("variadic.base.types")
332 @safe unittest {
333     shouldCompile(
334         Cpp(
335             q{
336                 template<int, typename, bool, typename...>
337                 struct VariadicTypes {
338                     using Type = void;
339                 };
340             }
341         ),
342         D(
343             q{
344                 static assert(is(VariadicTypes!(0, short, false).Type == void));
345                 static assert(is(VariadicTypes!(1, short, false, int).Type == void));
346                 static assert(is(VariadicTypes!(2, short, false, int, double, bool).Type == void));
347                 static assert(is(VariadicTypes!(3, short, false, int, int).Type == void));
348             }
349         ),
350     );
351 }
352 
353 @("variadic.base.values")
354 @safe unittest {
355     shouldCompile(
356         Cpp(
357             q{
358                 template<short, typename, bool, int...>
359                 struct VariadicValues {
360                     using Type = void;
361                 };
362             }
363         ),
364         D(
365             q{
366                 static assert(is(VariadicValues!(0, float, false).Type == void));
367                 static assert(is(VariadicValues!(1, float, false, 0, 1, 2, 3).Type == void));
368             }
369         ),
370     );
371 }
372 
373 
374 
375 @("variadic.specialized")
376 @safe unittest {
377     shouldCompile(
378         Cpp(
379             q{
380                 template<typename...>
381                 struct Variadic {
382                     using Type = void;
383                 };
384 
385                 template<typename T0, typename T1>
386                 struct Variadic<T0, T1> {
387                     using Type = bool;
388                 };
389             }
390         ),
391         D(
392             q{
393                 static assert(is(Variadic!().Type == void)); // general
394                 static assert(is(Variadic!(int).Type == void)); // general
395                 static assert(is(Variadic!(int, double, bool).Type == void)); // general
396                 static assert(is(Variadic!(int, int).Type == bool)); // specialisation
397             }
398         ),
399     );
400 }
401 
402 
403 // as seen in type_traits
404 @("__or_")
405 @safe unittest {
406     shouldCompile(
407         Cpp(
408             q{
409                 template<typename _Tp, _Tp __v>
410                 struct integral_constant
411                 {
412                     static constexpr _Tp                  value = __v;
413                     typedef _Tp                           value_type;
414                     typedef integral_constant<_Tp, __v>   type;
415                     constexpr operator value_type() const noexcept { return value; }
416                     constexpr value_type operator()() const noexcept { return value; }
417                 };
418 
419                 template<typename _Tp, _Tp __v>
420                 constexpr _Tp integral_constant<_Tp, __v>::value;
421 
422                 typedef integral_constant<bool, true>     true_type;
423 
424                 typedef integral_constant<bool, false>    false_type;
425 
426                 template<bool, typename, typename>
427                 struct conditional;
428 
429                 template<typename...>
430                 struct __or_;
431 
432                 template<>
433                 struct __or_<> : public false_type { };
434 
435                 template<typename _B1>
436                 struct __or_<_B1> : public _B1 { };
437 
438                 template<typename _B1, typename _B2>
439                 struct __or_<_B1, _B2>
440                     : public conditional<_B1::value, _B1, _B2>::type
441                 { };
442 
443                 template<typename _B1, typename _B2, typename _B3, typename... _Bn>
444                 struct __or_<_B1, _B2, _B3, _Bn...>
445                     : public conditional<_B1::value, _B1, __or_<_B2, _B3, _Bn...>>::type
446                 { };
447 
448                 template<bool _Cond, typename _Iftrue, typename _Iffalse>
449                 struct conditional
450                 { typedef _Iftrue type; };
451 
452                 template<typename _Iftrue, typename _Iffalse>
453                 struct conditional<false, _Iftrue, _Iffalse>
454                 { typedef _Iffalse type; };
455             }
456         ),
457         D(
458             q{
459             }
460         ),
461     );
462 }
463 
464 
465 // as seen in type traits
466 @("is_lvalue_reference")
467 @safe unittest {
468     shouldCompile(
469         Cpp(
470             q{
471                 template<typename _Tp, _Tp __v>
472                 struct integral_constant
473                 {
474                     static constexpr _Tp                  value = __v;
475                     typedef _Tp                           value_type;
476                     typedef integral_constant<_Tp, __v>   type;
477                     constexpr operator value_type() const noexcept { return value; }
478                     constexpr value_type operator()() const noexcept { return value; }
479                 };
480 
481                 template<typename _Tp, _Tp __v>
482                 constexpr _Tp integral_constant<_Tp, __v>::value;
483 
484                 typedef integral_constant<bool, true>  true_type;
485                 typedef integral_constant<bool, false> false_type;
486 
487                 template<typename>
488                 struct is_lvalue_reference: public false_type { };
489 
490                 template<typename _Tp>
491                 struct is_lvalue_reference<_Tp&>: public true_type { };
492             }
493         ),
494         D(
495             q{
496                 // FIXME #85
497                 // static assert(!is_lvalue_reference!int.value);
498                 // static assert( is_lvalue_reference!(int*).value);
499             }
500         ),
501     );
502 }
503 
504 
505 // as seen in type traits
506 @("decltype")
507 @safe unittest {
508     shouldCompile(
509         Cpp(
510             q{
511                 template<typename T>
512                 struct Struct {
513                     T i;
514                     using Type = decltype(i);
515                 };
516             }
517         ),
518         D(
519             q{
520                 static assert(is(Struct!int.Type == int));
521                 static assert(is(Struct!double.Type == double));
522             }
523         ),
524     );
525 }
526 
527 
528 // as seen in type traits
529 @("typename")
530 @safe unittest {
531     shouldCompile(
532         Cpp(
533             q{
534                 template<typename T>
535                 struct TheType {
536                     using Type = T;
537                 };
538 
539                 template<typename T>
540                 struct Struct {
541                     using AlsoType = typename TheType<T>::Type;
542                 };
543             }
544         ),
545         D(
546             q{
547                 static assert(is(Struct!int.AlsoType == int));
548                 static assert(is(Struct!double.AlsoType == double));
549             }
550         ),
551     );
552 }
553 
554 
555 // as seen in type traits
556 @("add_volatile")
557 @safe unittest {
558     shouldCompile(
559         Cpp(
560             q{
561                 template<typename T>
562                 struct add_volatile { using Type = volatile T; };
563             }
564         ),
565         D(
566             q{
567                 static assert(is(add_volatile!int.Type == int));
568                 static assert(is(add_volatile!double.Type == double));
569             }
570         ),
571     );
572 }
573 
574 
575 // as seen in type traits
576 @("unsigned")
577 @safe unittest {
578     shouldCompile(
579         Cpp(
580             q{
581                 template<bool C, typename T0, typename T1>
582                 struct Helper {
583                     using Type = T1;
584                 };
585 
586                 template<typename T>
587                 struct Thingie {
588                     static const bool b0 = sizeof(T) < sizeof(unsigned short);
589                     using Type = typename Helper<b0, unsigned long, unsigned long long>::Type;
590                 };
591             }
592         ),
593         D(
594             q{
595             }
596         ),
597     );
598 }
599 
600 
601 @("sizeof")
602 @safe unittest {
603     shouldCompile(
604         Cpp(
605             q{
606                 template<typename T>
607                 struct Thingie {
608                     static constexpr auto b0 = sizeof(T) < sizeof(unsigned short);
609                 };
610             }
611         ),
612         D(
613             q{
614                 static assert( Thingie!ubyte.b0);
615                 static assert(!Thingie!int.b0);
616             }
617         ),
618     );
619 }