aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPedro Alves <pedro@palves.net>2017-04-26 00:49:06 +0100
committerPedro Alves <pedro@palves.net>2020-08-20 18:17:32 +0100
commitd4049ba6cb3c12a2d07b4ade5ea770158f84032d (patch)
tree9190665b511b84836a93e67d3eef487688053179
parentb70e516e89d95d06252cb04ded3397ec0a0a3212 (diff)
downloadgdb-d4049ba6cb3c12a2d07b4ade5ea770158f84032d.zip
gdb-d4049ba6cb3c12a2d07b4ade5ea770158f84032d.tar.gz
gdb-d4049ba6cb3c12a2d07b4ade5ea770158f84032d.tar.bz2
Rewrite valid-expr.h's internals in terms of the detection idiom (C++17/N4502)
An earlier attempt at doing this had failed (wouldn't work in GCCs around 4.8, IIRC), but now that I try again, it works. I suspect that my previous attempt did not use the pre C++14-safe void_t (in traits.h). I want to switch to this model because: - It's the standard detection idiom that folks will learn starting with C++17. - In the enum_flags unit tests, I have a static_assert that triggers a warning (resulting in build error), which GCC does not suppress because the warning is not being triggered in the SFINAE context. Switching to the detection idiom fixes that. Alternatively, switching to the C++03-style expression-validity checking with a varargs overload would allow addressing that, but I think that would be going backwards idiomatically speaking. - While this patch shows a net increase of lines of code, the magic being added to traits.h can be removed in a few years when we start requiring C++17. gdbsupport/ChangeLog: * traits.h (struct nonesuch, struct detector, detected_or) (detected_or_t, is_detected, detected_t, detected_or) (detected_or_t, is_detected_exact, is_detected_convertible): New. * valid-expr.h (CHECK_VALID_EXPR_INT): Use gdb::is_detected_exact.
-rw-r--r--gdbsupport/traits.h67
-rw-r--r--gdbsupport/valid-expr.h20
2 files changed, 70 insertions, 17 deletions
diff --git a/gdbsupport/traits.h b/gdbsupport/traits.h
index 2a6f006..93b609a 100644
--- a/gdbsupport/traits.h
+++ b/gdbsupport/traits.h
@@ -52,6 +52,73 @@ struct make_void { typedef void type; };
template<typename... Ts>
using void_t = typename make_void<Ts...>::type;
+/* Implementation of the detection idiom:
+
+ - http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4502.pdf
+ - http://en.cppreference.com/w/cpp/experimental/is_detected
+
+*/
+
+struct nonesuch
+{
+ nonesuch () = delete;
+ ~nonesuch () = delete;
+ nonesuch (const nonesuch &) = delete;
+ void operator= (const nonesuch &) = delete;
+};
+
+namespace detection_detail {
+/* Implementation of the detection idiom (negative case). */
+template<typename Default, typename AlwaysVoid,
+ template<typename...> class Op, typename... Args>
+struct detector
+{
+ using value_t = std::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 = std::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;
+
+} /* detection_detail */
+
+template<template<typename...> class Op, typename... Args>
+using is_detected
+ = typename detection_detail::detector<nonesuch, void, Op, Args...>::value_t;
+
+template<template<typename...> class Op, typename... Args>
+using detected_t
+ = typename detection_detail::detector<nonesuch, void, Op, Args...>::type;
+
+template<typename Default, template<typename...> class Op, typename... Args>
+using detected_or = detection_detail::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 = std::is_same<Expected, detected_t<Op, Args...>>;
+
+template<typename To, template<typename...> class Op, typename... Args>
+using is_detected_convertible
+ = std::is_convertible<detected_t<Op, Args...>, To>;
+
/* A few trait helpers, mainly stolen from libstdc++. Uppercase
because "and/or", etc. are reserved keywords. */
diff --git a/gdbsupport/valid-expr.h b/gdbsupport/valid-expr.h
index b1c8446..a22fa61 100644
--- a/gdbsupport/valid-expr.h
+++ b/gdbsupport/valid-expr.h
@@ -58,26 +58,12 @@
#define CHECK_VALID_EXPR_INT(TYPENAMES, TYPES, VALID, EXPR_TYPE, EXPR) \
namespace CONCAT (check_valid_expr, __LINE__) { \
\
- template<typename, typename, typename = void> \
- struct is_valid_expression \
- : std::false_type {}; \
- \
template <TYPENAMES> \
- struct is_valid_expression<TYPES, gdb::void_t<decltype (EXPR)>> \
- : std::true_type {}; \
+ using archetype = decltype (EXPR); \
\
- static_assert (is_valid_expression<TYPES>::value == VALID, \
+ static_assert (gdb::is_detected_exact<EXPR_TYPE, \
+ archetype, TYPES>::value == VALID, \
""); \
- \
- template<TYPENAMES, typename = void> \
- struct is_same_type \
- : std::is_same<EXPR_TYPE, void> {}; \
- \
- template <TYPENAMES> \
- struct is_same_type<TYPES, gdb::void_t<decltype (EXPR)>> \
- : std::is_same<EXPR_TYPE, decltype (EXPR)> {}; \
- \
- static_assert (is_same_type<TYPES>::value, ""); \
} /* namespace */
/* A few convenience macros that support expressions involving a