// bsls_consteval.h                                                   -*-C++-*-
#ifndef INCLUDED_BSLS_CONSTEVAL
#define INCLUDED_BSLS_CONSTEVAL

#include <bsls_ident.h>
BSLS_IDENT("$Id: $")

//@PURPOSE: Provide macros related to compile-time evaluation.
//
//@MACROS:
//  BSLS_CONSTEVAL_IS_CONSTANT_EVALUATED: 'true' during constant evaluation
//  BSLS_CONSTEVAL_IS_CONSTANT_EVALUATED_IS_ACTIVE: undefined if inactive
//  BSLS_CONSTEVAL_CONSTEXPR: 'constexpr' if 'IS_ACTIVE' defined
//  BSLS_CONSTEVAL_CONSTEXPR_MEMBER: 'constexpr' if 'IS_ACTIVE', else 'const'
//
//@DESCRIPTION: This component provides preprocessor macros that, when
// possible, will identify if a function is being evaluated at compile time.
// This enables branching to avoid the use of constructs that would not be
// valid to evaluate at compile time.  When available, the
// 'std::is_constant_evaluated()' function will be used.  On some platforms
// where that is unavailable a compiler intrinsic will be used instead.
// Finally, when no option for this functionality is available the
// 'BSLS_CONSTEVAL_IS_CONSTANT_EVALUATED' macro will expand to nothing and
// 'BSLS_CONSTEVAL_IS_CONSTANT_EVALUATED_IS_ACTIVE' will not be defined.  To
// ease writing declarations of functions where there is conditionally
// available compile-time behavior, 'BSLS_CONSTEVAL_CONSTEXPR' is defined to be
// equivalent to 'constexpr' if
// 'BSLS_CONSTEVAL_IS_CONSTANT_EVALUATED_IS_ACTIVE' is defined.
//
///Macro Reference
///---------------
// This section documents the preprocessor macros defined in this component.
//
//: 'BSLS_CONSTEVAL_IS_CONSTANT_EVALUATED'
//:     This macro expands to 'std::is_constant_evaluated' when it is
//:     available, or to a compiler intrinsic with equivalent functionality if
//:     available, else, it expands to nothing.
//
//: 'BSLS_CONSTEVAL_IS_CONSTANT_EVALUATED_IS_ACTIVE'
//:     This macro is defined to 1 if 'BSLS_CONSTEVAL_IS_CONSTANT_EVALUATED'
//:     can be used to identify compile-time evaluation, and is undefined
//:     otherwise.
//
//: 'BSLS_CONSTEVAL_CONSTEXPR'
//:     This macro is defined to be 'constexpr' if
//:     'BSLS_CONSTEVAL_IS_CONSTANT_EVALUATED_IS_ACTIVE' is defined, otherwise
//:     it is defined as empty.
//
//: 'BSLS_CONSTEVAL_CONSTEXPR_MEMBER'
//:      This macro is defined to be 'constexpr' if
//:      'BSLS_CONSTEVAL_IS_CONSTANT_EVALUATED_IS_ACTIVE' is defined;
//:      otherwise it is 'const'.  This should be applied to variables and
//:      member variables that should be initialized by a function that is
//:      'BSLS_CONSTEVAL_CONSTEXPR'.
//
///Usage
///-----
// This section illustrates intended use of this component.
//
///Example 1: BSLS_CONSTEVAL_CONSTEXPR 'compute' with output
///---------------------------------------------------------
// In this simple example, the macros are used to determine when it is
// permissible to log a message to 'stdout'.
//..
//  BSLS_CONSTEVAL_CONSTEXPR int compute()
//      // Return '23' if the invocation is evaluated at compile time and that
//      // is detectable, otherwise print a diagnostic message to 'stdout' and
//      // return '17'.
//  {
//  #ifdef BSLS_CONSTEVAL_IS_CONSTANT_EVALUATED_IS_ACTIVE
//      if (BSLS_CONSTEVAL_IS_CONSTANT_EVALUATED) {
//          return 23;                                                // RETURN
//      }
//  #endif
//      printf("Computing value\n");
//      return 17;                                                    // RETURN
//  }
//..
// Now we define a 'test1' function to invoke 'compute' in different contexts.
// This function can be evaluated both at runtime and at compile time without
// errors.  Below, this function is evaluated in both cases, and the difference
// in behavior is observed.
//..
//  void test1()
//      // Invoke 'compute' in both a const and non-const initialization,
//      // verifying the expected results.
//  {
//                                      int i = compute();
//      BSLS_CONSTEVAL_CONSTEXPR_MEMBER int j = compute();
//  #ifdef BSLS_CONSTEVAL_IS_CONSTANT_EVALUATED_IS_ACTIVE
//      ASSERT(17 == i);
//      ASSERT(23 == j);
//  #else
//      ASSERT(17 == i);
//      ASSERT(17 == j);
//  #endif
//  }
//..
// When '17 == i' or '17 == j', 'compute' will write "Computing value/n" to
// 'stdout'.  So, this message will be written once or twice depending on
// whether 'BSLS_CONSTEVAL_IS_CONSTANT_EVALUATED_IS_ACTIVE' is defined.  The
// variable 'j' will always be 'const', and on platforms where the 'compute'
// function supports being 'constexpr', i.e. those where it can use
// 'BSLS_CONSTEVAL_IS_CONSTANT_EVALUATED' to suppress its use of standard
// output, 'j' will also be 'constexpr' and its value will be computed at
// compile time.
//
///Example 2: Evolving a 'constexpr' function
///------------------------------------------
// Consider the situation where there exist two implementations of the same
// algorithm, one of which satisfies the (stringent and pessimizing)
// requirements needed to be a C++11 'constexpr' function, the other of which
// takes advantage of runtime optimizations (such as hardware acceleration,
// exceptions, or non-'constexpr' third-party libraries) that are not available
// at compile time on any platform:
//..
//  int runtimeCompute(int input);
//  BSLS_KEYWORD_CONSTEXPR int compiletimeCompute(int input);
//      // Return a complicated computed value based on the specified 'input'.
//..
// Assuming these functions were introduced long ago, it is likely they are
// heavily used wherever valid throughout a codebase.  The 'compiletimeCompute'
// function is likely used to initialize many 'const' and 'constexpr'
// variables, but also potentially used in many runtime-only expressions, or as
// part of other 'constexpr' functions that are themselves sometimes used at
// runtime.  The 'runtimeCompute' function, similarly, is likely used in many
// contexts that could become 'constexpr' or be evaluated at compile time, but
// is hindering that due to itself not being 'constexpr'.
//
// We can begin to transform 'compiletimeCompute' and 'runtimeCompute' to both
// have improved performance wherever they might be used by moving their
// implementations to separate functions:
//..
//  int runtimeComputeImpl(int input);
//      // Return a complicated computed value based on the specified 'input'.
//
//  BSLS_KEYWORD_CONSTEXPR int compiletimeComputeImpl(int input);
//      // Return a complicated computed value based on the specified 'input'.
//..
// Then, for 'compiletimeCompute' we can provide a new implementation that will
// use the better runtime algorithm when possible, while remaining 'constexpr'
// on all of the platforms where it previously was 'constexpr' (i.e., without
// changing its declaration):
//..
//  BSLS_KEYWORD_CONSTEXPR int compiletimeCompute(int input)
//      // Return a complicated computed value based on the specified 'input'.
//  {
//  #ifdef BSLS_CONSTEVAL_IS_CONSTANT_EVALUATED_IS_ACTIVE
//      if (!BSLS_CONSTEVAL_IS_CONSTANT_EVALUATED) {
//          return runtimeComputeImpl(input);                         // RETURN
//      }
//  #endif  // BSLS_CONSTEVAL_IS_CONSTANT_EVALUATED_IS_ACTIVE
//      return compiletimeComputeImpl(input);
//  }
//..
// Now clients using 'compiletimeCompute' at runtime, both within and outside
// of other 'constexpr' functions,  will get the benefits of an improved
// algorithm without any need for change.
//
// Similarly, the implementation of 'runtimeCompute' can be improved to be
// opportunistically 'constexpr' by taking advantage of
// 'BSLS_CONSTEVAL_CONSTEXPR', potentially allowing some already existing
// expressions to be compile time evaluated on more modern platforms:
//..
//  BSLS_CONSTEVAL_CONSTEXPR int runtimeCompute(int input)
//      // Return a complicated computed value based on the specified 'input'.
//  {
//  #ifdef BSLS_CONSTEVAL_IS_CONSTANT_EVALUATED_IS_ACTIVE
//      if (BSLS_CONSTEVAL_IS_CONSTANT_EVALUATED) {
//          return compiletimeComputeImpl(input);                     // RETURN
//      }
//  #endif  // BSLS_CONSTEVAL_IS_CONSTANT_EVALUATED_IS_ACTIVE
//      return runtimeComputeImpl(input);
//  }
//..
// Clients of 'runtimeCompute' can continue to use it at runtime with no
// changes, occasionally getting the benefits of the compile-time algorithm.
// When using 'runtimeCompute' to initialize a variable, the compile-time
// behavior can be forced by annotating the variable with
// 'BSLS_CONSTEVAL_CONSTEXPR_MEMBER'.
//
// With these changes, clients on older platforms can continue to take
// advantage of having optimal algorithms available at both compile time and
// runtime while getting the best available implementation on newer platforms
// that enable the detection of compile-time evaluation.

#include <bsls_compilerfeatures.h>

#ifdef BSLS_COMPILERFEATURES_SUPPORT_TRAITS_HEADER
#include <type_traits>
#endif

#if defined(BSLS_COMPILERFEATURES_SUPPORT_CONSTEXPR_CPP14)
    #if defined(BSLS_COMPILERFEATURES_SUPPORT_IS_CONSTANT_EVALUATED)
        #define BSLS_CONSTEVAL_IS_CONSTANT_EVALUATED                          \
                std::is_constant_evaluated()
        #define BSLS_CONSTEVAL_IS_CONSTANT_EVALUATED_IS_ACTIVE 1
    #elif defined(__has_builtin)
        #if __has_builtin(__builtin_is_constant_evaluated)
            #define BSLS_CONSTEVAL_IS_CONSTANT_EVALUATED                      \
                    __builtin_is_constant_evaluated()
            #define BSLS_CONSTEVAL_IS_CONSTANT_EVALUATED_IS_ACTIVE 1
        #endif // __has_builtin(__builtin_is_constant_evaluated)
    #endif // defined(BSLS_COMPILERFEATURES_SUPPORT_IS_CONSTANT_EVALUATED)
#endif // defined BSLS_COMPILERFEATURES_SUPPORT_CONSTEXPR_CPP14

#if !defined(BSLS_CONSTEVAL_IS_CONSTANT_EVALUATED)
    #define BSLS_CONSTEVAL_IS_CONSTANT_EVALUATED
#endif // !defined(BSLS_CONSTEVAL_IS_CONSTANT_EVALUATED)

#ifdef BSLS_CONSTEVAL_IS_CONSTANT_EVALUATED_IS_ACTIVE
    #define BSLS_CONSTEVAL_CONSTEXPR constexpr
    #define BSLS_CONSTEVAL_CONSTEXPR_MEMBER constexpr
#else
    #define BSLS_CONSTEVAL_CONSTEXPR
    #define BSLS_CONSTEVAL_CONSTEXPR_MEMBER const
#endif

#endif // INCLUDED_BSLS_CONSTEVAL
// ----------------------------------------------------------------------------
// Copyright 2022 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------- END-OF-FILE ----------------------------------