This adds the "detection idiom" API from N4502
http://open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4502.pdf I've added the low-level parts to <type_traits>, with public aliases in <experimental/type_traits>, so that I can use them elsewhere in the library. Tested powerpc64le-linux, committed to trunk.
commit 280016fc93e2c62e1c07f2588002d422ba5ac7b1 Author: Jonathan Wakely <jwak...@redhat.com> Date: Tue Jun 30 23:19:50 2015 +0100 Implement N4502, the C++ Detection Idiom. * 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. diff --git a/libstdc++-v3/doc/xml/manual/status_cxx2017.xml b/libstdc++-v3/doc/xml/manual/status_cxx2017.xml index d110572..07e2dbe 100644 --- a/libstdc++-v3/doc/xml/manual/status_cxx2017.xml +++ b/libstdc++-v3/doc/xml/manual/status_cxx2017.xml @@ -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> diff --git a/libstdc++-v3/include/experimental/type_traits b/libstdc++-v3/include/experimental/type_traits index db78eec..b0ed3b0 100644 --- a/libstdc++-v3/include/experimental/type_traits +++ b/libstdc++-v3/include/experimental/type_traits @@ -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 diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits index e09c856..55ca916 100644 --- a/libstdc++-v3/include/std/type_traits +++ b/libstdc++-v3/include/std/type_traits @@ -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 index 0000000..45d0692 --- /dev/null +++ b/libstdc++-v3/testsuite/experimental/type_traits/detection.cc @@ -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" );