Implement N4502, the C++ Detection Idiom.
authorJonathan Wakely <jwakely@redhat.com>
Wed, 1 Jul 2015 12:23:54 +0000 (13:23 +0100)
committerJonathan Wakely <redi@gcc.gnu.org>
Wed, 1 Jul 2015 12:23:54 +0000 (13:23 +0100)
* doc/xml/manual/status_cxx2017.xml: Update status table.
* include/experimental/type_traits (void_t, is_detected,
is_detected_v, detected_t, detected_or, detected_or_t,
is_detected_exact, is_detected_exact_v, is_detected_convertible,
is_detected_convertible_v): Define.
* include/std/type_traits (__detector, __detected_or, __detected_or_t,
__detected_or_t_): Define.
* testsuite/experimental/type_traits/detection.cc: New.

From-SVN: r225242

libstdc++-v3/ChangeLog
libstdc++-v3/doc/xml/manual/status_cxx2017.xml
libstdc++-v3/include/experimental/type_traits
libstdc++-v3/include/std/type_traits
libstdc++-v3/testsuite/experimental/type_traits/detection.cc [new file with mode: 0644]

index f159f786b7d553c81a81a3f5876d5f72e10ba260..e349937100cc41f4f472bfe1eedff4ef00ce2df0 100644 (file)
@@ -1,3 +1,14 @@
+2015-07-01  Jonathan Wakely  <jwakely@redhat.com>
+
+       * doc/xml/manual/status_cxx2017.xml: Update status table.
+       * include/experimental/type_traits (void_t, is_detected,
+       is_detected_v, detected_t, detected_or, detected_or_t,
+       is_detected_exact, is_detected_exact_v, is_detected_convertible,
+       is_detected_convertible_v): Define.
+       * include/std/type_traits (__detector, __detected_or, __detected_or_t,
+       __detected_or_t_): Define.
+       * testsuite/experimental/type_traits/detection.cc: New.
+
 2015-06-30  Jonathan Wakely  <jwakely@redhat.com>
 
        * doc/Makefile.am (stamp-pdf-doxygen): Grep for LaTeX errors in log.
index d110572e57a88ec464bc4a44c6f1963d2723a337..07e2dbeac398cb03d740c80058b9d1b69125cc9c 100644 (file)
@@ -328,14 +328,13 @@ not in any particular release.
     </row>
 
     <row>
-      <?dbhtml bgcolor="#C8B0B0" ?>
       <entry>
        <link xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4502.pdf">
          N4502
        </link>
       </entry>
       <entry> Support for the C++ Detection Idiom, V2 </entry>
-      <entry>N</entry>
+      <entry>Y</entry>
       <entry>Library Fundamentals 2 TS</entry>
     </row>
 
index db78eec9ec37c7f3f478a395b011eed39b71db21..b0ed3b0fa664caedd25b22aa041c01609d90a119 100644 (file)
@@ -220,6 +220,59 @@ template <typename _From, typename _To>
   // raw_invocation_type_t (still unimplemented)
 _GLIBCXX_END_NAMESPACE_VERSION
 } // namespace fundamentals_v1
+
+inline namespace fundamentals_v2
+{
+_GLIBCXX_BEGIN_NAMESPACE_VERSION
+
+#define __cpp_lib_experimental_detect 201505
+
+// [meta.detect]
+
+template<typename...> using void_t = void;
+
+struct nonesuch
+{
+  nonesuch() = delete;
+  ~nonesuch() = delete;
+  nonesuch(nonesuch const&) = delete;
+  void operator=(nonesuch const&) = delete;
+};
+
+template<template<typename...> class _Op, typename... _Args>
+  using is_detected
+    = typename std::__detector<nonesuch, void, _Op, _Args...>::value_t;
+
+template<template<typename...> class _Op, typename... _Args>
+  constexpr bool is_detected_v = is_detected<_Op, _Args...>::value;
+
+template<template<typename...> class _Op, typename... _Args>
+  using detected_t
+    = typename std::__detector<nonesuch, void, _Op, _Args...>::type;
+
+template<typename _Default, template<typename...> class _Op, typename... _Args>
+  using detected_or = std::__detected_or<_Default, _Op, _Args...>;
+
+template<typename _Default, template<typename...> class _Op, typename... _Args>
+  using detected_or_t = typename detected_or<_Default, _Op, _Args...>::type;
+
+template<typename Expected, template<typename...> class _Op, typename... _Args>
+  using is_detected_exact = is_same<Expected, detected_t<_Op, _Args...>>;
+
+template<typename Expected, template<typename...> class _Op, typename... _Args>
+  constexpr bool is_detected_exact_v
+    = is_detected_exact<Expected, _Op, _Args...>::value;
+
+template<typename _To, template<typename...> class _Op, typename... _Args>
+  using is_detected_convertible
+    = is_convertible<detected_t<_Op, _Args...>, _To>;
+
+template<typename _To, template<typename...> class _Op, typename... _Args>
+  constexpr bool is_detected_convertible_v
+    = is_detected_convertible<_To, _Op, _Args...>::value;
+
+_GLIBCXX_END_NAMESPACE_VERSION
+} // namespace fundamentals_v2
 } // namespace experimental
 } // namespace std
 
index e09c856c934e595c26a634a82fe51a4a31a1ea38..55ca9167b7b37ed45d75c27d57acba944c265310 100644 (file)
@@ -2417,6 +2417,41 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename...> using void_t = void;
 #endif
 
+  /// Implementation of the detection idiom (negative case).
+  template<typename _Default, typename _AlwaysVoid,
+          template<typename...> class _Op, typename... _Args>
+    struct __detector
+    {
+      using value_t = false_type;
+      using type = _Default;
+    };
+
+  /// Implementation of the detection idiom (positive case).
+  template<typename _Default, template<typename...> class _Op,
+           typename... _Args>
+    struct __detector<_Default, __void_t<_Op<_Args...>>, _Op, _Args...>
+    {
+      using value_t = true_type;
+      using type = _Op<_Args...>;
+    };
+
+  // Detect whether _Op<_Args...> is a valid type, use _Default if not.
+  template<typename _Default, template<typename...> class _Op,
+          typename... _Args>
+    using __detected_or = __detector<_Default, void, _Op, _Args...>;
+
+  // _Op<_Args...> if that is a valid type, otherwise _Default.
+  template<typename _Default, template<typename...> class _Op,
+          typename... _Args>
+    using __detected_or_t
+      = typename __detected_or<_Default, _Op, _Args...>::type;
+
+  // _Op<_Args...> if that is a valid type, otherwise _Default<_Args...>.
+  template<template<typename...> class _Default,
+          template<typename...> class _Op, typename... _Args>
+    using __detected_or_t_ =
+      __detected_or_t<_Default<_Args...>, _Op, _Args...>;
+
   /// @} group metaprogramming
 
   /**
diff --git a/libstdc++-v3/testsuite/experimental/type_traits/detection.cc b/libstdc++-v3/testsuite/experimental/type_traits/detection.cc
new file mode 100644 (file)
index 0000000..45d0692
--- /dev/null
@@ -0,0 +1,85 @@
+// Copyright (C) 2015 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++14" }
+// { dg-do compile }
+
+#include <experimental/type_traits>
+
+using std::declval;
+using std::ptrdiff_t;
+using std::experimental::is_detected;
+using std::experimental::is_detected_exact;
+using std::experimental::detected_or_t;
+using std::experimental::is_same_v;
+
+// Examples taken from N4502
+
+// archetypal helper alias for a copy assignment operation:
+template <class T>
+using copy_assign_t = decltype(declval<T&>() = declval<T const &>());
+
+// plausible implementation for the is_assignable type trait:
+template <class T>
+  using is_copy_assignable = is_detected<copy_assign_t, T>;
+
+// plausible implementation for an augmented is_assignable type trait
+// that also checks the return type:
+template <class T>
+  using is_canonical_copy_assignable = is_detected_exact<T&, copy_assign_t, T>;
+
+struct A { };
+struct B { B& operator=(const B&); };
+struct C { void operator=(const C&); };
+struct D { D& operator=(D&); };
+struct E { E& operator=(E&&); };
+
+static_assert( is_copy_assignable<A>::value,  "A is copy assignable" );
+static_assert( is_copy_assignable<B>::value,  "B is copy assignable" );
+static_assert( is_copy_assignable<C>::value,  "C is copy assignable" );
+static_assert( !is_copy_assignable<D>::value, "D is not copy assignable" );
+static_assert( !is_copy_assignable<E>::value,  "E is not copy assignable" );
+
+static_assert( is_canonical_copy_assignable<A>::value,
+               "A has canonical copy assignment" );
+static_assert( is_canonical_copy_assignable<B>::value,
+               "B has canonical copy assignment" );
+static_assert( !is_canonical_copy_assignable<C>::value,
+               "C does not have canonical copy assignment" );
+static_assert( !is_canonical_copy_assignable<D>::value,
+               "D does not have canonical copy assignment" );
+static_assert( !is_canonical_copy_assignable<E>::value,
+               "E does not have canonical copy assignment" );
+
+// archetypal helper alias for a particular type member:
+template <class T>
+  using diff_t = typename T::difference_type;
+// alias the type member, if it exists, otherwise alias ptrdiff_t:
+template <class Ptr>
+  using difference_type = detected_or_t<ptrdiff_t, diff_t, Ptr>;
+
+struct has { using difference_type = char; };
+struct has_not { };
+struct inherits : has { };
+struct hides : private has { };
+struct reveals : private has { using has::difference_type; };
+
+static_assert( is_same_v<difference_type<has>,      char>,      "has" );
+static_assert( is_same_v<difference_type<has_not>,  ptrdiff_t>, "has not" );
+static_assert( is_same_v<difference_type<inherits>, char>,      "inherits" );
+static_assert( is_same_v<difference_type<hides>,    ptrdiff_t>, "hides" );
+static_assert( is_same_v<difference_type<reveals>,  char>,      "reveals" );