1dnl
2dnl Based on https://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx.html
3dnl Author: Anatol Belski <ab@php.net>
4dnl
5dnl PHP_CXX_COMPILE_STDCXX(version, mandatory|optional, var_name_to_put_switch_in)
6dnl
7dnl ARGUMENTS
8dnl
9dnl first arg  - version as 11, 14, 17 or 20
10dnl second arg - if mandatory, the configure will fail when no features found.
11dnl                   Optional will make configure silently continue
12dnl third arg  - a variable name where the corresponding switch would be put. If
13dnl              the variable is already defined, its contents will be overwritten.
14dnl
15dnl EXAMPLE
16dnl
17dnl  PHP_CXX_COMPILE_STDCXX(14, mandatory, MY_STDCXX_SWiTCH)
18dnl  echo $MY_STDCXX_SWITCH
19dnl
20
21dnl
22dnl PHP specific implementation start.
23dnl
24
25AC_DEFUN([PHP_CXX_COMPILE_STDCXX], [dnl
26  m4_if([$1], [11], [ax_cxx_compile_alternatives="11 0x"],
27        [$1], [14], [ax_cxx_compile_alternatives="14 1y"],
28        [$1], [17], [ax_cxx_compile_alternatives="17 1z"],
29        [$1], [20], [ax_cxx_compile_alternatives="20"],
30        [m4_fatal([invalid first argument `$1' to PHP_CXX_COMPILE_STDCXX])])[]dnl
31  m4_if([$2], [], [ax_cxx_compile_cxx$1_required=true],
32        [$2], [mandatory], [ax_cxx_compile_cxx$1_required=true],
33        [$2], [optional], [ax_cxx_compile_cxx$1_required=false],
34        [m4_fatal([invalid third argument `$2' to PHP_CXX_COMPILE_STDCXX])])[]dnl
35  AC_LANG_PUSH([C++])dnl
36  ac_success=no
37
38  dnl HP's aCC needs +std=c++11 according to:
39  dnl http://h21007.www2.hp.com/portal/download/files/unprot/aCxx/PDF_Release_Notes/769149-001.pdf
40  dnl Cray's crayCC needs "-h std=c++11"
41  for alternative in ${ax_cxx_compile_alternatives}; do
42    for switch in -std=c++${alternative} +std=c++${alternative} "-h std=c++${alternative}"; do
43      cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch])
44      AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch,
45                     $cachevar,
46          [ac_save_CXX="$CXX"
47           CXX="$CXX $switch"
48           AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])],
49            [eval $cachevar=yes],
50            [eval $cachevar=no])
51           CXX="$ac_save_CXX"])
52      if eval test x\$$cachevar = xyes; then
53        eval AS_TR_SH([$3])="$switch"
54        ac_success=yes
55        break
56      fi
57    done
58    if test x$ac_success = xyes; then
59      break
60    fi
61  done
62
63  AC_LANG_POP([C++])
64  if test x$ax_cxx_compile_cxx$1_required = xtrue; then
65    if test x$ac_success = xno; then
66      AC_MSG_ERROR([*** A compiler with support for C++$1 language features is required.])
67    fi
68  fi
69  if test x$ac_success = xno; then
70    AC_MSG_NOTICE([No compiler with C++$1 support was found])
71  fi
72  AC_SUBST(HAVE_CXX$1)
73])
74
75
76dnl
77dnl PHP specific implementation end.
78dnl The relevant part of the unchanged original implementation is below.
79dnl
80
81#
82# LICENSE
83#
84#   Copyright (c) 2008 Benjamin Kosnik <bkoz@redhat.com>
85#   Copyright (c) 2012 Zack Weinberg <zackw@panix.com>
86#   Copyright (c) 2013 Roy Stogner <roystgnr@ices.utexas.edu>
87#   Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov <sokolov@google.com>
88#   Copyright (c) 2015 Paul Norman <penorman@mac.com>
89#   Copyright (c) 2015 Moritz Klammler <moritz@klammler.eu>
90#   Copyright (c) 2016, 2018 Krzesimir Nowak <qdlacz@gmail.com>
91#   Copyright (c) 2019 Enji Cooper <yaneurabeya@gmail.com>
92#   Copyright (c) 2020 Jason Merrill <jason@redhat.com>
93#   Copyright (c) 2021 Jörn Heusipp <osmanx@problemloesungsmaschine.de>
94#
95#   Copying and distribution of this file, with or without modification, are
96#   permitted in any medium without royalty provided the copyright notice
97#   and this notice are preserved.  This file is offered as-is, without any
98#   warranty.
99
100
101dnl  Test body for checking C++11 support
102
103m4_define([_AX_CXX_COMPILE_STDCXX_testbody_11],
104  _AX_CXX_COMPILE_STDCXX_testbody_new_in_11
105)
106
107dnl  Test body for checking C++14 support
108
109m4_define([_AX_CXX_COMPILE_STDCXX_testbody_14],
110  _AX_CXX_COMPILE_STDCXX_testbody_new_in_11
111  _AX_CXX_COMPILE_STDCXX_testbody_new_in_14
112)
113
114dnl  Test body for checking C++17 support
115
116m4_define([_AX_CXX_COMPILE_STDCXX_testbody_17],
117  _AX_CXX_COMPILE_STDCXX_testbody_new_in_11
118  _AX_CXX_COMPILE_STDCXX_testbody_new_in_14
119  _AX_CXX_COMPILE_STDCXX_testbody_new_in_17
120)
121
122dnl  Test body for checking C++20 support
123
124m4_define([_AX_CXX_COMPILE_STDCXX_testbody_20],
125  _AX_CXX_COMPILE_STDCXX_testbody_new_in_11
126  _AX_CXX_COMPILE_STDCXX_testbody_new_in_14
127  _AX_CXX_COMPILE_STDCXX_testbody_new_in_17
128  _AX_CXX_COMPILE_STDCXX_testbody_new_in_20
129)
130
131
132dnl  Tests for new features in C++11
133
134m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_11], [[
135
136// If the compiler admits that it is not ready for C++11, why torture it?
137// Hopefully, this will speed up the test.
138
139#ifndef __cplusplus
140
141#error "This is not a C++ compiler"
142
143#elif __cplusplus < 201103L
144
145#error "This is not a C++11 compiler"
146
147#else
148
149namespace cxx11
150{
151
152  namespace test_static_assert
153  {
154
155    template <typename T>
156    struct check
157    {
158      static_assert(sizeof(int) <= sizeof(T), "not big enough");
159    };
160
161  }
162
163  namespace test_final_override
164  {
165
166    struct Base
167    {
168      virtual ~Base() {}
169      virtual void f() {}
170    };
171
172    struct Derived : public Base
173    {
174      virtual ~Derived() override {}
175      virtual void f() override {}
176    };
177
178  }
179
180  namespace test_double_right_angle_brackets
181  {
182
183    template < typename T >
184    struct check {};
185
186    typedef check<void> single_type;
187    typedef check<check<void>> double_type;
188    typedef check<check<check<void>>> triple_type;
189    typedef check<check<check<check<void>>>> quadruple_type;
190
191  }
192
193  namespace test_decltype
194  {
195
196    int
197    f()
198    {
199      int a = 1;
200      decltype(a) b = 2;
201      return a + b;
202    }
203
204  }
205
206  namespace test_type_deduction
207  {
208
209    template < typename T1, typename T2 >
210    struct is_same
211    {
212      static const bool value = false;
213    };
214
215    template < typename T >
216    struct is_same<T, T>
217    {
218      static const bool value = true;
219    };
220
221    template < typename T1, typename T2 >
222    auto
223    add(T1 a1, T2 a2) -> decltype(a1 + a2)
224    {
225      return a1 + a2;
226    }
227
228    int
229    test(const int c, volatile int v)
230    {
231      static_assert(is_same<int, decltype(0)>::value == true, "");
232      static_assert(is_same<int, decltype(c)>::value == false, "");
233      static_assert(is_same<int, decltype(v)>::value == false, "");
234      auto ac = c;
235      auto av = v;
236      auto sumi = ac + av + 'x';
237      auto sumf = ac + av + 1.0;
238      static_assert(is_same<int, decltype(ac)>::value == true, "");
239      static_assert(is_same<int, decltype(av)>::value == true, "");
240      static_assert(is_same<int, decltype(sumi)>::value == true, "");
241      static_assert(is_same<int, decltype(sumf)>::value == false, "");
242      static_assert(is_same<int, decltype(add(c, v))>::value == true, "");
243      return (sumf > 0.0) ? sumi : add(c, v);
244    }
245
246  }
247
248  namespace test_noexcept
249  {
250
251    int f() { return 0; }
252    int g() noexcept { return 0; }
253
254    static_assert(noexcept(f()) == false, "");
255    static_assert(noexcept(g()) == true, "");
256
257  }
258
259  namespace test_constexpr
260  {
261
262    template < typename CharT >
263    unsigned long constexpr
264    strlen_c_r(const CharT *const s, const unsigned long acc) noexcept
265    {
266      return *s ? strlen_c_r(s + 1, acc + 1) : acc;
267    }
268
269    template < typename CharT >
270    unsigned long constexpr
271    strlen_c(const CharT *const s) noexcept
272    {
273      return strlen_c_r(s, 0UL);
274    }
275
276    static_assert(strlen_c("") == 0UL, "");
277    static_assert(strlen_c("1") == 1UL, "");
278    static_assert(strlen_c("example") == 7UL, "");
279    static_assert(strlen_c("another\0example") == 7UL, "");
280
281  }
282
283  namespace test_rvalue_references
284  {
285
286    template < int N >
287    struct answer
288    {
289      static constexpr int value = N;
290    };
291
292    answer<1> f(int&)       { return answer<1>(); }
293    answer<2> f(const int&) { return answer<2>(); }
294    answer<3> f(int&&)      { return answer<3>(); }
295
296    void
297    test()
298    {
299      int i = 0;
300      const int c = 0;
301      static_assert(decltype(f(i))::value == 1, "");
302      static_assert(decltype(f(c))::value == 2, "");
303      static_assert(decltype(f(0))::value == 3, "");
304    }
305
306  }
307
308  namespace test_uniform_initialization
309  {
310
311    struct test
312    {
313      static const int zero {};
314      static const int one {1};
315    };
316
317    static_assert(test::zero == 0, "");
318    static_assert(test::one == 1, "");
319
320  }
321
322  namespace test_lambdas
323  {
324
325    void
326    test1()
327    {
328      auto lambda1 = [](){};
329      auto lambda2 = lambda1;
330      lambda1();
331      lambda2();
332    }
333
334    int
335    test2()
336    {
337      auto a = [](int i, int j){ return i + j; }(1, 2);
338      auto b = []() -> int { return '0'; }();
339      auto c = [=](){ return a + b; }();
340      auto d = [&](){ return c; }();
341      auto e = [a, &b](int x) mutable {
342        const auto identity = [](int y){ return y; };
343        for (auto i = 0; i < a; ++i)
344          a += b--;
345        return x + identity(a + b);
346      }(0);
347      return a + b + c + d + e;
348    }
349
350    int
351    test3()
352    {
353      const auto nullary = [](){ return 0; };
354      const auto unary = [](int x){ return x; };
355      using nullary_t = decltype(nullary);
356      using unary_t = decltype(unary);
357      const auto higher1st = [](nullary_t f){ return f(); };
358      const auto higher2nd = [unary](nullary_t f1){
359        return [unary, f1](unary_t f2){ return f2(unary(f1())); };
360      };
361      return higher1st(nullary) + higher2nd(nullary)(unary);
362    }
363
364  }
365
366  namespace test_variadic_templates
367  {
368
369    template <int...>
370    struct sum;
371
372    template <int N0, int... N1toN>
373    struct sum<N0, N1toN...>
374    {
375      static constexpr auto value = N0 + sum<N1toN...>::value;
376    };
377
378    template <>
379    struct sum<>
380    {
381      static constexpr auto value = 0;
382    };
383
384    static_assert(sum<>::value == 0, "");
385    static_assert(sum<1>::value == 1, "");
386    static_assert(sum<23>::value == 23, "");
387    static_assert(sum<1, 2>::value == 3, "");
388    static_assert(sum<5, 5, 11>::value == 21, "");
389    static_assert(sum<2, 3, 5, 7, 11, 13>::value == 41, "");
390
391  }
392
393  // http://stackoverflow.com/questions/13728184/template-aliases-and-sfinae
394  // Clang 3.1 fails with headers of libstd++ 4.8.3 when using std::function
395  // because of this.
396  namespace test_template_alias_sfinae
397  {
398
399    struct foo {};
400
401    template<typename T>
402    using member = typename T::member_type;
403
404    template<typename T>
405    void func(...) {}
406
407    template<typename T>
408    void func(member<T>*) {}
409
410    void test();
411
412    void test() { func<foo>(0); }
413
414  }
415
416}  // namespace cxx11
417
418#endif  // __cplusplus >= 201103L
419
420]])
421
422
423dnl  Tests for new features in C++14
424
425m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_14], [[
426
427// If the compiler admits that it is not ready for C++14, why torture it?
428// Hopefully, this will speed up the test.
429
430#ifndef __cplusplus
431
432#error "This is not a C++ compiler"
433
434#elif __cplusplus < 201402L
435
436#error "This is not a C++14 compiler"
437
438#else
439
440namespace cxx14
441{
442
443  namespace test_polymorphic_lambdas
444  {
445
446    int
447    test()
448    {
449      const auto lambda = [](auto&&... args){
450        const auto istiny = [](auto x){
451          return (sizeof(x) == 1UL) ? 1 : 0;
452        };
453        const int aretiny[] = { istiny(args)... };
454        return aretiny[0];
455      };
456      return lambda(1, 1L, 1.0f, '1');
457    }
458
459  }
460
461  namespace test_binary_literals
462  {
463
464    constexpr auto ivii = 0b0000000000101010;
465    static_assert(ivii == 42, "wrong value");
466
467  }
468
469  namespace test_generalized_constexpr
470  {
471
472    template < typename CharT >
473    constexpr unsigned long
474    strlen_c(const CharT *const s) noexcept
475    {
476      auto length = 0UL;
477      for (auto p = s; *p; ++p)
478        ++length;
479      return length;
480    }
481
482    static_assert(strlen_c("") == 0UL, "");
483    static_assert(strlen_c("x") == 1UL, "");
484    static_assert(strlen_c("test") == 4UL, "");
485    static_assert(strlen_c("another\0test") == 7UL, "");
486
487  }
488
489  namespace test_lambda_init_capture
490  {
491
492    int
493    test()
494    {
495      auto x = 0;
496      const auto lambda1 = [a = x](int b){ return a + b; };
497      const auto lambda2 = [a = lambda1(x)](){ return a; };
498      return lambda2();
499    }
500
501  }
502
503  namespace test_digit_separators
504  {
505
506    constexpr auto ten_million = 100'000'000;
507    static_assert(ten_million == 100000000, "");
508
509  }
510
511  namespace test_return_type_deduction
512  {
513
514    auto f(int& x) { return x; }
515    decltype(auto) g(int& x) { return x; }
516
517    template < typename T1, typename T2 >
518    struct is_same
519    {
520      static constexpr auto value = false;
521    };
522
523    template < typename T >
524    struct is_same<T, T>
525    {
526      static constexpr auto value = true;
527    };
528
529    int
530    test()
531    {
532      auto x = 0;
533      static_assert(is_same<int, decltype(f(x))>::value, "");
534      static_assert(is_same<int&, decltype(g(x))>::value, "");
535      return x;
536    }
537
538  }
539
540}  // namespace cxx14
541
542#endif  // __cplusplus >= 201402L
543
544]])
545
546
547dnl  Tests for new features in C++17
548
549m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_17], [[
550
551// If the compiler admits that it is not ready for C++17, why torture it?
552// Hopefully, this will speed up the test.
553
554#ifndef __cplusplus
555
556#error "This is not a C++ compiler"
557
558#elif __cplusplus < 201703L
559
560#error "This is not a C++17 compiler"
561
562#else
563
564#include <initializer_list>
565#include <utility>
566#include <type_traits>
567
568namespace cxx17
569{
570
571  namespace test_constexpr_lambdas
572  {
573
574    constexpr int foo = [](){return 42;}();
575
576  }
577
578  namespace test::nested_namespace::definitions
579  {
580
581  }
582
583  namespace test_fold_expression
584  {
585
586    template<typename... Args>
587    int multiply(Args... args)
588    {
589      return (args * ... * 1);
590    }
591
592    template<typename... Args>
593    bool all(Args... args)
594    {
595      return (args && ...);
596    }
597
598  }
599
600  namespace test_extended_static_assert
601  {
602
603    static_assert (true);
604
605  }
606
607  namespace test_auto_brace_init_list
608  {
609
610    auto foo = {5};
611    auto bar {5};
612
613    static_assert(std::is_same<std::initializer_list<int>, decltype(foo)>::value);
614    static_assert(std::is_same<int, decltype(bar)>::value);
615  }
616
617  namespace test_typename_in_template_template_parameter
618  {
619
620    template<template<typename> typename X> struct D;
621
622  }
623
624  namespace test_fallthrough_nodiscard_maybe_unused_attributes
625  {
626
627    int f1()
628    {
629      return 42;
630    }
631
632    [[nodiscard]] int f2()
633    {
634      [[maybe_unused]] auto unused = f1();
635
636      switch (f1())
637      {
638      case 17:
639        f1();
640        [[fallthrough]];
641      case 42:
642        f1();
643      }
644      return f1();
645    }
646
647  }
648
649  namespace test_extended_aggregate_initialization
650  {
651
652    struct base1
653    {
654      int b1, b2 = 42;
655    };
656
657    struct base2
658    {
659      base2() {
660        b3 = 42;
661      }
662      int b3;
663    };
664
665    struct derived : base1, base2
666    {
667        int d;
668    };
669
670    derived d1 {{1, 2}, {}, 4};  // full initialization
671    derived d2 {{}, {}, 4};      // value-initialized bases
672
673  }
674
675  namespace test_general_range_based_for_loop
676  {
677
678    struct iter
679    {
680      int i;
681
682      int& operator* ()
683      {
684        return i;
685      }
686
687      const int& operator* () const
688      {
689        return i;
690      }
691
692      iter& operator++()
693      {
694        ++i;
695        return *this;
696      }
697    };
698
699    struct sentinel
700    {
701      int i;
702    };
703
704    bool operator== (const iter& i, const sentinel& s)
705    {
706      return i.i == s.i;
707    }
708
709    bool operator!= (const iter& i, const sentinel& s)
710    {
711      return !(i == s);
712    }
713
714    struct range
715    {
716      iter begin() const
717      {
718        return {0};
719      }
720
721      sentinel end() const
722      {
723        return {5};
724      }
725    };
726
727    void f()
728    {
729      range r {};
730
731      for (auto i : r)
732      {
733        [[maybe_unused]] auto v = i;
734      }
735    }
736
737  }
738
739  namespace test_lambda_capture_asterisk_this_by_value
740  {
741
742    struct t
743    {
744      int i;
745      int foo()
746      {
747        return [*this]()
748        {
749          return i;
750        }();
751      }
752    };
753
754  }
755
756  namespace test_enum_class_construction
757  {
758
759    enum class byte : unsigned char
760    {};
761
762    byte foo {42};
763
764  }
765
766  namespace test_constexpr_if
767  {
768
769    template <bool cond>
770    int f ()
771    {
772      if constexpr(cond)
773      {
774        return 13;
775      }
776      else
777      {
778        return 42;
779      }
780    }
781
782  }
783
784  namespace test_selection_statement_with_initializer
785  {
786
787    int f()
788    {
789      return 13;
790    }
791
792    int f2()
793    {
794      if (auto i = f(); i > 0)
795      {
796        return 3;
797      }
798
799      switch (auto i = f(); i + 4)
800      {
801      case 17:
802        return 2;
803
804      default:
805        return 1;
806      }
807    }
808
809  }
810
811  namespace test_template_argument_deduction_for_class_templates
812  {
813
814    template <typename T1, typename T2>
815    struct pair
816    {
817      pair (T1 p1, T2 p2)
818        : m1 {p1},
819          m2 {p2}
820      {}
821
822      T1 m1;
823      T2 m2;
824    };
825
826    void f()
827    {
828      [[maybe_unused]] auto p = pair{13, 42u};
829    }
830
831  }
832
833  namespace test_non_type_auto_template_parameters
834  {
835
836    template <auto n>
837    struct B
838    {};
839
840    B<5> b1;
841    B<'a'> b2;
842
843  }
844
845  namespace test_structured_bindings
846  {
847
848    int arr[2] = { 1, 2 };
849    std::pair<int, int> pr = { 1, 2 };
850
851    auto f1() -> int(&)[2]
852    {
853      return arr;
854    }
855
856    auto f2() -> std::pair<int, int>&
857    {
858      return pr;
859    }
860
861    struct S
862    {
863      int x1 : 2;
864      volatile double y1;
865    };
866
867    S f3()
868    {
869      return {};
870    }
871
872    auto [ x1, y1 ] = f1();
873    auto& [ xr1, yr1 ] = f1();
874    auto [ x2, y2 ] = f2();
875    auto& [ xr2, yr2 ] = f2();
876    const auto [ x3, y3 ] = f3();
877
878  }
879
880  namespace test_exception_spec_type_system
881  {
882
883    struct Good {};
884    struct Bad {};
885
886    void g1() noexcept;
887    void g2();
888
889    template<typename T>
890    Bad
891    f(T*, T*);
892
893    template<typename T1, typename T2>
894    Good
895    f(T1*, T2*);
896
897    static_assert (std::is_same_v<Good, decltype(f(g1, g2))>);
898
899  }
900
901  namespace test_inline_variables
902  {
903
904    template<class T> void f(T)
905    {}
906
907    template<class T> inline T g(T)
908    {
909      return T{};
910    }
911
912    template<> inline void f<>(int)
913    {}
914
915    template<> int g<>(int)
916    {
917      return 5;
918    }
919
920  }
921
922}  // namespace cxx17
923
924#endif  // __cplusplus < 201703L
925
926]])
927
928
929dnl  Tests for new features in C++20
930
931m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_20], [[
932
933#ifndef __cplusplus
934
935#error "This is not a C++ compiler"
936
937#elif __cplusplus < 202002L
938
939#error "This is not a C++20 compiler"
940
941#else
942
943#include <version>
944
945namespace cxx20
946{
947
948// As C++20 supports feature test macros in the standard, there is no
949// immediate need to actually test for feature availability on the
950// Autoconf side.
951
952}  // namespace cxx20
953
954#endif  // __cplusplus < 202002L
955
956]])
957