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 }