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 }