Implement the 'detection idiom' from LFTS v2
authorMarshall Clow <mclow.lists@gmail.com>
Sat, 26 Nov 2016 15:49:40 +0000 (15:49 +0000)
committerMarshall Clow <mclow.lists@gmail.com>
Sat, 26 Nov 2016 15:49:40 +0000 (15:49 +0000)
llvm-svn: 287981

libcxx/include/experimental/type_traits
libcxx/test/std/experimental/utilities/meta/meta.detect/detected_or.pass.cpp [new file with mode: 0644]
libcxx/test/std/experimental/utilities/meta/meta.detect/detected_t.pass.cpp [new file with mode: 0644]
libcxx/test/std/experimental/utilities/meta/meta.detect/is_detected.pass.cpp [new file with mode: 0644]
libcxx/test/std/experimental/utilities/meta/meta.detect/is_detected_convertible.pass.cpp [new file with mode: 0644]
libcxx/test/std/experimental/utilities/meta/meta.detect/is_detected_exact.pass.cpp [new file with mode: 0644]

index ae49fc1..524d980 100644 (file)
@@ -172,6 +172,45 @@ inline namespace fundamentals_v1 {
   template <class T>
     using raw_invocation_type_t = typename raw_invocation_type<T>::type;
 
+  // 3.3.3, Logical operator traits
+  template<class... B> struct conjunction;
+  template<class... B> constexpr bool conjunction_v = conjunction<B...>::value;
+  template<class... B> struct disjunction;
+  template<class... B> constexpr bool disjunction_v = disjunction<B...>::value;
+  template<class B> struct negation;
+  template<class B> constexpr bool negation_v = negation<B>::value;
+
+  // 3.3.4, Detection idiom
+  template <class...> using void_t = void;
+
+  struct nonesuch {
+    nonesuch() = delete;
+    ~nonesuch() = delete;
+    nonesuch(nonesuch const&) = delete;
+    void operator=(nonesuch const&) = delete;
+  };
+
+  template <template<class...> class Op, class... Args>
+    using is_detected = see below;
+  template <template<class...> class Op, class... Args>
+    constexpr bool is_detected_v = is_detected<Op, Args...>::value;
+  template <template<class...> class Op, class... Args>
+    using detected_t = see below;
+  template <class Default, template<class...> class Op, class... Args>
+    using detected_or = see below;
+  template <class Default, template<class...> class Op, class... Args>
+    using detected_or_t = typename detected_or<Default, Op, Args...>::type;
+  template <class Expected, template<class...> class Op, class... Args>
+    using is_detected_exact = is_same<Expected, detected_t<Op, Args...>>;
+  template <class Expected, template<class...> class Op, class... Args>
+    constexpr bool is_detected_exact_v
+      = is_detected_exact<Expected, Op, Args...>::value;
+  template <class To, template<class...> class Op, class... Args>
+     using is_detected_convertible = is_convertible<detected_t<Op, Args...>, To>;
+  template <class To, template<class...> class Op, class... Args>
+     constexpr bool is_detected_convertible_v
+       = is_detected_convertible<To, Op, Args...>::value;  
+
 } // namespace fundamentals_v1
 } // namespace experimental
 } // namespace std
@@ -420,6 +459,52 @@ template <class _Tp>
 using raw_invocation_type_t = typename raw_invocation_type<_Tp>::type;
 */
 
+// 3.3.4, Detection idiom
+template <class...> using void_t = void;
+
+struct nonesuch {
+    nonesuch()  = delete;
+    ~nonesuch() = delete;
+    nonesuch      (nonesuch const&) = delete;
+    void operator=(nonesuch const&) = delete;
+  };
+
+template <class _Default, class _AlwaysVoid, template <class...> class _Op, class... _Args>
+struct _DETECTOR {
+   using value_t = false_type;
+   using type = _Default;
+   };
+
+template <class _Default, template <class...> class _Op, class... _Args>
+struct _DETECTOR<_Default, void_t<_Op<_Args...>>, _Op, _Args...> {
+   using value_t = true_type;
+   using type = _Op<_Args...>;
+   };
+     
+
+template <template<class...> class _Op, class... _Args>
+  using is_detected = typename _DETECTOR<nonesuch, void, _Op, _Args...>::value_t;
+template <template<class...> class _Op, class... _Args>
+  using detected_t = typename _DETECTOR<nonesuch, void, _Op, _Args...>::type;
+template <template<class...> class _Op, class... _Args>
+  _LIBCPP_CONSTEXPR bool is_detected_v = is_detected<_Op, _Args...>::value;
+
+template <class Default, template<class...> class _Op, class... _Args>
+  using detected_or = _DETECTOR<Default, void, _Op, _Args...>;
+template <class Default, template<class...> class _Op, class... _Args>
+  using detected_or_t = typename detected_or<Default, _Op, _Args...>::type;
+
+template <class Expected, template<class...> class _Op, class... _Args>
+  using is_detected_exact = is_same<Expected, detected_t<_Op, _Args...>>;
+template <class Expected, template<class...> class _Op, class... _Args>
+  _LIBCPP_CONSTEXPR bool is_detected_exact_v = is_detected_exact<Expected, _Op, _Args...>::value;
+
+template <class To, template<class...> class _Op, class... _Args>
+  using is_detected_convertible = is_convertible<detected_t<_Op, _Args...>, To>;
+template <class To, template<class...> class _Op, class... _Args>
+  _LIBCPP_CONSTEXPR bool is_detected_convertible_v = is_detected_convertible<To, _Op, _Args...>::value;  
+
+
 _LIBCPP_END_NAMESPACE_LFTS
 
 #endif /* _LIBCPP_STD_VER > 11 */
diff --git a/libcxx/test/std/experimental/utilities/meta/meta.detect/detected_or.pass.cpp b/libcxx/test/std/experimental/utilities/meta/meta.detect/detected_or.pass.cpp
new file mode 100644 (file)
index 0000000..ffce814
--- /dev/null
@@ -0,0 +1,40 @@
+//===----------------------------------------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is dual licensed under the MIT and the University of Illinois Open
+// Source Licenses. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++98, c++03, c++11
+// <experimental/type_traits>
+
+#include <experimental/type_traits>
+#include <string>
+
+#include "test_macros.h"
+
+namespace ex = std::experimental;
+
+template <typename T>
+  using hasFoo = typename T::Foo;
+  
+struct yesFoo {
+    using Foo = int;
+};
+
+struct noFoo {
+};
+
+
+template <typename T, typename Res>
+void test() {
+    static_assert( std::is_same<Res, typename ex::detected_or  <double, hasFoo, T>::type>::value, "" );
+    static_assert( std::is_same<Res, typename ex::detected_or_t<double, hasFoo, T>      >::value, "" );
+}
+
+int main () {
+    test<yesFoo, int>();
+    test<noFoo, double>();
+}
diff --git a/libcxx/test/std/experimental/utilities/meta/meta.detect/detected_t.pass.cpp b/libcxx/test/std/experimental/utilities/meta/meta.detect/detected_t.pass.cpp
new file mode 100644 (file)
index 0000000..136fb06
--- /dev/null
@@ -0,0 +1,48 @@
+//===----------------------------------------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is dual licensed under the MIT and the University of Illinois Open
+// Source Licenses. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++98, c++03, c++11
+// <experimental/type_traits>
+
+#include <experimental/type_traits>
+#include <string>
+
+#include "test_macros.h"
+
+namespace ex = std::experimental;
+
+template <typename T>
+  using callFoo = decltype(std::declval<T&>().Foo());
+  
+struct yesFoo {
+    int Foo() { return 0; }
+};
+
+struct noFoo {
+};
+
+struct wrongFoo {
+    std::string Foo() { return ""; }
+};
+
+struct convertibleFoo {
+    long Foo() { return 0; }
+};
+
+
+template <typename T, typename Res>
+void test() {
+    static_assert( std::is_same<Res, typename ex::detected_t<callFoo, T>>::value, "" );
+}
+
+int main () {
+    test<yesFoo, int>();
+    test<noFoo, ex::nonesuch>();  // lookup failure returns nonesuch
+    test<wrongFoo, std::string>();
+}
diff --git a/libcxx/test/std/experimental/utilities/meta/meta.detect/is_detected.pass.cpp b/libcxx/test/std/experimental/utilities/meta/meta.detect/is_detected.pass.cpp
new file mode 100644 (file)
index 0000000..d8528a2
--- /dev/null
@@ -0,0 +1,37 @@
+//===----------------------------------------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is dual licensed under the MIT and the University of Illinois Open
+// Source Licenses. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++98, c++03, c++11
+// <experimental/type_traits>
+
+#include <experimental/type_traits>
+#include <string>
+
+#include "test_macros.h"
+
+namespace ex = std::experimental;
+
+template <typename T>
+  using copy_assign_t = decltype(std::declval<T&>() = std::declval<T const &>());
+  
+struct not_assignable {
+    not_assignable & operator=(const not_assignable&) = delete;
+};
+
+template <typename T, bool b>
+void test() {
+    static_assert( b == ex::is_detected  <copy_assign_t, T>::value, "" );
+    static_assert( b == ex::is_detected_v<copy_assign_t, T>, "" );
+}
+
+int main () {
+    test<int, true>();
+    test<std::string, true>();
+    test<not_assignable, false>();
+}
diff --git a/libcxx/test/std/experimental/utilities/meta/meta.detect/is_detected_convertible.pass.cpp b/libcxx/test/std/experimental/utilities/meta/meta.detect/is_detected_convertible.pass.cpp
new file mode 100644 (file)
index 0000000..8d1e0ae
--- /dev/null
@@ -0,0 +1,50 @@
+//===----------------------------------------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is dual licensed under the MIT and the University of Illinois Open
+// Source Licenses. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++98, c++03, c++11
+// <experimental/type_traits>
+
+#include <experimental/type_traits>
+#include <string>
+
+#include "test_macros.h"
+
+namespace ex = std::experimental;
+
+template <typename T>
+  using callFoo = decltype(std::declval<T&>().Foo());
+  
+struct yesFoo {
+    int Foo() { return 0; }
+};
+
+struct noFoo {
+};
+
+struct wrongFoo {
+    std::string Foo() { return ""; }
+};
+
+struct convertibleFoo {
+    long Foo() { return 0; }
+};
+
+
+template <typename T, bool b>
+void test() {
+    static_assert( b == ex::is_detected_convertible  <int, callFoo, T>::value, "" );
+    static_assert( b == ex::is_detected_convertible_v<int, callFoo, T>, "" );
+}
+
+int main () {
+    test<yesFoo, true>();
+    test<noFoo, false>();
+    test<wrongFoo, false>();
+    test<convertibleFoo, true>();
+}
diff --git a/libcxx/test/std/experimental/utilities/meta/meta.detect/is_detected_exact.pass.cpp b/libcxx/test/std/experimental/utilities/meta/meta.detect/is_detected_exact.pass.cpp
new file mode 100644 (file)
index 0000000..e9e5d8c
--- /dev/null
@@ -0,0 +1,49 @@
+//===----------------------------------------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is dual licensed under the MIT and the University of Illinois Open
+// Source Licenses. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++98, c++03, c++11
+// <experimental/type_traits>
+
+#include <experimental/type_traits>
+#include <string>
+
+#include "test_macros.h"
+
+namespace ex = std::experimental;
+
+template <typename T>
+  using callFoo = decltype(std::declval<T&>().Foo());
+  
+struct yesFoo {
+    int Foo() { return 0; }
+};
+
+struct noFoo {
+};
+
+struct wrongFoo {
+    std::string Foo() { return ""; }
+};
+
+struct convertibleFoo {
+    long Foo() { return 0; }
+};
+
+template <typename T, bool b>
+void test() {
+    static_assert( b == ex::is_detected_exact  <int, callFoo, T>::value, "" );
+    static_assert( b == ex::is_detected_exact_v<int, callFoo, T>, "" );
+}
+
+int main () {
+    test<yesFoo, true>();
+    test<noFoo, false>();
+    test<wrongFoo, false>();
+    test<convertibleFoo, false>();
+}