// 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 ----------------------------------
