blob: 73dce0dd12034d9c69ac1072c6e947a3a697c49b [file] [log] [blame]
/*
* Copyright (c) 2017, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
#ifndef SHARE_METAPROGRAMMING_ENABLEIF_HPP
#define SHARE_METAPROGRAMMING_ENABLEIF_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>;
// ENABLE_IF(Condition...)
// ENABLE_IF_SDEFN(Condition...)
//
// The ENABLE_IF macro can be used in a function template parameter list to
// control the presence of that overload via SFINAE.
//
// When the declaration and definition of a function template are separate,
// only the declaration can use ENABLE_IF in the template parameter list.
// The definition should instead use ENABLE_IF_SDEFN with an _equivalent_
// (C++14 14.4 and 14.5.6.1) Condition for the corresponding template
// parameter. ("SDEFN" is short for "SEPARATE_DEFINITION".)
//
// 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.
//
// One of those details is that a function template definition that is
// separate from its declaration cannot have a default value. Thus,
// ENABLE_IF can't be used in such a definition. But the type expression in
// the separate definition must be equivalent (C++14 14.4 and 14.5.6.1) to
// that in the declaration. The ENABLE_IF_SDEFN macro provides the common
// code for the separate definition that must match the corresponding
// declaration code at the token level.
//
// 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. This situation is sufficiently rare that no additional
// macro support is provided for it. (One solution is to add a new
// type parameter defaulted to the type being checked in Condition, and
// use that new parameter instead in Condition. There is an automatic
// macro-based solution, but it involves the __COUNTER__ extension.)
//
// Some references suggest a different approach to using a template
// parameter for SFINAE. An anonymous type parameter with a default type
// that uses std::enable_if can also be used in some cases, i.e.
//
// typename = std::enable_if_t<CONDITION>
//
// However, this doesn't work when there are overloads that need to be
// selected amongst via SFINAE. Two signatures that differ only in a
// template parameter default are not distinct overloads, they are multiple
// definitions of the same function.
//
// Some versions of gcc permit ENABLE_IF to be used in some separate
// definitions. Other toolchains reject such usage.
//
// The expansion of ENABLE_IF doesn't use ENABLE_IF_SDEFN (or both use a
// common helper) because of issues with the Visual Studio preprocessor's
// handling of variadic macros.
#define ENABLE_IF(...) \
std::enable_if_t<bool(__VA_ARGS__), int> = 0
#define ENABLE_IF_SDEFN(...) \
std::enable_if_t<bool(__VA_ARGS__), int>
#endif // SHARE_METAPROGRAMMING_ENABLEIF_HPP