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 @("variadic.specialized")
377 @safe unittest {
378     shouldCompile(
379         Cpp(
380             q{
381                 template<typename...>
382                 struct Variadic {
383                     using Type = void;
384                 };
385 
386                 template<typename T0, typename T1>
387                 struct Variadic<T0, T1> {
388                     using Type = bool;
389                 };
390             }
391         ),
392         D(
393             q{
394                 static assert(is(Variadic!().Type == void)); // general
395                 static assert(is(Variadic!(int).Type == void)); // general
396                 static assert(is(Variadic!(int, double, bool).Type == void)); // general
397                 static assert(is(Variadic!(int, int).Type == bool)); // specialisation
398             }
399         ),
400     );
401 }
402 
403 
404 // as seen in type_traits
405 @("__or_")
406 @safe unittest {
407     shouldCompile(
408         Cpp(
409             q{
410                 template<typename _Tp, _Tp __v>
411                 struct integral_constant
412                 {
413                     static constexpr _Tp                  value = __v;
414                     typedef _Tp                           value_type;
415                     typedef integral_constant<_Tp, __v>   type;
416                     constexpr operator value_type() const noexcept { return value; }
417                     constexpr value_type operator()() const noexcept { return value; }
418                 };
419 
420                 template<typename _Tp, _Tp __v>
421                 constexpr _Tp integral_constant<_Tp, __v>::value;
422 
423                 typedef integral_constant<bool, true>     true_type;
424 
425                 typedef integral_constant<bool, false>    false_type;
426 
427                 template<bool, typename, typename>
428                 struct conditional;
429 
430                 template<typename...>
431                 struct __or_;
432 
433                 template<>
434                 struct __or_<> : public false_type { };
435 
436                 template<typename _B1>
437                 struct __or_<_B1> : public _B1 { };
438 
439                 template<typename _B1, typename _B2>
440                 struct __or_<_B1, _B2>
441                     : public conditional<_B1::value, _B1, _B2>::type
442                 { };
443 
444                 template<typename _B1, typename _B2, typename _B3, typename... _Bn>
445                 struct __or_<_B1, _B2, _B3, _Bn...>
446                     : public conditional<_B1::value, _B1, __or_<_B2, _B3, _Bn...>>::type
447                 { };
448 
449                 template<bool _Cond, typename _Iftrue, typename _Iffalse>
450                 struct conditional
451                 { typedef _Iftrue type; };
452 
453                 template<typename _Iftrue, typename _Iffalse>
454                 struct conditional<false, _Iftrue, _Iffalse>
455                 { typedef _Iffalse type; };
456             }
457         ),
458         D(
459             q{
460                 // TODO: actually assert on the types
461             }
462         ),
463     );
464 }
465 
466 
467 @("__or_.binary")
468 @safe unittest {
469     shouldCompile(
470         Cpp(
471             q{
472                 template <typename...> struct __or_;
473                 template <typename B0, typename B1>
474                 struct __or_<B0, B1> {
475                     static constexpr auto value = true;
476                 };
477 
478                 template <typename T>
479                 struct is_copy_constructible {
480                     static constexpr auto value = true;
481                 };
482 
483                 template <typename T>
484                 struct is_nothrow_move_constructible {
485                     static constexpr auto value = true;
486                 };
487 
488                 template <typename T, bool B
489                     = __or_<is_copy_constructible<typename T::value_type>,
490                     is_nothrow_move_constructible<typename T::value_type>>::value>
491                 struct Oops {
492                     static constexpr auto value = B;
493                 };
494             }
495         ),
496         D(
497             q{
498                 struct Foo {
499                     alias value_type = int;
500                 }
501                 static assert(Oops!Foo.value);
502             }
503         ),
504     );
505 }
506 
507 
508 // as seen in type traits
509 @("is_lvalue_reference")
510 @safe unittest {
511     shouldCompile(
512         Cpp(
513             q{
514                 template<typename _Tp, _Tp __v>
515                 struct integral_constant
516                 {
517                     static constexpr _Tp                  value = __v;
518                     typedef _Tp                           value_type;
519                     typedef integral_constant<_Tp, __v>   type;
520                     constexpr operator value_type() const noexcept { return value; }
521                     constexpr value_type operator()() const noexcept { return value; }
522                 };
523 
524                 template<typename _Tp, _Tp __v>
525                 constexpr _Tp integral_constant<_Tp, __v>::value;
526 
527                 typedef integral_constant<bool, true>  true_type;
528                 typedef integral_constant<bool, false> false_type;
529 
530                 template<typename>
531                 struct is_lvalue_reference: public false_type { };
532 
533                 template<typename _Tp>
534                 struct is_lvalue_reference<_Tp&>: public true_type { };
535             }
536         ),
537         D(
538             q{
539                 // FIXME #85
540                 // static assert(!is_lvalue_reference!int.value);
541                 // static assert( is_lvalue_reference!(int*).value);
542             }
543         ),
544     );
545 }
546 
547 
548 // as seen in type traits
549 @("decltype")
550 @safe unittest {
551     shouldCompile(
552         Cpp(
553             q{
554                 template<typename T>
555                 struct Struct {
556                     T i;
557                     using Type = decltype(i);
558                 };
559             }
560         ),
561         D(
562             q{
563                 static assert(is(Struct!int.Type == int));
564                 static assert(is(Struct!double.Type == double));
565             }
566         ),
567     );
568 }
569 
570 
571 // as seen in type traits
572 @("typename")
573 @safe unittest {
574     shouldCompile(
575         Cpp(
576             q{
577                 template<typename T>
578                 struct TheType {
579                     using Type = T;
580                 };
581 
582                 template<typename T>
583                 struct Struct {
584                     using AlsoType = typename TheType<T>::Type;
585                 };
586             }
587         ),
588         D(
589             q{
590                 static assert(is(Struct!int.AlsoType == int));
591                 static assert(is(Struct!double.AlsoType == double));
592             }
593         ),
594     );
595 }
596 
597 
598 // as seen in type traits
599 @("add_volatile")
600 @safe unittest {
601     shouldCompile(
602         Cpp(
603             q{
604                 template<typename T>
605                 struct add_volatile { using Type = volatile T; };
606             }
607         ),
608         D(
609             q{
610                 static assert(is(add_volatile!int.Type == int));
611                 static assert(is(add_volatile!double.Type == double));
612             }
613         ),
614     );
615 }
616 
617 
618 // as seen in type traits
619 @("unsigned")
620 @safe unittest {
621     shouldCompile(
622         Cpp(
623             q{
624                 template<bool C, typename T0, typename T1>
625                 struct Helper {
626                     using Type = T1;
627                 };
628 
629                 template<typename T>
630                 struct Thingie {
631                     static const bool b0 = sizeof(T) < sizeof(unsigned short);
632                     using Type = typename Helper<b0, unsigned long, unsigned long long>::Type;
633                 };
634             }
635         ),
636         D(
637             q{
638             }
639         ),
640     );
641 }
642 
643 
644 @("sizeof")
645 @safe unittest {
646     shouldCompile(
647         Cpp(
648             q{
649                 template<typename T>
650                 struct Thingie {
651                     static constexpr auto b0 = sizeof(T) < sizeof(unsigned short);
652                 };
653             }
654         ),
655         D(
656             q{
657                 static assert( Thingie!ubyte.b0);
658                 static assert(!Thingie!int.b0);
659             }
660         ),
661     );
662 }
663 
664 
665 
666 @("__normal_iterator.base")
667 @safe unittest {
668     shouldCompile(
669         Cpp(
670             q{
671                 template<typename I>
672                 struct Struct {
673                     I i;
674                     const I& base() const { return i; }
675                 };
676             }
677         ),
678         D(
679             q{
680                 struct Int { int value; }
681                 Struct!Int s;
682                 Int i = s.base();
683             }
684         ),
685    );
686 }
687 
688 
689 @ShouldFail("need to fix declaration of new template parameters in specialisations")
690 @("move_iterator.reference")
691 @safe unittest {
692     shouldCompile(
693         Cpp(
694             q{
695                 template<bool _Cond, typename _Iftrue, typename _Iffalse>
696                 struct conditional
697                 { typedef _Iftrue type; };
698 
699                 template<typename _Iftrue, typename _Iffalse>
700                 struct conditional<false, _Iftrue, _Iffalse>
701                 { typedef _Iffalse type; };
702 
703                 template<typename T> struct remove_reference      { using type = T; };
704                 template<typename T> struct remove_reference<T&>  { using type = T; };
705                 template<typename T> struct remove_reference<T&&> { using type = T; };
706 
707                 template<typename T> struct is_reference      { enum { value = false }; };
708                 template<typename T> struct is_reference<T&>  { enum { value = true  }; };
709                 template<typename T> struct is_reference<T&&> { enum { value = true  }; };
710 
711                 template<typename T>
712                 struct Iterator {
713                     using reference = typename conditional<is_reference<T>::value,
714                                                            typename remove_reference<T>::type&&,
715                                                            T>::type;
716                 };
717             }
718         ),
719         D(
720             q{
721             }
722         ),
723    );
724 }
725 
726 
727 @("allocator.simple")
728 @safe unittest {
729     shouldCompile(
730         Cpp(
731             q{
732                 template <typename> class allocator;
733                 template <> class allocator<void>;
734                 template <> class allocator<void> {
735                     template<typename Up, typename... Args>
736                     void construct(Up* p, Args&&... args);
737                 };
738             }
739         ),
740         D(
741             q{
742                 auto a = allocator!void();
743                 static struct Foo { int i; double d; }
744                 Foo* foo;
745                 a.construct(foo, 42, 33.3);
746             }
747         ),
748    );
749 }
750 
751 @("allocator.pointer")
752 @safe unittest {
753     shouldCompile(
754         Cpp(
755             q{
756                 template <typename T>
757                 class Allocator {
758                     typedef T* pointer;
759                 };
760             }
761         ),
762         D(
763             q{
764                 static assert(is(Allocator!int.pointer == int*));
765                 static assert(is(Allocator!double.pointer == double*));
766             }
767         ),
768    );
769 }
770 
771 
772 @("refer to type template argument in another argument")
773 @safe unittest {
774     shouldCompile(
775         Cpp(
776             q{
777                 template<typename T, int S = sizeof(T)>
778                 struct Foo {
779                     static constexpr auto Size = S;
780                 };
781             }
782         ),
783         D(
784             q{
785                 static assert(Foo!int.Size == 4);
786                 static assert(Foo!long.Size == 8);
787             }
788         ),
789     );
790 }
791 
792 
793 @("__is_empty.specialisation")
794 @safe unittest {
795     shouldCompile(
796         Cpp(
797             q{
798                 template<typename T, bool = __is_empty(T)>
799                 struct Foo {
800                     static constexpr auto value = 1;
801                 };
802 
803                 template<typename T>
804                 struct Foo<T, false> {
805                     static constexpr auto value = 2;
806                 };
807             }
808         ),
809         D(
810             q{
811                 struct Empty{}
812                 struct Int { int i; }
813 
814                 static assert(Foo!Empty.value == 1);
815                 // In C++ the assertion below would pass. In D it doesn't
816                 // due to different semantics, but explicitly picking the
817                 // specialisation works.
818                 // static assert(Foo!Int.value == 2);
819                 static assert(Foo!(Int, false).value == 2);
820             }
821         ),
822    );
823 }
824 
825 
826 
827 @("default template type parameter")
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                     public:
895                         static constexpr auto value = 42;
896                         allocator(const allocator& other) throw() {}
897                     };
898                 }
899             }
900         ),
901         D(
902             q{
903                 allocator!int foo = void;
904                 static assert(foo.value == 42);
905             }
906         ),
907    );
908 }
909 
910 
911 @("using.partial")
912 @safe unittest {
913     shouldCompile(
914         Cpp(
915             q{
916                 template<typename T>
917                     struct new_allocator {
918                 };
919 
920                 template<typename _Tp>
921                 using __allocator_base = new_allocator<_Tp>;
922             }
923         ),
924         D(
925             q{
926                 static assert(is(__allocator_base!int == new_allocator!int));
927             }
928         ),
929    );
930 }
931 
932 
933 @("using.complete")
934 @safe unittest {
935     shouldCompile(
936         Cpp(
937             q{
938                 /// A metafunction that always yields void, used for detecting valid types.
939                 template<typename...> using void_t = void;
940             }
941         ),
942         D(
943             q{
944                 static assert(is(void_t!int == void), void_t!int.stringof);
945             }
946         ),
947    );
948 }
949 
950 
951 @("function.equals")
952 @safe unittest {
953     shouldCompile(
954         Cpp(
955             q{
956                 struct Foo {
957                     template<typename T0, typename T1>
958                     bool equals(T0 lhs, T1 rhs);
959                 };
960             }
961         ),
962         D(
963             q{
964                 Foo foo0, foo1;
965                 bool res = foo0.equals(foo0, foo1);
966             }
967         ),
968    );
969 }
970 
971 
972 @("function.ctor")
973 @safe unittest {
974     shouldCompile(
975         Cpp(
976             q{
977                 template <typename T>
978                 struct Foo {
979                     template<typename U>
980                     Foo(const Foo<U>&);
981                 };
982             }
983         ),
984         D(
985             q{
986                 Foo!int fooInt;
987                 auto fooDouble = Foo!double(fooInt);
988             }
989         ),
990    );
991 }
992 
993 
994 @("function.body.delete")
995 @safe unittest {
996     shouldCompile(
997         Cpp(
998             q{
999                 template <typename T>
1000                 struct Allocator {
1001                     void deallocate(T* ptr) {
1002                         ::operator delete(ptr);
1003                     }
1004                 };
1005             }
1006         ),
1007         D(
1008             q{
1009                 auto allocator = Allocator!int();
1010                 allocator.deallocate(new int);
1011             }
1012         ),
1013    );
1014 }