changeset 60768:4549a3e87a25

8251274: Provide utilities for function SFINAE using extra template parameters Summary: Added ENABLE_IF macro. Reviewed-by: eosterlund, lfoltan
author kbarrett
date Tue, 01 Sep 2020 21:49:20 -0400
parents d06cd81174ee
children 33aa4ce7622f
files src/hotspot/share/metaprogramming/enableIf.hpp
diffstat 1 files changed, 78 insertions(+), 17 deletions(-) [+]
line wrap: on
line diff
--- a/src/hotspot/share/metaprogramming/enableIf.hpp	Tue Sep 01 17:29:34 2020 -0700
+++ b/src/hotspot/share/metaprogramming/enableIf.hpp	Tue Sep 01 21:49:20 2020 -0400
@@ -1,5 +1,5 @@
- * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved.
  * This code is free software; you can redistribute it and/or modify it
@@ -25,23 +25,84 @@
-#include "memory/allocation.hpp"
+#include "metaprogramming/logical.hpp"
+#include <type_traits>
+// Retained temporarily for backward compatibility.
+// For function template SFINAE, use the ENABLE_IF macro below.
+// For class template SFINAE, use std::enable_if_t directly.
+template<bool cond, typename T = void>
+using EnableIf = std::enable_if<cond, T>;
-// This metaprogramming tool allows explicitly enabling and disabling overloads
-// of member functions depending on whether the condition B holds true.
-// For example typename EnableIf<IsPointer<T>::value>::type func(T ptr) would
-// only become an overload the compiler chooses from if the type T is a pointer.
-// If it is not, then the template definition is not expanded and there will be
-// no compiler error if there is another overload of func that is selected when
-// T is not a pointer. Like for example
-// typename EnableIf<!IsPointer<T>::value>::type func(T not_ptr)
+// ENABLE_IF(Condition...)
+// This macro can be used in a function template parameter list to control
+// the presence of that overload via SFINAE.
+// Condition must be a constant expression whose value is convertible to
+// bool.  The Condition is captured as a variadic macro parameter so that it
+// may contain unparenthesized commas.
+// An example of the usage of the ENABLE_IF macro is
+// template<typename T,
+//          ENABLE_IF(std::is_integral<T>::value),
+//          ENABLE_IF(std::is_signed<T>::value)>
+// void foo(T x) { ... }
+// That definition will not be considered in a call to foo unless T is a
+// signed integral type.
+// An alternative to two ENABLE_IF parameters would be single parameter
+// that is a conjunction of the expressions.  The benefit of multiple
+// ENABLE_IF parameters is the compiler may provide more information in
+// certain error contexts.
+// Details:
+// With C++98/03 there are 2 ways to use enable_if with function templates:
+// (1) As the return type
+// (2) As an extra parameter
+// C++11 adds another way, using an extra anonymous non-type template
+// parameter with a default value, i.e.
+//   std::enable_if_t<CONDITION, int> = 0
+// (The left-hand side is the 'int' type of the anonymous parameter.  The
+// right-hand side is the default value.  The use of 'int' and '0' are
+// conventional; the specific type and value don't matter, so long as they
+// are compatible.)
+// Compared to (1) this has the benefit of less cluttered syntax for the
+// function signature.  Compared to (2) it avoids polluting the signature
+// with dummy extra parameters.  And there are cases where this new approach
+// can be used while neither of the others is even possible.
+// Using an extra template parameter is somewhat syntactically complex, with
+// a number of details to get right.  However, that complexity can be
+// largely hidden using a macro, resulting in more readable uses of SFINAE
+// for function templates.
+// The Condition must be wrapped in parenthesis in the expansion. Otherwise,
+// a '>' operator in the expression may be misinterpreted as the end of the
+// template parameter list.  But rather than simply wrapping in parenthesis,
+// Condition is wrapped in an explicit conversion to bool, so the value need
+// not be *implicitly* convertible.
+// There is a problem when Condition is not dependent on any template
+// parameter.  Such a Condition will be evaluated at template definition
+// time, as part of template type checking.  If Condition is false, that
+// will result in a compile-time error rather than the desired SFINAE
+// exclusion.  A solution is to add a preceding dummy type template
+// parameter defaulting to 'int' and use that as the result type for
+// enable_if_t, thereby making it dependent.  This situation is sufficiently
+// rare that no additional macro support is provided for it; just use the
+// underlying enable_if_t directly.  (There is an automatic macro-based
+// solution, but it involves the __COUNTER__ extension.)
-template <bool B, typename T = void>
-struct EnableIf: AllStatic {};
-template <typename T>
-struct EnableIf<true, T>: AllStatic {
-  typedef T type;
+#define ENABLE_IF(...) \
+  std::enable_if_t<bool(__VA_ARGS__), int> = 0