1 module it.cpp.templates;
2 
3 
4 import it;
5 
6 
7 @("simple")
8 @safe unittest {
9     shouldCompile(
10         Cpp(
11             q{
12                 template<typename T>
13                 struct vector {
14                 public:
15                     T value;
16                     void push_back();
17                 };
18 
19                 template<typename U, int length>
20                 struct array {
21                 public:
22                     U elements[length];
23                 };
24 
25             }
26         ),
27         D(
28             q{
29                 auto vi = vector!int(42);
30                 static assert(is(typeof(vi.value) == int));
31                 vi.value = 33;
32 
33                 auto vf = vector!float(33.3);
34                 static assert(is(typeof(vf.value) == float));
35                 vf.value = 22.2;
36 
37                 auto vs = vector!string("foo");
38                 static assert(is(typeof(vs.value) == string));
39                 vs.value = "bar";
40 
41                 auto ai = array!(int, 3)();
42                 static assert(ai.elements.length == 3);
43                 static assert(is(typeof(ai.elements[0]) == int));
44             }
45         ),
46    );
47 }
48 
49 @("nameless type")
50 @safe unittest {
51     shouldCompile(
52         Cpp(
53             q{
54                 // none of the template parameters have names, which is allowed
55                 // in C++ but not in D
56                 template<bool, bool, typename>
57                 struct Foo {
58 
59                 };
60             }
61         ),
62         D(
63             q{
64                 auto f = Foo!(true, false, int)();
65             }
66         ),
67     );
68 }
69 
70 @("struct full specialisation")
71 @safe unittest {
72     shouldCompile(
73         Cpp(
74             q{
75                 // this is a ClassTemplate
76                 template<bool, bool, typename>
77                 struct __copy_move {
78                     enum { value = 42 };
79                 };
80 
81                 // This is a StructDecl
82                 template<>
83                 struct __copy_move<false, true, double> {
84                     enum { value = 33 };
85                 };
86             }
87         ),
88         D(
89             q{
90                 import std.conv: text;
91 
92                 // FIXME: libclang bug - templates don't have proper
93                 // EnumConstantDecl values for some reason
94                 // auto c1 = __copy_move!(true, true, int)();
95                 // static assert(c1.value == 42, text(cast(int) c1.value));
96 
97                 auto c2 = __copy_move!(false, true, double)();
98                 static assert(c2.value == 33, text(cast(int) c2.value));
99             }
100         ),
101     );
102 }
103 
104 // struct/class keyword could end up in different code paths
105 @("class full specialisation")
106 @safe unittest {
107     shouldCompile(
108         Cpp(
109             q{
110                 // this is a ClassTemplate
111                 template<bool, bool, typename>
112                 class __copy_move {
113                 public:
114                     enum { value = 42 };
115                 };
116 
117                 // This is a ClassDecl
118                 template<>
119                 class __copy_move<false, true, double> {
120                 public:
121                     enum { value = 33 };
122                 };
123             }
124         ),
125         D(
126             q{
127                 import std.conv: text;
128 
129                 // FIXME: libclang bug - templates don't have proper
130                 // EnumConstantDecl values for some reason
131                 // auto c1 = __copy_move!(true, true, int)();
132                 // static assert(c1.value == 42, text(cast(int) c1.value));
133 
134                 auto c2 = __copy_move!(false, true, double)();
135                 static assert(c2.value == 33, text(cast(int) c2.value));
136             }
137         ),
138     );
139 }
140 
141 @("struct partial specialisation")
142 @safe unittest {
143     shouldCompile(
144         Cpp(
145             q{
146                 // just structs to use as template type parameters
147                 struct Foo; struct Bar; struct Baz; struct Quux;
148 
149                 // this is a ClassTemplate
150                 template<typename, typename, bool, typename, int, typename>
151                 struct Template { using Type = bool; };
152 
153                 // this is a ClassTemplatePartialSpecialization
154                 template<typename T, bool V0, typename T3, typename T4>
155                 struct Template<Quux, T, V0, T3, 42, T4> { using Type = short; };
156 
157                 // this is a ClassTemplatePartialSpecialization
158                 template<typename T, bool V0, typename T3, typename T4>
159                 struct Template<T, Quux, V0, T3, 42, T4> { using Type = double; };
160             }
161         ),
162         D(
163             q{
164                 import std.conv: text;
165 
166                 auto t1 = Template!(Foo,  Bar,  false, Baz,  0, Quux)(); // full template
167                 auto t2 = Template!(Quux, Bar,  false, Baz, 42, Quux)(); // partial1
168                 auto t3 = Template!(Foo,  Quux, false, Baz, 42, Quux)(); // partial2
169 
170                 static assert(is(t1.Type == bool),   t2.Type.stringof);
171                 static assert(is(t2.Type == short),  t2.Type.stringof);
172                 static assert(is(t3.Type == double), t3.Type.stringof);
173             }
174         ),
175     );
176 }
177 
178 
179 
180 // as seen in stl_algobase.h
181 @("__copy_move")
182 @safe unittest {
183     shouldCompile(
184         Cpp(
185             q{
186                 namespace std {
187                     struct random_access_iterator_tag;
188 
189                     template<bool, bool, typename>
190                     struct __copy_move {};
191 
192                     template<typename _Category>
193                     struct __copy_move<true, false, _Category> {};
194 
195                     template<>
196                     struct __copy_move<false, false, random_access_iterator_tag> {};
197 
198                     template<>
199                     struct __copy_move<true, false, random_access_iterator_tag> {};
200 
201                     template<bool _IsMove>
202                     struct __copy_move<_IsMove, true, random_access_iterator_tag> {};
203                 }
204             }
205         ),
206         D(
207             q{
208                 struct RandomStruct {}
209                 auto c1 = __copy_move!(false, true, int)();
210                 auto c2 = __copy_move!(true, false, RandomStruct)();
211                 auto c3 = __copy_move!(false, false, random_access_iterator_tag)();
212                 auto c4 = __copy_move!(true, false, random_access_iterator_tag)();
213                 auto c5 = __copy_move!(false, true, random_access_iterator_tag)();
214                 auto c6 = __copy_move!(true, true, random_access_iterator_tag)();
215             }
216         ),
217     );
218 }
219 
220 
221 @("constexpr struct variable")
222 @safe unittest {
223     shouldCompile(
224         Cpp(
225             q{
226                 template<typename _Tp, _Tp __v>
227                 struct integral_constant {
228                     public: // FIXME #76
229                     static constexpr _Tp value = __v;
230                 };
231             }
232         ),
233         D(
234             q{
235                 static assert(integral_constant!(int, 42).value == 42);
236                 static assert(integral_constant!(int, 33).value == 33);
237             }
238         ),
239     );
240 }
241 
242 @("typedef to template type parameter")
243 @safe unittest {
244     shouldCompile(
245         Cpp(
246             q{
247                 template<typename _Tp, _Tp __v>
248                 struct integral_constant {
249                     public: // FIXME #76
250                     typedef _Tp value_type;
251                 };
252             }
253         ),
254         D(
255             q{
256                 static assert(is(integral_constant!(short, 42).value_type == short));
257                 static assert(is(integral_constant!(long, 42).value_type == long));
258             }
259         ),
260     );
261 }
262 
263 @("typedef to template struct")
264 @safe unittest {
265     shouldCompile(
266         Cpp(
267             q{
268                 template<typename _Tp, _Tp __v>
269                 struct integral_constant {
270                     public: // FIXME #76
271                     typedef integral_constant<_Tp, __v>   type;
272                 };
273             }
274         ),
275         D(
276             q{
277                 static assert(is(integral_constant!(int, 33).type == integral_constant!(int, 33)));
278             }
279         ),
280     );
281 }
282 
283 @("opCast template type")
284 @safe unittest {
285     shouldCompile(
286         Cpp(
287             q{
288                 template<typename _Tp, _Tp __v>
289                 struct integral_constant
290                 {
291                     public: // FIXME #76
292                     static constexpr _Tp value = __v;
293                     typedef _Tp value_type;
294                     constexpr operator value_type() const noexcept { return value; }
295                 };
296             }
297         ),
298         D(
299             q{
300                 integral_constant!(int , 42) i;
301                 auto j = cast(int) i;
302             }
303         ),
304     );
305 }
306 
307 
308 // as seen in type_traits
309 @("integral_constant")
310 @safe unittest {
311     shouldCompile(
312         Cpp(
313             q{
314                 template<typename _Tp, _Tp __v>
315                 struct integral_constant
316                 {
317                     public: // FIXME #76
318                     static constexpr _Tp value = __v;
319                     typedef _Tp value_type;
320                     constexpr operator value_type() const noexcept { return value; }
321                     constexpr value_type operator()() const noexcept { return value; }
322                 };
323             }
324         ),
325         D(
326             q{
327             }
328         ),
329     );
330 }
331 
332 
333 @("variadic.base.types")
334 @safe unittest {
335     shouldCompile(
336         Cpp(
337             q{
338                 template<int, typename, bool, typename...>
339                 struct VariadicTypes {
340                     using Type = void;
341                 };
342             }
343         ),
344         D(
345             q{
346                 static assert(is(VariadicTypes!(0, short, false).Type == void));
347                 static assert(is(VariadicTypes!(1, short, false, int).Type == void));
348                 static assert(is(VariadicTypes!(2, short, false, int, double, bool).Type == void));
349                 static assert(is(VariadicTypes!(3, short, false, int, int).Type == void));
350             }
351         ),
352     );
353 }
354 
355 @("variadic.base.values")
356 @safe unittest {
357     shouldCompile(
358         Cpp(
359             q{
360                 template<short, typename, bool, int...>
361                 struct VariadicValues {
362                     using Type = void;
363                 };
364             }
365         ),
366         D(
367             q{
368                 static assert(is(VariadicValues!(0, float, false).Type == void));
369                 static assert(is(VariadicValues!(1, float, false, 0, 1, 2, 3).Type == void));
370             }
371         ),
372     );
373 }
374 
375 
376 
377 @Tags("notravis")
378 @("variadic.specialized")
379 @safe unittest {
380     shouldCompile(
381         Cpp(
382             q{
383                 template<typename...>
384                 struct Variadic {
385                     using Type = void;
386                 };
387 
388                 template<typename T0, typename T1>
389                 struct Variadic<T0, T1> {
390                     using Type = bool;
391                 };
392             }
393         ),
394         D(
395             q{
396                 static assert(is(Variadic!().Type == void)); // general
397                 static assert(is(Variadic!(int).Type == void)); // general
398                 static assert(is(Variadic!(int, double, bool).Type == void)); // general
399                 static assert(is(Variadic!(int, int).Type == bool)); // specialisation
400             }
401         ),
402     );
403 }
404 
405 
406 // as seen in type_traits
407 @("__or_")
408 @safe unittest {
409     shouldCompile(
410         Cpp(
411             q{
412                 template<typename _Tp, _Tp __v>
413                 struct integral_constant
414                 {
415                     static constexpr _Tp                  value = __v;
416                     typedef _Tp                           value_type;
417                     typedef integral_constant<_Tp, __v>   type;
418                     constexpr operator value_type() const noexcept { return value; }
419                     constexpr value_type operator()() const noexcept { return value; }
420                 };
421 
422                 template<typename _Tp, _Tp __v>
423                 constexpr _Tp integral_constant<_Tp, __v>::value;
424 
425                 typedef integral_constant<bool, true>     true_type;
426 
427                 typedef integral_constant<bool, false>    false_type;
428 
429                 template<bool, typename, typename>
430                 struct conditional;
431 
432                 template<typename...>
433                 struct __or_;
434 
435                 template<>
436                 struct __or_<> : public false_type { };
437 
438                 template<typename _B1>
439                 struct __or_<_B1> : public _B1 { };
440 
441                 template<typename _B1, typename _B2>
442                 struct __or_<_B1, _B2>
443                     : public conditional<_B1::value, _B1, _B2>::type
444                 { };
445 
446                 template<typename _B1, typename _B2, typename _B3, typename... _Bn>
447                 struct __or_<_B1, _B2, _B3, _Bn...>
448                     : public conditional<_B1::value, _B1, __or_<_B2, _B3, _Bn...>>::type
449                 { };
450 
451                 template<bool _Cond, typename _Iftrue, typename _Iffalse>
452                 struct conditional
453                 { typedef _Iftrue type; };
454 
455                 template<typename _Iftrue, typename _Iffalse>
456                 struct conditional<false, _Iftrue, _Iffalse>
457                 { typedef _Iffalse type; };
458             }
459         ),
460         D(
461             q{
462             }
463         ),
464     );
465 }
466 
467 
468 @Tags("notravis")
469 @("__or_.binary")
470 @safe unittest {
471     shouldCompile(
472         Cpp(
473             q{
474                 template <typename...> struct __or_;
475                 template <typename B0, typename B1>
476                 struct __or_<B0, B1> {
477                     static constexpr auto value = true;
478                 };
479 
480                 template <typename T>
481                 struct is_copy_constructible {
482                     static constexpr auto value = true;
483                 };
484 
485                 template <typename T>
486                 struct is_nothrow_move_constructible {
487                     static constexpr auto value = true;
488                 };
489 
490                 template <typename T, bool B
491                     = __or_<is_copy_constructible<typename T::value_type>,
492                     is_nothrow_move_constructible<typename T::value_type>>::value>
493                 struct Oops {
494                     static constexpr auto value = B;
495                 };
496             }
497         ),
498         D(
499             q{
500                 struct Foo {
501                     alias value_type = int;
502                 }
503                 static assert(Oops!Foo.value);
504             }
505         ),
506     );
507 }
508 
509 
510 // as seen in type traits
511 @("is_lvalue_reference")
512 @safe unittest {
513     shouldCompile(
514         Cpp(
515             q{
516                 template<typename _Tp, _Tp __v>
517                 struct integral_constant
518                 {
519                     static constexpr _Tp                  value = __v;
520                     typedef _Tp                           value_type;
521                     typedef integral_constant<_Tp, __v>   type;
522                     constexpr operator value_type() const noexcept { return value; }
523                     constexpr value_type operator()() const noexcept { return value; }
524                 };
525 
526                 template<typename _Tp, _Tp __v>
527                 constexpr _Tp integral_constant<_Tp, __v>::value;
528 
529                 typedef integral_constant<bool, true>  true_type;
530                 typedef integral_constant<bool, false> false_type;
531 
532                 template<typename>
533                 struct is_lvalue_reference: public false_type { };
534 
535                 template<typename _Tp>
536                 struct is_lvalue_reference<_Tp&>: public true_type { };
537             }
538         ),
539         D(
540             q{
541                 // FIXME #85
542                 // static assert(!is_lvalue_reference!int.value);
543                 // static assert( is_lvalue_reference!(int*).value);
544             }
545         ),
546     );
547 }
548 
549 
550 // as seen in type traits
551 @("decltype")
552 @safe unittest {
553     shouldCompile(
554         Cpp(
555             q{
556                 template<typename T>
557                 struct Struct {
558                     T i;
559                     using Type = decltype(i);
560                 };
561             }
562         ),
563         D(
564             q{
565                 static assert(is(Struct!int.Type == int));
566                 static assert(is(Struct!double.Type == double));
567             }
568         ),
569     );
570 }
571 
572 
573 // as seen in type traits
574 @("typename")
575 @safe unittest {
576     shouldCompile(
577         Cpp(
578             q{
579                 template<typename T>
580                 struct TheType {
581                     using Type = T;
582                 };
583 
584                 template<typename T>
585                 struct Struct {
586                     using AlsoType = typename TheType<T>::Type;
587                 };
588             }
589         ),
590         D(
591             q{
592                 static assert(is(Struct!int.AlsoType == int));
593                 static assert(is(Struct!double.AlsoType == double));
594             }
595         ),
596     );
597 }
598 
599 
600 // as seen in type traits
601 @("add_volatile")
602 @safe unittest {
603     shouldCompile(
604         Cpp(
605             q{
606                 template<typename T>
607                 struct add_volatile { using Type = volatile T; };
608             }
609         ),
610         D(
611             q{
612                 static assert(is(add_volatile!int.Type == int));
613                 static assert(is(add_volatile!double.Type == double));
614             }
615         ),
616     );
617 }
618 
619 
620 // as seen in type traits
621 @("unsigned")
622 @safe unittest {
623     shouldCompile(
624         Cpp(
625             q{
626                 template<bool C, typename T0, typename T1>
627                 struct Helper {
628                     using Type = T1;
629                 };
630 
631                 template<typename T>
632                 struct Thingie {
633                     static const bool b0 = sizeof(T) < sizeof(unsigned short);
634                     using Type = typename Helper<b0, unsigned long, unsigned long long>::Type;
635                 };
636             }
637         ),
638         D(
639             q{
640             }
641         ),
642     );
643 }
644 
645 
646 @("sizeof")
647 @safe unittest {
648     shouldCompile(
649         Cpp(
650             q{
651                 template<typename T>
652                 struct Thingie {
653                     static constexpr auto b0 = sizeof(T) < sizeof(unsigned short);
654                 };
655             }
656         ),
657         D(
658             q{
659                 static assert( Thingie!ubyte.b0);
660                 static assert(!Thingie!int.b0);
661             }
662         ),
663     );
664 }
665 
666 
667 
668 @("__normal_iterator.base")
669 @safe unittest {
670     shouldCompile(
671         Cpp(
672             q{
673                 template<typename I>
674                 struct Struct {
675                     I i;
676                     const I& base() const { return i; }
677                 };
678             }
679         ),
680         D(
681             q{
682                 struct Int { int value; }
683                 Struct!Int s;
684                 Int i = s.base();
685             }
686         ),
687    );
688 }
689 
690 
691 @ShouldFail("need to fix declaration of new template parameters in specialisations")
692 @("move_iterator.reference")
693 @safe unittest {
694     shouldCompile(
695         Cpp(
696             q{
697                 template<bool _Cond, typename _Iftrue, typename _Iffalse>
698                 struct conditional
699                 { typedef _Iftrue type; };
700 
701                 template<typename _Iftrue, typename _Iffalse>
702                 struct conditional<false, _Iftrue, _Iffalse>
703                 { typedef _Iffalse type; };
704 
705                 template<typename T> struct remove_reference      { using type = T; };
706                 template<typename T> struct remove_reference<T&>  { using type = T; };
707                 template<typename T> struct remove_reference<T&&> { using type = T; };
708 
709                 template<typename T> struct is_reference      { enum { value = false }; };
710                 template<typename T> struct is_reference<T&>  { enum { value = true  }; };
711                 template<typename T> struct is_reference<T&&> { enum { value = true  }; };
712 
713                 template<typename T>
714                 struct Iterator {
715                     using reference = typename conditional<is_reference<T>::value,
716                                                            typename remove_reference<T>::type&&,
717                                                            T>::type;
718                 };
719             }
720         ),
721         D(
722             q{
723             }
724         ),
725    );
726 }
727 
728 
729 @("allocator.simple")
730 @safe unittest {
731     shouldCompile(
732         Cpp(
733             q{
734                 template <typename> class allocator;
735                 template <> class allocator<void>;
736                 template <> class allocator<void> {
737                     template<typename Up, typename... Args>
738                     void construct(Up* p, Args&&... args);
739                 };
740             }
741         ),
742         D(
743             q{
744             }
745         ),
746    );
747 }
748 
749 @("allocator.pointer")
750 @safe unittest {
751     shouldCompile(
752         Cpp(
753             q{
754                 template <typename T>
755                 class Allocator {
756                     typedef T* pointer;
757                 };
758             }
759         ),
760         D(
761             q{
762                 static assert(is(Allocator!int.pointer == int*));
763                 static assert(is(Allocator!double.pointer == double*));
764             }
765         ),
766    );
767 }
768 
769 
770 @Tags("notravis")
771 @("refer to type template argument in another argument")
772 @safe unittest {
773     shouldCompile(
774         Cpp(
775             q{
776                 template<typename T, int S = sizeof(T)>
777                 struct Foo {
778                     static constexpr auto Size = S;
779                 };
780             }
781         ),
782         D(
783             q{
784                 static assert(Foo!int.Size == 4);
785                 static assert(Foo!long.Size == 8);
786             }
787         ),
788     );
789 }
790 
791 @Tags("notravis")
792 @("__is_empty.specialisation")
793 @safe unittest {
794     shouldCompile(
795         Cpp(
796             q{
797                 template<typename T, bool = __is_empty(T)>
798                 struct Foo {
799                     static constexpr auto value = 1;
800                 };
801 
802                 template<typename T>
803                 struct Foo<T, false> {
804                     static constexpr auto value = 2;
805                 };
806             }
807         ),
808         D(
809             q{
810                 struct Empty{}
811                 struct Int { int i; }
812 
813                 static assert(Foo!Empty.value == 1);
814                 // In C++ the assertion below would pass. In D it doesn't
815                 // due to different semantics, but explicitly picking the
816                 // specialisation works.
817                 // static assert(Foo!Int.value == 2);
818                 static assert(Foo!(Int, false).value == 2);
819             }
820         ),
821    );
822 }
823 
824 
825 
826 @("default template type parameter")
827 @Tags("notravis")
828 @safe unittest {
829     shouldCompile(
830         Cpp(
831             q{
832                 template <typename T>
833                 struct Allocator {
834                 };
835 
836                 template <typename T, typename A = Allocator<T>>
837                 struct Vector {
838 
839                 };
840             }
841         ),
842         D(
843             q{
844                 Vector!int ints;
845             }
846         ),
847    );
848 }
849 
850 @("specialisation for cv")
851 @safe unittest {
852     shouldCompile(
853         Cpp(
854             q{
855                 template <typename T>
856                 struct Allocator {
857                     using Type = void;
858                     enum { value = 0 };
859                 };
860 
861                 template <typename T>
862                 struct Allocator<const T> {
863                     using Type = short;
864                 };
865 
866                 template <typename T>
867                 struct Allocator<volatile T> {
868                     using Type = float;
869                 };
870             }
871         ),
872         D(
873             q{
874                 // we can't specialise on const
875                 static assert(is(Allocator!int.Type == void), Allocator!int.Type.stringof);
876                 static assert(is(Allocator!(const int).Type == void), Allocator!(const int).Type.stringof);
877             }
878         ),
879    );
880 }
881 
882 
883 @("declaration and definitions with different template argument names")
884 @safe unittest {
885     shouldCompile(
886         Cpp(
887             q{
888                 namespace std {
889                     template <typename> class allocator;
890                 }
891 
892                 namespace std {
893                     template <typename T> class allocator {
894                         static constexpr auto value = 42;
895                         allocator(const allocator& other) throw() {}
896                     };
897                 }
898             }
899         ),
900         D(
901             q{
902                 allocator!int foo = void;
903                 static assert(foo.value == 42);
904             }
905         ),
906    );
907 }