// bslmf_invokeresult.h                                               -*-C++-*-
#ifndef INCLUDED_BSLMF_INVOKERESULT
#define INCLUDED_BSLMF_INVOKERESULT

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

//@PURPOSE: Determine the result type of an invocable expression.
//
//@CLASSES:
// bsl::invoke_result: Metafunction to determine invocation result type
// bsl::invoke_result_t: alias to the return type of the `bsl::invoke_result`
// bslmf::InvokeResultDeductionFailed: Returned on failed result deduction
//
//@MACROS:
// BSLMF_INVOKERESULT_SUPPORT_CPP17_SEMANTICS: defined if SFINAE-friendly
//
//@SEE_ALSO: bslstl_invoke
//
//@DESCRIPTION: This component provides a metafunction `bsl::invoke_result`
// that determines, at compile time, the type returned by invoking a callable
// type, including pointer-to-function, pointer-to-member function,
// pointer-to-member object (returns the object type), or functor class and a
// class `bslmf::InvokeResultDeductionFailed` that is returned when the
// invocation return type cannot be determined (C++03 only).  For a set of
// types `F`, `T1`, `T2`, and `T3`, `bsl::invoke_result<F, T1, t2, T3>::type`
// is roughly the type of the return value obtained by calling an object of
// type `F` with arguments of type `T1`, `T2`, and `T3`, respectively.
// However, `invoke_result` goes beyond function-like objects and deduces a
// return type if `F` is a pointer to function member or data member of some
// class `C` and `T1` is a type (derived from) `C`, pointer to `C`, or
// smart-pointer to `C`.  (See precise specification, below).  For the
// convenience of users, an alias for the type returned by the
// `bsl::invoke_result`, `bsl::invoke_result_t`, is provided by this component.
//
// The interfaces and functionality of `bsl::invoke_result` and
// `bsl::invoke_result_t` are intended to be identical to that of the C++17
// metafunctions, `std::invoke_result` and `std::invoke_result_t` except that
// invalid argument lists are detected in C++11 and later, but not in C++03.
// In C++03, invalid arguments lists will result in a compilation error
// (instead of simply missing `type`) in the remaining cases.  Some other
// functionality is lost when compiling with a C++03 compiler -- see the
// precise specification, below.
//
///C++17 Semantics Detection
///-------------------------
// This component defines the macro
// `BSLMF_INVOKERESULT_SUPPORT_CPP17_SEMANTICS` if `bsl::invoke_result` behaves
// according to the C++17 specification of `std::invoke_result`, which is
// elaborated below.  This macro is defined as long as the compiler supports
// the `decltype` specifier, which is generally available in C++11 and later
// compilation modes.
//
///Precise specification
///---------------------
// The C++11 and C++14 standard defines the pseudo-expression
// <u>INVOKE</u> `(f, t1, t2, ..., tN)`, as follows:
//
// * `(t1.*f)(t2, ..., tN)` when `f` is a pointer to a member function of a
//   class `T` and `t1` is an object of type `T` or a reference to an object
//   of type `T` or a reference to an object of a type derived from `T`;
// * `((*t1).*f)(t2, ..., tN)` when `f` is a pointer to a member function of
//   a class `T` and `t1` is not one of the types described in the previous
//   item;
// * `t1.*f` when `N == 1` and `f` is a pointer to member data of a class `T`
//   and `t1` is an object of type `T` or a reference to an object of type
//   `T` or a reference to an object of a type derived from `T`;
// * `(*t1).*f` when `N == 1` and `f` is a pointer to member data of a class
//   `T` and `t1` is not one of the types described in the previous item;
// * `f(t1, t2, ..., tN)` in all other cases.
//
// Given types `F`, `T1`, `T2`, ..., `TN` corresponding to the expressions
// `f`, `t1`, `t2`, ..., `tN` in the definition of <u>INVOKE</u>, the type produced
// by `bslmf::ResultType<F, T1, T2, ..., TN>::type` is generally the type of
// the psuedo-expression <u>INVOKE</u> `(f, t1, t2, ..., tN)`, with some
// limitations in C++03, as described below.
//
// Because C++03 does not support `decltype`, there are circumstances in which
// `bsl::invoke_result` is not able to deduce the return type of an invocable
// object of class type (i.e., a functor).  If `R` is the type of the <u>INVOKE</u>
// expression, then ideally `type` is `R`.  However the C++03 version of
// `bsl::invoke_result` determines `type` as follows:
//
// 1. If there exists a user-defined specialization of
//    `bsl::invoke_result<F, T1, T2, ... TN>`, then `type` is determined by the
//    specialization, regardless of correctness.  (This rule is true of C++11
//    and later, as well.)
// 2. Otherwise, if `F` is a function type, pointer to function type, pointer
//    to member function type, pointer to member object type, or reference to
//    any of these (i.e, `F` is anything other than a class type or reference
//    to class type), then `type` is `R`.
// 3. Otherwise, if `R` is o a fundamental type, o a pointer to (possibly
//    cv-qualified) `void` or fundamental type, o an lvalue reference to any of
//    the above types (possibly cv-qualified), o `bsl::nullptr_t`, or o `void`,
//    then `type` is `R`.
// 4. Otherwise, if `F` is a class type with member `result_type`, then `type`
//    is `F::result_type`.  Note that `bsl::invoke_result` cannot deduce
//    different result types for different overloads of `operator()` in this
//    case.
// 5. Otherwise, if `F` is a class type with member type `ResultType`, then
//    `type` is `F::ResultType`.  Note that `bsl::invoke_result` cannot deduce
//    different result types for different overloads of `operator()` in this
//    case.
// 6. Otherwise, `type` is `bslmf::InvokeResultDeductionFailed`.  The benefit
//    of this placeholder over a compilation error is that `invoke_result` is
//    often used in a context where the return value will eventually be
//    discarded.  Thus, generating a useless type is often harmless.  In cases
//    where it is not harmless, the placeholder type will almost certainly
//    result in a compilation error in the surrounding code.
//
// If the callable type is a pointer-to-member (data or function), invalid
// argument lists are not detected.  Thus, there is a small chance that invalid
// code will compile successfully, though it is hard to see now this would be
// harmful, since determining the return type of an expression is not very
// useful if the expression is not eventually evaluated, which will certainly
// produce the expected compilation error for invalid argument lists.
//
///Usage Example
///-------------
// Suppose we want to create a wrapper that executes an invocable object and
// sets a `done` flag.  The `done` flag will not be set if the invocation
// exits via an exception.  The wrapper takes an invocable `f` and an argument
// `x` and evaluates `f(x)`, returning the result.  In the absence of C++14
// automatically-deduced function return declarations, we use
// `bsl::invoke_result` to deduce the return type of `f(x)`.
//
// First, we write the wrapper template as follows:
// ```
// template <class FT, class XT>
// typename bsl::invoke_result<FT, XT>::type
// invokeAndSetFlag(bool *done, FT f, XT x)
//     // Return 'f(x)' and set '*done' to true if no exception.
// {
//     typedef typename bsl::invoke_result<FT, XT>::type ResultType;
//     *done = false; // Clear flag in case of exception
//     ResultType result = f(x);
//     *done = true;  // Set flag on success
//     return result;
// }
// ```
// Note that additional metaprogramming would be required to make this
// template work for return type `void`; such metaprogramming is beyond the
// scope of this usage example.
//
// Then we define a couple of simple functors to be used with the wrapper.
// The first functor is a simple template that triples its invocation
// argument:
// ```
// template <class t_TP>
// struct Triple {
//     // Functor that triples its argument.
//
//     t_TP operator()(t_TP v) const { return static_cast<t_TP>(v * 3); }
//         // Return three times the specified 'v' value.
// };
// ```
// Next, we define a second functor that returns an enumerator `ODD` or
// `EVEN`, depending on whether its argument is exactly divisible by 2.  Since
// the return type is not a fundamental type, this functor indicates its
// return type using the `ResultType` idiom:
// ```
// enum EvenOdd { e_EVEN, e_ODD };
//
// struct CalcEvenOdd {
//     // Functor that determines whether its argument is odd or even.
//
//     typedef EvenOdd ResultType;
//
//     EvenOdd operator()(int i) const { return (i & 1) ? e_ODD : e_EVEN; }
//         // Return 'e_ODD' if the specified 'i' is odd; otherwise return
//         // 'e_EVEN'
// };
// ```
// Finally, we can invoke these functors through our wrapper:
// ```
// int main()
//     // Run the usage example.
// {
//     bool done = false;
//
//     Triple<short> ts = {};
//     short         r0 = invokeAndSetFlag(&done, ts, short(9));
//     assert(done && 27 == r0);
//
//     CalcEvenOdd ceo = {};
//     done            = false;
//     EvenOdd r1      = invokeAndSetFlag(&done, ceo, 5);
//     assert(done && e_ODD == r1);
//
//     done = false;
//     EvenOdd r2 = invokeAndSetFlag(&done, ceo, 8);
//     assert(done && e_EVEN == r2);
//
//     return 0;
// }
// ```

#include <bslscm_version.h>

#include <bslmf_addconst.h>
#include <bslmf_addlvaluereference.h>
#include <bslmf_addpointer.h>
#include <bslmf_addrvaluereference.h>
#include <bslmf_addvolatile.h>
#include <bslmf_assert.h>
#include <bslmf_decay.h>
#include <bslmf_enableif.h>
#include <bslmf_functionpointertraits.h>
#include <bslmf_isaccessiblebaseof.h>
#include <bslmf_isclass.h>
#include <bslmf_isconvertible.h>
#include <bslmf_islvaluereference.h>
#include <bslmf_ismemberobjectpointer.h>
#include <bslmf_isreference.h>
#include <bslmf_isreferencewrapper.h>
#include <bslmf_isrvaluereference.h>
#include <bslmf_isvoid.h>
#include <bslmf_memberfunctionpointertraits.h>
#include <bslmf_memberpointertraits.h>
#include <bslmf_movableref.h>
#include <bslmf_removecv.h>
#include <bslmf_resulttype.h>
#include <bslmf_tag.h>
#include <bslmf_voidtype.h>

#include <bsls_compilerfeatures.h>
#include <bsls_nullptr.h>
#include <bsls_platform.h>

#if BSLS_COMPILERFEATURES_SIMULATE_CPP11_FEATURES
// clang-format off
// Include version that can be compiled with C++03
// Generated on Mon Jan 13 08:31:25 2025
// Command line: sim_cpp11_features.pl bslmf_invokeresult.h

# define COMPILING_BSLMF_INVOKERESULT_H
# include <bslmf_invokeresult_cpp03.h>
# undef COMPILING_BSLMF_INVOKERESULT_H

// clang-format on
#else

# if defined(BSLS_COMPILERFEATURES_SUPPORT_DECLTYPE)
// The implementation of C++17 semantics in this component depends upon the
// use of 'decltype' and expression SFINAE.  Note that we have no feature
// macro to test for expression SFINAE.
# define BSLMF_INVOKERESULT_SUPPORT_CPP17_SEMANTICS 1
#endif

namespace BloombergLP {
namespace bslmf {

                 // ==========================================
                 // class template InvokeResultDeductionFailed
                 // ==========================================

/// When `invoke_result` cannot deduce the actual return type of a functor
/// (in C++03 mode), it yields this type as a placeholder.  The advantage of
/// using this placeholder instead of a compilation failure (e.g., using a
/// static assert) is that the return type of an INVOKE() operation is
/// often discarded, so our failure to deduce the return type is often
/// harmless.  Since `InvokeResultDeductionFailed` is a return type, it must
/// be convertible from the actual return type; this conversion is
/// accomplished by means of a constructor that makes it convertible from
/// *any* type.
struct InvokeResultDeductionFailed {

    // CREATORS

    /// Convert from an arbitrary type.  The actual argument value is
    /// discarded.
    template <class t_TYPE>
    InvokeResultDeductionFailed(const t_TYPE&)
    {
    }
};

#if !BSLS_COMPILERFEATURES_SIMULATE_CPP11_FEATURES // $var-args=13

/// Forward declaration
template <class t_FN, class... t_ARGTYPES>
struct InvokeResult_BaseCalcUtil;

#endif

}  // close package namespace
}  // close enterprise namespace

                        // ============================
                        // class template invoke_result
                        // ============================

namespace bsl {

#if !BSLS_COMPILERFEATURES_SIMULATE_CPP11_FEATURES // $var-args=13

/// This class is a metafunction that conditionally provides a `type` member
/// that is the type resulting from invoking an object of the specified
/// `t_FN` template parameter with arguments of the specified `t_ARGTYPES`
/// template parameters.  More precisely, given types `F`, `T1`, `T2`, ...,
/// `TN` corresponding to expressions `f`, `t1`, `t2`, ..., `tN`,
/// `bslmf::ResultType<F, T1, T2, ..., TN>::type` is usually the type of the
/// psuedo-expression <u>INVOKE</u> `(f, t1, t2, ..., tN)`, as defined in section
/// [func.rquire] of the C++11 standard.  If the compiler supports C++11
/// `decltype` and the psuedo-expression <u>INVOKE</u> `(f, t1, t2, ..., tN)` is
/// not well-formed, this class provides no `type` member.  If `t_FN` is a
/// class (functor) type and the compiler doesn't support C++11 `decltype`,
/// the return type is automatically deduced for fundamental types, `void`,
/// pointers or references to those, or `bsl::nullptr_t` and is deduced by
/// `bslmf::ResultType<t_FN>::type` otherwise.  If deduction fails, this
/// metafunction yields `bslmf::InvokeResultDeductionFailed`.  See
/// component-level documentation for more detail.
template <class t_FN, class... t_ARGTYPES>
class invoke_result
: public BloombergLP::bslmf::InvokeResult_BaseCalcUtil<t_FN, t_ARGTYPES...>::
      BaseType {

    ///Implementation Note
    ///- - - - - - - - - -
    // If, by the rules outlined above in the class documentation, this type
    // defines a member 'type' typedef, that typedef comes from the
    // 'bslmf::InvokeResult_BaseCalcUtil' specialization from which this class
    // inherits.
};

#ifdef BSLS_COMPILERFEATURES_SUPPORT_ALIAS_TEMPLATES

// ALIASES

/// `invoke_result_t` is an alias to the return type of the
/// `bsl::invoke_result` meta-function.  Note, that the `invoke_result_t`
/// avoids the `::type` suffix and `typename` prefix when we want to use the
/// result of the meta-function in templates.
template <class t_FN, class... t_ARGTYPES>
using invoke_result_t = typename invoke_result<t_FN, t_ARGTYPES...>::type;
#endif  // BSLS_COMPILERFEATURES_SUPPORT_ALIAS_TEMPLATES

#endif

}  // close namespace bsl

// ============================================================================
//                              TEMPLATE IMPLEMENTATIONS
// ============================================================================

namespace BloombergLP {
namespace bslmf {

#if !BSLS_COMPILERFEATURES_SIMULATE_CPP11_FEATURES // $var-args=13

/// Forward declaration
template <bool     t_IS_FUNCPTR,
          bool     t_IS_MEMFUNCPTR,
          bool     t_IS_MEMOBJPTR,
          class    t_FN,
          class... t_ARGTYPES>
struct InvokeResult_Imp;

                 // =========================================
                 // struct template InvokeResult_BaseCalcUtil
                 // =========================================

/// This component-private utility `struct` template provides a nested
/// typedef, `BaseType`, which is a class type that itself provides a nested
/// typedef `type` that is the type of the `INVOKE(fn, args...)` expression
/// given `fn` is an object of the specified `t_FN` type and `args...` are
/// objects of the specified `t_ARGTYPES...` types.  If the
/// `INVOKE(fn, args...)` expression is not well-formed, `BaseType` provides
/// no such nested typedef.
template <class t_FN, class... t_ARGTYPES>
struct InvokeResult_BaseCalcUtil {

  private:
    // PRIVATE TYPES

    /// Remove references and cv-qualifiers from `t_FN`, and decay function
    /// types and array types to pointers.  In C++03, treat
    /// `bslmf::MovableRef<t_T>` as a (movable) reference-qualified `t_T`.
    typedef typename bslmf::MovableRefUtil::Decay<t_FN>::type F;

    enum {
        k_IS_FUNCPTR   = BloombergLP::bslmf::IsFunctionPointer<F>::value,
        k_IS_MEMFUNCPTR= BloombergLP::bslmf::IsMemberFunctionPointer<F>::value,
        k_IS_MEMOBJPTR = bsl::is_member_object_pointer<F>::value
    };

    /// `FwdFn` is the type forwarded to `InvokeResult_Imp`.  It is `F`
    /// (a.k.a., `decay_t<t_FN>`) if `F` is a function pointer or
    /// pointer-to-member and `t_FN` otherwise.
    typedef typename bsl::conditional<k_IS_FUNCPTR || k_IS_MEMFUNCPTR ||
                                          k_IS_MEMOBJPTR,
                                      F,
                                      t_FN>::type FwdFn;

  public:
    // TYPES

    /// In C++11 and later, conditionally provides a nested typedef `type`
    /// that is the type returned by the expression <u>INVOKE</u> `(f, args...)`,
    /// where `f` is an object of type `t_FN` and `args...` is a list of
    /// object of types `t_ARGTYPES...`, if the expression is well formed.
    /// In C++03, provide a nested typed `type` that is the type returned by
    /// the same invoke expression if the type can be deduced, and is
    /// `InvokeResultDeductionFailed` otherwise.  Note that in C++11 and
    /// later, `type` is never `InvokeResultDeductionFailed`.
    typedef typename BloombergLP::bslmf::InvokeResult_Imp<k_IS_FUNCPTR,
                                                          k_IS_MEMFUNCPTR,
                                                          k_IS_MEMOBJPTR,
                                                          FwdFn,
                                                          t_ARGTYPES...>
        BaseType;
};

#endif

                      // ===============================
                      // struct InvokeResult_VoidChecker
                      // ===============================

/// Empty type used to detect void expressions.  The size of this type is
/// the same as `bslmf::Tag<1>`.
struct InvokeResult_VoidChecker : Tag<true> {
};

/// Return `InvokeResult_VoidChecker()` if the left argument is of type
/// cv-`void`; otherwise `bslmf::Tag<false>()`.  This overload of the comma
/// operator is declared but not defined, and is intended to be used in
/// metafunctions in an unevaluated context to detect void expressions.  For
/// any non-void expression `expr`, `(expr,InvokeResult_VoidChecker())`,
/// will match this overload and produce a result of type
/// `bslmf::Tag<false>`.  However, `const t_TYPE&` will not match `void`, so
/// if `expr` is a void expression, the built-in comma operator is matched
/// and the result will have type `InvokeResult_VoidChecker` (i.e., the
/// second argument).
///
/// Note that Sun CC incorrectly matches this overload for a void
/// expression, then fails hard.  The `enable_if` prevents this match for
/// Sun CC and any other compilers that may similarly match `void` and is
/// harmless for compilers that don't.
template <class t_TYPE>
typename bsl::enable_if<!bsl::is_void<t_TYPE>::value, Tag<false> >::type
operator,(const t_TYPE&, InvokeResult_VoidChecker);

/// Metafunction helpers for deducing the return type of an expression.
struct InvokeResult_Index {

    enum {
        // Enumeration of possible return types.

        e_VOID,
        e_BOOL,
        e_CHAR,
        e_SCHAR,
        e_UCHAR,
        e_CHAR8_T,
        e_WCHAR_T,
        e_CHAR16_T,
        e_CHAR32_T,
        e_SHORT,
        e_USHORT,
        e_INT,
        e_UNSIGNED,
        e_LONG,
        e_ULONG,
        e_LONG_LONG,
        e_ULONG_LONG,
        e_FLOAT,
        e_DOUBLE,
        e_LONG_DOUBLE,

        // Pointer to void is special among pointers because it cannot be
        // dereferenced.
        e_VOIDPTR,
        e_CONST_VOIDPTR,
        e_VOLATILE_VOIDPTR,
        e_CONST_VOLATILE_VOIDPTR,

        e_NULLPTR_T,
        e_POINTER, // Any pointer type other than 'void *' or 'nullptr_t'
        e_OTHER    // Anything other than above
    };

    // CLASS METHODS
    static bslmf::Tag<e_BOOL>                   fromVal(bool&                );
    static bslmf::Tag<e_CHAR>                   fromVal(char&                );
    static bslmf::Tag<e_SCHAR>                  fromVal(signed char&         );
    static bslmf::Tag<e_UCHAR>                  fromVal(unsigned char&       );
#ifdef BSLS_COMPILERFEATURES_SUPPORT_UTF8_CHAR_TYPE
    static bslmf::Tag<e_CHAR8_T>                fromVal(char8_t&             );
#endif
    static bslmf::Tag<e_WCHAR_T>                fromVal(wchar_t&             );
#ifdef BSLS_COMPILERFEATURES_SUPPORT_UNICODE_CHAR_TYPES
    static bslmf::Tag<e_CHAR16_T>               fromVal(char16_t&            );
    static bslmf::Tag<e_CHAR32_T>               fromVal(char32_t&            );
#endif
    /// Return a tag type representing the argument type.  These functions
    /// are declared but not defined and are intended to be used in an
    /// unevaluated context (e.g., within `sizeof`) to convert an expression
    /// into a compile-time enumeration constant.
    static bslmf::Tag<e_SHORT>                  fromVal(short&               );
    static bslmf::Tag<e_USHORT>                 fromVal(unsigned short&      );
    static bslmf::Tag<e_INT>                    fromVal(int&                 );
    static bslmf::Tag<e_UNSIGNED>               fromVal(unsigned&            );
    static bslmf::Tag<e_LONG>                   fromVal(long&                );
    static bslmf::Tag<e_ULONG>                  fromVal(unsigned long&       );
    static bslmf::Tag<e_LONG_LONG>              fromVal(long long&           );
    static bslmf::Tag<e_ULONG_LONG>             fromVal(unsigned long long&  );
    static bslmf::Tag<e_FLOAT>                  fromVal(float&               );
    static bslmf::Tag<e_DOUBLE>                 fromVal(double&              );
    static bslmf::Tag<e_LONG_DOUBLE>            fromVal(long double&         );
    static bslmf::Tag<e_VOIDPTR>                fromVal(void *&              );
    static bslmf::Tag<e_CONST_VOIDPTR>          fromVal(const void *&        );
    static bslmf::Tag<e_VOLATILE_VOIDPTR>       fromVal(volatile void *&     );
    static bslmf::Tag<e_CONST_VOLATILE_VOIDPTR> fromVal(const volatile void*&);
    static bslmf::Tag<e_NULLPTR_T>              fromVal(bsl::nullptr_t&);
    template <class t_TP>
    static bslmf::Tag<e_POINTER>                fromVal(t_TP *&);
    template <class t_TP>
    static bslmf::Tag<e_OTHER>                  fromVal(t_TP&);
};

/// Metafunction to convert a type index back to a type.  For each
/// specialization of this struct, the `type` member will be the type
/// corresponding to `index`.  For example, if `index` is `e_UCHAR`, then
/// `InvokeResult_Type<index>::type` is `unsigned char`.
template <int t_INDEX> struct InvokeResult_Type;

// Turn off bde_verify warnings for "Declaration without tag".  Pedantically,
// every 'type' declared in a metafunction should have the tag '// TYPES', but
// that breaks up the clean 3-line declaration of each specialization, making
// the pattern harder to for the eye to follow.
// BDE_VERIFY pragma: push
// BDE_VERIFY pragma: -KS00
template <>
struct InvokeResult_Type<InvokeResult_Index::e_VOID>
    { typedef void               type; };
template <>
struct InvokeResult_Type<InvokeResult_Index::e_BOOL>
    { typedef bool               type; };
template <>
struct InvokeResult_Type<InvokeResult_Index::e_CHAR>
    { typedef char               type; };
template <>
struct InvokeResult_Type<InvokeResult_Index::e_SCHAR>
    { typedef signed char        type; };
template <>
struct InvokeResult_Type<InvokeResult_Index::e_UCHAR>
    { typedef unsigned char      type; };
#ifdef BSLS_COMPILERFEATURES_SUPPORT_UTF8_CHAR_TYPE
template <>
struct InvokeResult_Type<InvokeResult_Index::e_CHAR8_T>
    { typedef char8_t            type; };
#endif
template <>
struct InvokeResult_Type<InvokeResult_Index::e_WCHAR_T>
    { typedef wchar_t            type; };
#ifdef BSLS_COMPILERFEATURES_SUPPORT_UNICODE_CHAR_TYPES
template <>
struct InvokeResult_Type<InvokeResult_Index::e_CHAR16_T>
    { typedef char16_t           type; };
template <>
struct InvokeResult_Type<InvokeResult_Index::e_CHAR32_T>
    { typedef char32_t           type; };
#endif
template <>
struct InvokeResult_Type<InvokeResult_Index::e_SHORT>
    { typedef short              type; };
template <>
struct InvokeResult_Type<InvokeResult_Index::e_USHORT>
    { typedef unsigned short     type; };
template <>
struct InvokeResult_Type<InvokeResult_Index::e_INT>
    { typedef int                type; };
template <>
struct InvokeResult_Type<InvokeResult_Index::e_UNSIGNED>
    { typedef unsigned           type; };
template <>
struct InvokeResult_Type<InvokeResult_Index::e_LONG>
    { typedef long               type; };
template <>
struct InvokeResult_Type<InvokeResult_Index::e_ULONG>
    { typedef unsigned long      type; };
template <>
struct InvokeResult_Type<InvokeResult_Index::e_LONG_LONG>
    { typedef long long          type; };
template <>
struct InvokeResult_Type<InvokeResult_Index::e_ULONG_LONG>
    { typedef unsigned long long type; };
template <>
struct InvokeResult_Type<InvokeResult_Index::e_FLOAT>
    { typedef float              type; };
template <>
struct InvokeResult_Type<InvokeResult_Index::e_DOUBLE>
    { typedef double             type; };
template <>
struct InvokeResult_Type<InvokeResult_Index::e_LONG_DOUBLE>
    { typedef long double        type; };
template <>
struct InvokeResult_Type<InvokeResult_Index::e_VOIDPTR>
    { typedef void              *type; };
template <>
struct InvokeResult_Type<InvokeResult_Index::e_CONST_VOIDPTR>
    { typedef const void        *type; };
template <>
struct InvokeResult_Type<InvokeResult_Index::e_VOLATILE_VOIDPTR>
    { typedef volatile void     *type; };
template <>
struct InvokeResult_Type<InvokeResult_Index::e_CONST_VOLATILE_VOIDPTR>
    { typedef const volatile void *type; };
template <>
struct InvokeResult_Type<InvokeResult_Index::e_NULLPTR_T>
    { typedef bsl::nullptr_t     type; };
template <>
struct InvokeResult_Type<InvokeResult_Index::e_POINTER>
    { typedef void              *type; };
template <>
struct InvokeResult_Type<InvokeResult_Index::e_OTHER>
    { typedef InvokeResultDeductionFailed type; };
// Re-enable warnings for "Declaration without tag"
// BDE_VERIFY pragma: pop

/// Utility metaprogramming functions inherited by other metaprogramming
/// classes.
struct InvokeResult_ImpUtils
{

    // TYPES

    /// Type convertible from any lvalue type.  Used for overload resolution
    /// in metafunctions.
    struct AnyLvalue {

        // CREATORS

        /// (Declared but not defined) Convert from any lvalue argument.
        template <class t_TP>
        AnyLvalue(volatile t_TP&);
    };

    /// Type convertible from any rvalue type.  Used for overload resolution
    /// in metafunctions.
    struct AnyRvalue {

        // CREATORS
#ifdef BSLS_COMPILERFEATURES_SUPPORT_RVALUE_REFERENCES
        /// (Declared but not defined) Convert from any rvalue argument.
        template <class t_TP>
        AnyRvalue(
               t_TP&&,
               typename bsl::enable_if<bsl::is_rvalue_reference<t_TP&&>::value,
                                       int>::type = 0);
#else
        template <class t_TP>
        AnyRvalue(t_TP);
            // (Declared but not defined) Convert from any rvalue argument.
            // This constructor will also match lvalue arguments, but is used
            // in a context where 'AnyLValue' is a better conversion path.
#endif
    };

    // CLASS METHODS
#ifdef BSLS_COMPILERFEATURES_SUPPORT_RVALUE_REFERENCES
    /// Return a reference to the specified `t_SOME_TYPE` template parameter
    /// type; if `t_SOME_TYPE` is an rvalue, then the returned reference is
    /// an rvalue reference.  This function is declared but not defined and
    /// is intended to be called in an unevaluated context.  Because there
    /// is no definition, the available constructors for `t_SOME_TYPE` are
    /// irrelevant.
    template <class t_SOME_TYPE>
    static typename bsl::add_rvalue_reference<t_SOME_TYPE>::type myDeclval();
#else
    template <class t_SOME_TYPE>
    static t_SOME_TYPE myDeclval();
        // Return an object of the specified 't_SOME_TYPE' template parameter
        // type.  This function is declared but not defined and is intended to
        // be called in an unevaluated context.  Because there is no
        // definition, the available constructors for 't_SOME_TYPE' are
        // irrelevant.
#endif

    /// (Declared but not defined) Return `bslmf::Tag<false>()` if the
    /// first argument is an rvalue and `bslmf::Tag<true>()` if it is
    /// lvalue.  In actual use, the second argument is always a literal
    /// `int`, which causes the second overload to be preferred in case of
    /// ambiguity.
    static bslmf::Tag<false>    checkLvalue(AnyRvalue, ...);
    static bslmf::Tag<true >    checkLvalue(AnyLvalue, int);

    /// (Declared but not defined) Return `bslmf::Tag<true>()` if the
    /// argument is `const`-qualified and `bslmf::Tag<false>()` otherwise.
    template <class t_TP>
    static bslmf::Tag<false> checkConst(t_TP&);
    template <class t_TP>
    static bslmf::Tag<true> checkConst(const t_TP&);

    /// (Declared but not defined) Return `bslmf::Tag<true>()` if the
    /// argument is `volatile`-qualified and `bslmf::Tag<false>()`
    /// otherwise.  Note that if `t_TP` is both const- and
    /// volatile-qualified, it will not match `volatile t_TP&`, hence the
    /// need for the const overloads.
    template <class t_TP>
    static bslmf::Tag<false> checkVolatile(t_TP&);
    template <class t_TP>
    static bslmf::Tag<false> checkVolatile(const t_TP&);
    template <class t_TP>
    static bslmf::Tag<true> checkVolatile(volatile t_TP&);
    template <class t_TP>
    static bslmf::Tag<true> checkVolatile(const volatile t_TP&);

    /// (Declared but not defined) Return the argument, with cv-qualifiers
    /// removed.
    template <class t_TP>
    static t_TP& uncv(const t_TP&);
    template <class t_TP>
    static t_TP& uncv(const volatile t_TP&);

    /// If the argument type `t_TP` is pointer to type `X`, where `X` is not
    /// cv-`void`, return a reference to `X`; otherwise return a reference
    /// to `t_TP`.  Note that these functions are declared but not defined
    /// and are intended to be called only in an unevaluated context.
    template <class t_TP>
    static t_TP& unpoint(t_TP&);
    template <class t_TP>
    static const t_TP& unpoint(const t_TP&);
    template <class t_TP>
    static typename bsl::enable_if<!bsl::is_void<t_TP>::value, t_TP>::type&
    unpoint(t_TP *&);
    template <class t_TP>
    static typename bsl::enable_if<!bsl::is_void<t_TP>::value, t_TP>::type&
    unpoint(t_TP *const&);
    template <class t_TP>
    static typename bsl::enable_if<!bsl::is_void<t_TP>::value, t_TP>::type&
    unpoint(t_TP *volatile&);
    template <class t_TP>
    static typename bsl::enable_if<!bsl::is_void<t_TP>::value, t_TP>::type&
    unpoint(t_TP *const volatile&);
};

/// Starting with type, `t_UNQUAL_TYPE`, generate a new type by applying the
/// following steps in order:
///
/// 1. If the specified `t_IS_CONST` parameter is true, apply
///    `bsl::add_const`; otherwise leave unchanged.
/// 2. If the specified `t_IS_VOLATILE` parameter is true, apply
///    `bsl::add_volatile`; otherwise leave unchanged.
/// 3. If the specified `t_IS_LVALUE` parameter is true, apply
///    `bsl::add_lvalue_reference`; otherwise leave unchanged.
///
/// Set the `type` member to the resulting type.
template <class t_UNQUAL_TYPE,
          bool t_IS_CONST,
          bool t_IS_VOLATILE,
          bool t_IS_LVALUE>
struct InvokeResult_AddCVRef {

  private:
    // PRIVATE TYPES
    typedef
        typename bsl::conditional<t_IS_CONST,
                                  typename bsl::add_const<t_UNQUAL_TYPE>::type,
                                  t_UNQUAL_TYPE>::type CQualType;

    typedef
        typename bsl::conditional<t_IS_VOLATILE,
                                  typename bsl::add_volatile<CQualType>::type,
                                  CQualType>::type CVQualType;

  public:
    // TYPES
    typedef typename bsl::conditional<
        t_IS_LVALUE,
        typename bsl::add_lvalue_reference<CVQualType>::type,
        CVQualType>::type type;
};

#if !BSLS_COMPILERFEATURES_SIMULATE_CPP11_FEATURES // $var-args=13

#ifndef BSLMF_INVOKERESULT_SUPPORT_CPP17_SEMANTICS
template <bool /* IS_VOID */, class t_FN, class... t_ARGTYPES>
struct InvokeResult_FunctorDeduction : InvokeResult_ImpUtils {
    // Deduce return type of 't_FN(t_ARGTYPES...)'.  This template is
    // instantiated only when 't_FN' is of class type (i.e., a functor).  This
    // primary template is selected when 't_FN(t_ARGTYPES...)' is not 'void'.
    // Note that this template is not defined in C++11 mode (if 'decltype'
    // exists).

    typedef typename bsl::decay<t_FN>::type F;
        // Remove references and cv-qualifiers from 't_FN', and decay function
        // types and array types to pointers.

    enum {
        // In an unevaluated context ('BSLMF_TAG_TO_INT'), "invoke"
        // 'myDeclval<t_FN>()(myDeclval<t_ARGTYPES>()...)' and use overloading
        // to deduce the type and other attributes of the return value.

        k_INDEX      = BSLMF_TAG_TO_INT(InvokeResult_Index::fromVal(
                         uncv(myDeclval<t_FN>()(myDeclval<t_ARGTYPES>()...)))),
        k_IS_POINTER = (k_INDEX == InvokeResult_Index::e_POINTER),
        k_IS_LVALUE  = BSLMF_TAG_TO_INT(
                checkLvalue(myDeclval<t_FN>()(myDeclval<t_ARGTYPES>()...), 0)),
        k_IS_CONST_PTR = k_IS_POINTER &&
                         BSLMF_TAG_TO_INT(checkConst(
                               myDeclval<t_FN>()(myDeclval<t_ARGTYPES>()...))),
        k_IS_VOLATILE_PTR = k_IS_POINTER &&
                            BSLMF_TAG_TO_INT(checkVolatile(myDeclval<t_FN>()(
                                                 myDeclval<t_ARGTYPES>()...))),
        k_TARGET_INDEX = BSLMF_TAG_TO_INT(InvokeResult_Index::fromVal(
                uncv(unpoint(myDeclval<t_FN>()(myDeclval<t_ARGTYPES>()...))))),
        k_IS_CONST_TARGET    = BSLMF_TAG_TO_INT(checkConst(
                      unpoint(myDeclval<t_FN>()(myDeclval<t_ARGTYPES>()...)))),
        k_IS_VOLATILE_TARGET = BSLMF_TAG_TO_INT(checkVolatile(
                      unpoint(myDeclval<t_FN>()(myDeclval<t_ARGTYPES>()...)))),
        k_CANT_DEDUCE_TYPE   = (k_TARGET_INDEX ==
                                (int)InvokeResult_Index::e_OTHER)
    };

    typedef typename bsl::conditional<
            ! k_CANT_DEDUCE_TYPE,
            typename InvokeResult_Type<k_TARGET_INDEX>::type,
            typename ResultType<F,InvokeResultDeductionFailed>::type
        >::type UnqualTargetType;
        // The deduced result after stripping off pointer, reference, and
        // cv-qualifiers.  The 'TARGET_INDEX' indicates the fundamental type of
        // the target result.  If the target could not be deduced (i.e.,
        // 'TARGET_INDEX == e_OTHER), then attempt to find the result by
        // looking for a 'result_type' or 'ResultType' alias in 't_FN'; failing
        // that, use 'InvokeResultDeductionFailed'.

    typedef typename
    InvokeResult_AddCVRef<UnqualTargetType,
                          static_cast<bool>(k_IS_CONST_TARGET)
                          && ! static_cast<bool>(k_CANT_DEDUCE_TYPE),
                          static_cast<bool>(k_IS_VOLATILE_TARGET)
                          && ! static_cast<bool>(k_CANT_DEDUCE_TYPE),
                          false>::type CVQualTargetType;
        // The deduced target after adding back previously-stripped cv
        // qualifiers, if any.  Note that if the expression yielded a pointer
        // type, these cv qualifiers apply to the target of the pointer, not
        // the pointer itself.

    typedef typename
    bsl::conditional<static_cast<bool>(k_IS_POINTER)
                     && ! static_cast<bool>(k_CANT_DEDUCE_TYPE),
                     typename bsl::add_pointer<CVQualTargetType>::type,
                     CVQualTargetType>::type UnqualType;
        // The deduced result after adding back previously-stripped pointers,
        // if any.

    typedef typename
    InvokeResult_AddCVRef<
        UnqualType,
        static_cast<bool>(k_IS_CONST_PTR)
        && ! static_cast<bool>(k_CANT_DEDUCE_TYPE),
        static_cast<bool>(k_IS_VOLATILE_PTR)
        && ! static_cast<bool>(k_CANT_DEDUCE_TYPE),
        static_cast<bool>(k_IS_LVALUE)
        && ! static_cast<bool>(k_CANT_DEDUCE_TYPE)>::type Qtype;
        // The deduced result after adding back previously-stripped cv
        // qualifiers and references.  Note that if the result is a pointer,
        // the cv qualifiers apply to the pointer, not to the target.

    typedef typename bsl::conditional<static_cast<bool>(k_IS_LVALUE), Qtype,
                      typename bsl::remove_cv<Qtype>::type>::type type;
        // The final deduced result type.  If the type is not a reference,
        // top-level cv qualifiers are stripped off.
};

template <class t_FN, class... t_ARGTYPES>
struct InvokeResult_FunctorDeduction<true /* IS_VOID */, t_FN, t_ARGTYPES...> {
    // Deduce return type of 't_FN(t_ARGTYPES...)'.  This template is
    // instantiated only when 't_FN' is of class type (i.e., a functor).  This
    // specialization is selected when 't_FN(t_ARGTYPES...)' is cv-'void'.

    typedef void type;
};
#endif // !BSLMF_INVOKERESULT_SUPPORT_CPP17_SEMANTICS

#endif

/// This metafunction determines which cv qualifiers and reference
/// qualifiers should be propagated from the first argument of
/// `invoke_result`.  This primary template is instantiated when
/// `t_ARG_TYPE` is the same or is derived from `t_MEMOF_CLASS`.  The
/// constant `k_IS_LVALUE` is true iff `t_ARG_TYPE` is an lvalue reference;
/// the constant `k_IS_CONST` is true iff `t_ARG_TYPE` is const-qualified;
/// and the constant `k_IS_VOLATILE` is true iff `t_ARG_TYPE` is
/// volatile-qualified.
template <class t_MEMOF_CLASS,
          class t_ARG_TYPE,
          bool t_IS_DERIVED = bsl::is_convertible<
              typename bsl::decay<t_ARG_TYPE>::type *,
              typename bsl::decay<t_MEMOF_CLASS>::type *>::value>
struct InvokeResult_MemPtrArgQualifiers {

    // TYPES
    enum {
        k_IS_LVALUE = bsl::is_lvalue_reference<t_ARG_TYPE>::value,
        k_IS_CONST  = bsl::is_const<
            typename bsl::remove_reference<t_ARG_TYPE>::type>::value,
        k_IS_VOLATILE = bsl::is_volatile<
            typename bsl::remove_reference<t_ARG_TYPE>::type>::value
    };
};

/// This metafunction determines which cv qualifiers and reference
/// qualifiers should be propagated from the first argument of
/// `invoke_result`.
///
/// This specialization is instantiated when `t_ARG_TYPE` is not derived
/// from `t_MEMOF_CLASS` and is assumed to be a pointer or smart pointer
/// type.  If type `A` is the result of dereferencing an object of type
/// `t_ARG_TYPE`, then the constant `k_IS_LVALUE` is true iff `A` is an
/// lvalue reference; the constant `k_IS_CONST` is true iff `A` is a
/// const-qualified reference; and the constant `k_IS_VOLATILE` is true iff
/// `A` is a volatile-qualified reference.
template <class t_MEMOF_CLASS, class t_ARG_TYPE>
struct InvokeResult_MemPtrArgQualifiers<t_MEMOF_CLASS, t_ARG_TYPE, false>
: InvokeResult_ImpUtils {

#ifdef BSLS_COMPILERFEATURES_SUPPORT_RVALUE_REFERENCES
  private:
    // CLASS METHODS

    /// (Declared but not defined.)  Return an lvalue reference
    /// corresponding of the specified `t_TP` type, which is deduced from
    /// the specified unnamed argument.  If the argument is an lvalue, the
    /// return type is identical to the argument type.  If the argument is
    /// an rvalue, the return type is an lvalue to the argument type with
    /// the same cv qualifiers.  This function is useful for avoiding too
    /// many redundant overloads in metafunctions that determine cv
    /// qualifications, etc.
    template <class t_TP>
    static t_TP& tolvalue(t_TP&&);

  public:
    // TYPES
    enum {k_IS_LVALUE = BSLMF_TAG_TO_INT(checkLvalue(*myDeclval<t_ARG_TYPE>(),
                                                     0)),
          k_IS_CONST =
              BSLMF_TAG_TO_INT(checkConst(tolvalue(*myDeclval<t_ARG_TYPE>()))),
          k_IS_VOLATILE = BSLMF_TAG_TO_INT(
              checkVolatile(tolvalue(*myDeclval<t_ARG_TYPE>())))};
#else
  public:
    // TYPES
    enum {
        k_IS_LVALUE = BSLMF_TAG_TO_INT(checkLvalue(*myDeclval<t_ARG_TYPE>(),
                                                   0)),
        // In C++03, cv qualifiers are discarded from rvalues.
        k_IS_CONST = k_IS_LVALUE &&
                     BSLMF_TAG_TO_INT(checkConst(*myDeclval<t_ARG_TYPE>())),
        k_IS_VOLATILE = k_IS_LVALUE && BSLMF_TAG_TO_INT(checkVolatile(
                                                     *myDeclval<t_ARG_TYPE>()))
    };
#endif // BSLS_COMPILERFEATURES_SUPPORT_RVALUE_REFERENCES
};

#if !BSLS_COMPILERFEATURES_SIMULATE_CPP11_FEATURES // $var-args=13

/// Forward declaration
template <class t_VOID_TYPE, class t_FN, class... t_ARGTYPES>
struct InvokeResult_FunctorImp;

/// Forward declaration
template <class t_VOID_TYPE, class t_FN, class... t_ARGTYPES>
struct InvokeResult_FuncPtrImp;

/// Forward declaration
template <class t_FN, class... t_ARGTYPES>
struct InvokeResult_MemFuncPtrImp;

/// Forward declaration
template <class t_FN, class... t_ARGTYPES>
struct InvokeResult_MemObjPtrImp;

                      // ================================
                      // struct template InvokeResult_Imp
                      // ================================

/// This component-private, partial `struct` template specialization
/// provides the implementation of `InvokeResult_Imp` for types that are
/// neither function pointers, pointers to member functions, nor pointers to
/// member objects.
template <bool t_IS_FUNCPTR,
          bool t_IS_MEMFUNCPTR,
          bool t_IS_MEMOBJPTR,
          class t_FN,
          class... t_ARGTYPES>
struct InvokeResult_Imp : InvokeResult_FunctorImp<void, t_FN, t_ARGTYPES...> {
};

/// This component-private, partial `struct` template specialization
/// provides the implementation of `InvokeResult_Imp` for function pointer
/// types.
template <class t_FN, class... t_ARGTYPES>
struct InvokeResult_Imp<true /* t_IS_FUNCPTR */,
                        false,
                        false,
                        t_FN,
                        t_ARGTYPES...>
: InvokeResult_FuncPtrImp<void, t_FN, t_ARGTYPES...> {
};

/// This component-private, partial `struct` template specialization
/// provides the implementation of `InvokeResult_Imp` for pointer to member
/// function types.
template <class t_FN, class... t_ARGTYPES>
struct InvokeResult_Imp<false,
                        true /* t_IS_MEMFUNCPTR */,
                        false,
                        t_FN,
                        t_ARGTYPES...>
: InvokeResult_MemFuncPtrImp<t_FN, t_ARGTYPES...> {
};

/// This component-private, partial `struct` template specialization
/// provides the implementation of `InvokeResult_Imp` for pointer to member
/// object types.
template <class t_FN, class... t_ARGTYPES>
struct InvokeResult_Imp<false,
                        false,
                        true /* t_IS_MEMOBJPTR */,
                        t_FN,
                        t_ARGTYPES...>
: InvokeResult_MemObjPtrImp<t_FN, t_ARGTYPES...> {
};

                  // =======================================
                  // struct template InvokeResult_FunctorImp
                  // =======================================

#ifdef BSLMF_INVOKERESULT_SUPPORT_CPP17_SEMANTICS
/// Implementation of `invoke_result<t_FN, t_ARGTYPES...>`.  This
/// specialization is instantiated in C++11 and later when `t_FN` is neither
/// a pointer-to-function, pointer-to-member-function, nor
/// pointer-to-member-object type, and the `INVOKE(fn, args...)` expression,
/// is *not* well-formed given `fn` is an object of the specified `t_FN`
/// type and `args...` are objects of the specified `t_ARGTYPES...` types.
/// The `INVOKE(fn, args...)` expression in this case is `fn(args...)`.
/// Note that this `struct` does not provide a `type` typedef.
template <class t_VOID_TYPE, class t_FN, class... t_ARGTYPES>
struct InvokeResult_FunctorImp {
};

/// Implementation of `invoke_result<t_FN, t_ARGTYPES...>`.  This
/// specialization is instantiated in C++11 and later when `t_FN` is neither
/// a pointer-to-function, pointer-to-member-function, nor
/// pointer-to-member-object type, and the `INVOKE(fn, args...)` expression
/// is well-formed given `fn` is an object of the specified `t_FN` type and
/// `args...` are objects of the specified `t_ARGTYPES...` types.  The
/// `INVOKE(fn, args...)` expression in this case is `fn(args...)`.
template <class t_FN, class... t_ARGTYPES>
struct InvokeResult_FunctorImp<
    typename bslmf::VoidType<decltype(InvokeResult_ImpUtils::myDeclval<t_FN>()(
                    InvokeResult_ImpUtils::myDeclval<t_ARGTYPES>()...))>::type,
    t_FN,
    t_ARGTYPES...> : InvokeResult_ImpUtils {

    // TYPES

    /// For C++11 and later, the type of the `INVOKE(fn, args...)`
    /// expression given `fn` is an object of the specified `t_FN` type and
    /// `args...` are objects of the specified `t_ARGTYPES...` types.  The
    /// `INVOKE(fn, args...)` expression in this case is `fn(args...)`.
    typedef decltype(myDeclval<t_FN>()(myDeclval<t_ARGTYPES>()...)) type;
};
#else   // ! BSLMF_INVOKERESULT_SUPPORT_CPP17_SEMANTICS
template <class t_VOID_TYPE, class t_FN, class... t_ARGTYPES>
struct InvokeResult_FunctorImp : InvokeResult_ImpUtils {
    // Implementation of 'invoke_result<t_FN, t_ARGTYPES...>'.  This
    // specialization is instantiated in C++03 when 't_FN' is neither a
    // pointer-to-function, pointer-to-member-function, nor
    // pointer-to-member-object type.

    // TYPES
    enum {
        // Determine if 'myDeclval<t_FN>()(myDeclval<t_ARGTYPES>()...)' is a
        // void expression by invoking the overloaded comma operator using a
        // 'InvokeResult_VoidChecker' as the second argument.  If the
        // expression is of void type, then the built-in comma operator will
        // yield 'InvokeResult_VoidChecker', otherwise the overloaded comma
        // operator will yield 'bslmf::Tag<false>'
        k_IS_VOID = BSLMF_TAG_TO_INT((
                                 myDeclval<t_FN>()(myDeclval<t_ARGTYPES>()...),
                                 InvokeResult_VoidChecker()))
    };

    typedef typename InvokeResult_FunctorDeduction<k_IS_VOID,
                                                   t_FN,
                                                   t_ARGTYPES...>::type type;
        // For C++03, the result of invoking
        // 'myDeclval<t_FN>()(myDeclval<t_ARGTYPES>()...)' if it can be deduced
        // without 'decltype'; otherwise 'InvokeResultDeductionFailed'.
};
#endif  // BSLMF_INVOKERESULT_SUPPORT_CPP17_SEMANTICS

                  // =======================================
                  // struct template InvokeResult_FuncPtrImp
                  // =======================================

#ifdef BSLMF_INVOKERESULT_SUPPORT_CPP17_SEMANTICS
/// Implementation of `invoke_result<t_FN, t_ARGTYPES...>`.  This
/// specialization is instantiated in C++11 and later when `t_FN` is a
/// pointer-to-function type, and the `INVOKE(fn, args...)` expression is
/// *not* well-formed given `fn` is an object of the specified `t_FN` type
/// and `args...` are objects of the specified `t_ARGTYPES...` types.  The
/// `INVOKE(fn, args...)` expression in this case is `fn(args...)`.  Note
/// that this `struct` does not provide a `type` typedef.
template <class t_VOID_TYPE, class t_FN, class... t_ARGTYPES>
struct InvokeResult_FuncPtrImp {
};

/// Implementation of `invoke_result<t_FN, t_ARGTYPES...>`.  This
/// specialization is instantiated in C++11 and later when `t_FN` is a
/// pointer-to-function type, and the `INVOKE(fn, args...)` expression is
/// well-formed given `fn` is an object of the specified `t_FN` type and
/// `args...` are objects of the specified `t_ARGTYPES...` types.  The
/// `INVOKE(fn, args...)` expression in this case is `fn(args...)`.
template <class t_FN, class... t_ARGTYPES>
struct InvokeResult_FuncPtrImp<
    typename bslmf::VoidType<decltype(InvokeResult_ImpUtils::myDeclval<t_FN>()(
                    InvokeResult_ImpUtils::myDeclval<t_ARGTYPES>()...))>::type,
    t_FN,
    t_ARGTYPES...> : InvokeResult_ImpUtils {

    // TYPES

    /// For C++11 and later, the type of result of the `INVOKE(fn, args...)`
    /// expression where `fn` is an object of the specified `t_FN` type, and
    /// `args...` are objects of the specified `t_ARGTYPES...` types.  The
    /// `INVOKE(fn, args...)` expression in this case is is `fn(args...)`.
    typedef decltype(myDeclval<t_FN>()(myDeclval<t_ARGTYPES>()...)) type;
};
#else  // ! BSLMF_INVOKERESULT_SUPPORT_CPP17_SEMANTICS
template <class t_VOID_TYPE, class t_FN, class... t_ARGTYPES>
struct InvokeResult_FuncPtrImp {
    // Implementation of 'invoke_result<t_FN, t_ARGTYPES...>.  This
    // specialization is instantiated in C++03 when 't_FN' is a
    // pointer-to-function type.  Note that this C++03 implementation does not
    // check whether 't_ARGTYPES...' are valid for 't_FN'.

    typedef typename bslmf::FunctionPointerTraits<t_FN>::ResultType QType;
        // The return value of the function pointed-to by 't_FN'.

    typedef typename bsl::conditional<
        bsl::is_reference<QType>::value || bsl::is_class<QType>::value,
        QType,
        typename bsl::remove_cv<QType>::type>::type type;
        // The return value of the function pointed-to by 't_FN'.  If the type
        // is a scalar rvalue, cv qualifications are stripped off.
};
#endif // BSLMF_INVOKERESULT_SUPPORT_CPP17_SEMANTICS

                 // ==========================================
                 // struct template InvokeResult_MemFuncPtrImp
                 // ==========================================

#ifdef BSLMF_INVOKERESULT_SUPPORT_CPP17_SEMANTICS
/// Forward declaration.
template <class t_VOID_TYPE,
          bool t_ARG1_DERIVES_FROM_CLASS,
          bool t_ARG1_IS_REFERENCE_WRAPPER,
          class t_FN,
          class t_ARG1TYPE,
          class... t_ARGTYPES>
struct InvokeResult_MemFuncPtrImpDispatch;

// SPECIALIZATIONS

/// Implementation of `invoke_result<t_FN, t_ARGTYPES...>`.  This
/// specialization is instantiated in C++11 and later when `t_FN` is a
/// pointer-to-member-function type, and the `t_ARGTYPES...` pack is empty.
/// Note that this `struct` does not provide a `type` typedef.
template <class t_FN>
struct InvokeResult_MemFuncPtrImp<t_FN> {
};

/// Implementation of `invoke_result<t_FN, t_ARGTYPES...>`.  This
/// specialization is instantiated in C++11 and later when `t_FN` is a
/// pointer-to-member-function type, and the `t_ARGTYPES...` pack contains
/// 1 type or more.
template <class t_FN, class t_ARG1TYPE, class... t_ARGTYPES>
struct InvokeResult_MemFuncPtrImp<t_FN, t_ARG1TYPE, t_ARGTYPES...>
: InvokeResult_MemFuncPtrImpDispatch<
      void,
      IsAccessibleBaseOf<
          typename MemberFunctionPointerTraits<t_FN>::ClassType,
          typename bsl::remove_reference<t_ARG1TYPE>::type>::value,
      IsReferenceWrapper<typename bsl::remove_const<
          typename bsl::remove_reference<t_ARG1TYPE>::type>::type>::value,
      t_FN,
      t_ARG1TYPE,
      t_ARGTYPES...> {
};
#else  // ! BSLMF_INVOKERESULT_SUPPORT_CPP17_SEMANTICS
template <class t_FN, class... t_ARGTYPES>
struct InvokeResult_MemFuncPtrImp<t_FN, t_ARGTYPES...> {
    // Implementation of 'invoke_result<t_FN, t_ARGTYPES...>.  This
    // specialization is instantiated in C++03 when 't_FN' is a
    // pointer-to-member-function type.

    typedef typename MemberFunctionPointerTraits<t_FN>::ResultType QType;
        // The return value of the function pointed-to by 't_FN'.

    typedef typename bsl::conditional<
        bsl::is_reference<QType>::value || bsl::is_class<QType>::value,
        QType,
        typename bsl::remove_cv<QType>::type>::type type;
        // The return value of the function pointed-to by 't_FN'.  If the type
        // is a scalar rvalue, cv qualifications are stripped off.
};
#endif // BSLMF_INVOKERESULT_SUPPORT_CPP17_SEMANTICS

             // ==================================================
             // struct template InvokeResult_MemFuncPtrImpDispatch
             // ==================================================

#ifdef BSLMF_INVOKERESULT_SUPPORT_CPP17_SEMANTICS
/// Implementation of `invoke_result<t_FN, t_ARG1TYPE, t_ARGTYPES...>`.
/// This specialization is instantiated in C++11 and later when `t_FN` is a
/// pointer-to-member-function type, and the `INVOKE(fn, arg1, args...)`
/// expression is *not* well-formed given `fn` is an object of the specified
/// `t_FN` type and `arg1, args...` are objects of the specified
/// `t_ARG1TYPE, t_ARGTYPES...` types.  Note that this `struct` does not
/// provide a `type` typedef.
template <class    t_VOID_TYPE,
          bool     t_ARG1_DERIVES_FROM_CLASS,
          bool     t_ARG1_IS_REFERENCE_WRAPPER,
          class    t_FN,
          class    t_ARG1TYPE,
          class... t_ARGTYPES>
struct InvokeResult_MemFuncPtrImpDispatch {
};

/// Implementation of `invoke_result<t_FN, t_ARG1TYPE, t_ARGTYPES...>`.
/// This specialization is instantiated in C++11 and later when `t_FN` is a
/// pointer-to-member-function type, `t_ARG1TYPE` is neither a class type
/// that derives from the class type of `t_FN` nor a specialization of
/// `bsl::reference_wrapper`, and the `INVOKE(fn, arg1, args...)` expression
/// is well-formed given `fn` is an object of the specified `t_FN` type and
/// `arg1, args...` are objects of the specified `t_ARG1TYPE, t_ARGTYPES...`
/// types.  The `INVOKE(fn, arg1, args...)` expression in this case is
/// `((*arg1).*fn)(args...)`.
template <class t_FN, class t_ARG1TYPE, class... t_ARGTYPES>
struct InvokeResult_MemFuncPtrImpDispatch<
    typename bslmf::VoidType<
        decltype(((*InvokeResult_ImpUtils::myDeclval<t_ARG1TYPE>()).*
                  InvokeResult_ImpUtils::myDeclval<t_FN>())(
                    InvokeResult_ImpUtils::myDeclval<t_ARGTYPES>()...))>::type,
    /* t_ARG1_DERIVES_FROM_CLASS */ false,
    /* t_ARG1_IS_REFERENCE_WRAPPER */ false,
    t_FN,
    t_ARG1TYPE,
    t_ARGTYPES...> : InvokeResult_ImpUtils {

    // TYPES

    /// For C++11 and later, the type of the `INVOKE(fn, args...)`
    /// expression where `fn` is an object of the specified `t_FN` type, and
    /// `arg1, args...` are objects of the specified
    /// `t_ARG1TYPE, t_ARGTYPES...` types.  The `INVOKE(fn, arg1, args...)`
    /// expression in this case is `((*arg1).*fn)(args...)`.
    typedef decltype(((*myDeclval<t_ARG1TYPE>()).*
                      myDeclval<t_FN>())(myDeclval<t_ARGTYPES>()...)) type;
};

/// Implementation of `invoke_result<t_FN, t_ARGTYPES...>`.  This
/// specialization is instantiated in C++11 and later when `t_FN` is a
/// pointer-to-member-function type, `t_ARG1TYPE` is a class type that
/// derives from the class type of `t_FN`, and the
/// `INVOKE(fn, arg1, args...)` expression is well-formed given `fn` is an
/// object of the specified `t_FN` type and `arg1, args...` are objects of
/// the specified `t_ARG1TYPE, t_ARGTYPES...` types.  The
/// `INVOKE(fn, arg1, args...)` expression in this case is
/// `(arg1.*fn)(args...)`.
template <class t_FN, class t_ARG1TYPE, class... t_ARGTYPES>
struct InvokeResult_MemFuncPtrImpDispatch<
    typename bslmf::VoidType<
        decltype((InvokeResult_ImpUtils::myDeclval<t_ARG1TYPE>().*
                  InvokeResult_ImpUtils::myDeclval<t_FN>())(
                    InvokeResult_ImpUtils::myDeclval<t_ARGTYPES>()...))>::type,
    /* t_ARG1_DERIVES_FROM_CLASS */ true,
    /* t_ARG1_IS_REFERENCE_WRAPPER */ false,
    t_FN,
    t_ARG1TYPE,
    t_ARGTYPES...> : InvokeResult_ImpUtils {

    // TYPES

    /// For C++11 and later, the type of the `INVOKE(fn, arg1, args...)`
    /// expression where `fn` is an object of the specified `t_FN` type, and
    /// `arg1, args...` are objects of the specified
    /// `t_ARG1TYPE, t_ARGTYPES...` types.  The `INVOKE(fn, arg1, args...)`
    /// expression in this case is `(arg1.*fn)(args...)`.
    typedef decltype((myDeclval<t_ARG1TYPE>().*
                      myDeclval<t_FN>())(myDeclval<t_ARGTYPES>()...)) type;
};

/// Implementation of `invoke_result<t_FN, t_ARGTYPES...>`.  This
/// specialization is instantiated in C++11 and later when `t_FN` is a
/// pointer-to-member-function type, `t_ARG1TYPE` is a specialization of
/// `bsl::reference_wrapper`, and the `INVOKE(fn, arg1, args...)` expression
/// is well-formed given `fn` is an object of the specified `t_FN` type and
/// `arg1, args...` are objects of the specified `t_ARG1TYPE, t_ARGTYPES...`
/// types.  The `INVOKE(fn, arg1, args...)` expression in this case is
/// `(arg1.get().*fn)(args...)`.
template <class t_FN, class t_ARG1TYPE, class... t_ARGTYPES>
struct InvokeResult_MemFuncPtrImpDispatch<
    typename bslmf::VoidType<
        decltype((InvokeResult_ImpUtils::myDeclval<t_ARG1TYPE>().get().*
                  InvokeResult_ImpUtils::myDeclval<t_FN>())(
                    InvokeResult_ImpUtils::myDeclval<t_ARGTYPES>()...))>::type,
    /* t_ARG1_DERIVES_FROM_CLASS */ false,
    /* t_ARG1_IS_REFERENCE_WRAPPER */ true,
    t_FN,
    t_ARG1TYPE,
    t_ARGTYPES...> : InvokeResult_ImpUtils {

    // TYPES

    /// For C++11 and later, the type of the `INVOKE(fn, arg1, args...)`
    /// expression where `fn` is an object of the specified `t_FN` type, and
    /// `arg1, args...` are objects of the specified
    /// `t_ARG1TYPE, t_ARGTYPES...` types.  The `INVOKE(fn, arg1, args...)`
    /// expression in this case is `(arg1.get().*fn)(args...)`.
    typedef decltype((myDeclval<t_ARG1TYPE>().get().*
                      myDeclval<t_FN>())(myDeclval<t_ARGTYPES>()...)) type;
};

#endif // BSLMF_INVOKERESULT_SUPPORT_CPP17_SEMANTICS

                 // =========================================
                 // struct template InvokeResult_MemObjPtrImp
                 // =========================================

#ifdef BSLMF_INVOKERESULT_SUPPORT_CPP17_SEMANTICS
/// Forward declaration.
template <class t_VOID_TYPE,
          bool  t_ARG_DERIVES_FROM_CLASS,
          bool  t_ARG_IS_REFERENCE_WRAPPER,
          class t_FN,
          class t_ARGTYPE>
struct InvokeResult_MemObjPtrImpDispatch;

// SPECIALIZATIONS

/// Implementation of `invoke_result<t_FN, t_ARGTYPES...>`.  This
/// specialization is instantiated in C++11 and later when `t_FN` is a
/// pointer-to-member-object type, and the `t_ARGTYPES...` pack is empty or
/// contains more than 1 type.  Note that this `struct` does not provide a
/// `type` typedef.
template <class t_FN, class... t_ARGTYPES>
struct InvokeResult_MemObjPtrImp {
};

/// Implementation of `invoke_result<t_FN, t_ARGTYPES...>`.  This
/// specialization is instantiated in C++11 and later when `t_FN` is a
/// pointer-to-member-object type and the `t_ARGTYPES...` pack contains
/// exactly 1 type.
template <class t_FN, class t_ARGTYPE>
struct InvokeResult_MemObjPtrImp<t_FN, t_ARGTYPE>
: InvokeResult_MemObjPtrImpDispatch<
      void,
      IsAccessibleBaseOf<
          typename MemberPointerTraits<t_FN>::ClassType,
          typename bsl::remove_reference<t_ARGTYPE>::type>::value,
      IsReferenceWrapper<typename bsl::remove_const<
          typename bsl::remove_reference<t_ARGTYPE>::type>::type>::value,
      t_FN,
      t_ARGTYPE> {
};

#else // ! BSLMF_INVOKERESULT_SUPPORT_CPP17_SEMANTICS

template <class t_FN, class... t_ARGTYPES>
struct InvokeResult_MemObjPtrImp {
};

template <class t_CLASS, class t_RET, class t_ARGTYPE>
struct InvokeResult_MemObjPtrImp<t_RET t_CLASS::*, t_ARGTYPE> {
    // Implementation of 'invoke_result<t_FN, t_ARGTYPES...>.  This
    // specialization is instantated in C++03 when 't_FN' is a pointer to data
    // member and 't_ARGTYPE' is an rvalue type.

  private:
    typedef InvokeResult_MemPtrArgQualifiers<t_CLASS, t_ARGTYPE> ArgQualifiers;
        // Determine the cv qualifications and reference qualifications from
        // 't_ARGTYPE' that should be applied to 't_RET'.

    typedef typename InvokeResult_AddCVRef<t_RET,
                                           ArgQualifiers::k_IS_CONST,
                                           ArgQualifiers::k_IS_VOLATILE,
                                           ArgQualifiers::k_IS_LVALUE>::type
        cvtype;
        // The type of member pointed to by the pointer-to-member-object, with
        // cv and reference qualifiers taken from 't_ARGTYPE'.

  public:
#ifdef BSLS_COMPILERFEATURES_SUPPORT_RVALUE_REFERENCES
    typedef typename bsl::conditional<
        ArgQualifiers::k_IS_LVALUE,
        cvtype,
        typename bsl::add_rvalue_reference<cvtype>::type>::type type;
        // Result type.  If rvalue references are supported, data members of
        // rvalues are always returned as rvalue references in C++11 and later.
#else
    typedef cvtype type;
        // Rvalue result type.
#endif
};
#endif // BSLMF_INVOKERESULT_SUPPORT_CPP17_SEMANTICS

             // =================================================
             // struct template InvokeResult_MemObjPtrImpDispatch
             // =================================================

#ifdef BSLMF_INVOKERESULT_SUPPORT_CPP17_SEMANTICS
/// Implementation of `invoke_result<t_FN, t_ARGTYPE>`.  This specialization
/// is instantiated in C++11 and later when `t_FN` is a
/// pointer-to-member-object type, and the `INVOKE(fn, arg)` expression is
/// *not* well-formed given `fn` is an object of the specified `t_FN` type
/// and `arg` is an object of the specified `t_ARGTYPE`.  Note that this
/// `struct` does not provide a `type` typedef.
template <class t_VOID_TYPE,
          bool  t_ARG_DERIVES_FROM_CLASS,
          bool  t_ARG_IS_REFERENCE_WRAPPER,
          class t_FN,
          class t_ARGTYPE>
struct InvokeResult_MemObjPtrImpDispatch {
};

/// Implementation of `invoke_result<t_FN, t_ARGTYPE>`.  This specialization
/// is instantiated in C++11 and later when `t_FN` is a
/// pointer-to-member-object type, `t_ARGTYPE` is neither a class type that
/// derives from the class type of `t_FN` nor a specialization of
/// `bsl::reference_wrapper`, and the `INVOKE(fn, arg)` expression is
/// well-formed given `fn` is an object of the specified `t_FN` type and
/// `arg` is an object of the specified `t_ARGTYPE` type.  The
/// `INVOKE(fn, arg)` expression in this case is `(*arg).*fn`.
template <class t_FN, class t_ARGTYPE>
struct InvokeResult_MemObjPtrImpDispatch<
    typename bslmf::VoidType<
        decltype((*InvokeResult_ImpUtils::myDeclval<t_ARGTYPE>()).*
                 InvokeResult_ImpUtils::myDeclval<t_FN>())>::type,
    /* t_ARG1_DERIVES_FROM_CLASS */ false,
    /* t_ARG1_IS_REFERENCE_WRAPPER */ false,
    t_FN,
    t_ARGTYPE> : InvokeResult_ImpUtils {

    // TYPES
    typedef decltype((*myDeclval<t_ARGTYPE>()).*myDeclval<t_FN>()) type;
    // For C++11 and later, the type of the 'INVOKE(fn, arg)' expression where
    // 'fn' is an object of the specified 't_FN' type, and 'arg' is an object
    // of the specified 't_ARGTYPE' type.  The 'INVOKE(fn, arg)' expression in
    // this case is '(*arg).*fn'.
};

/// Implementation of `invoke_result<t_FN, t_ARGTYPE>`.  This specialization
/// is instantiated in C++11 and later when `t_FN` is a
/// pointer-to-member-object type, `t_ARGTYPE` is a class type that derives
/// from the class type of `t_FN`, and the `INVOKE(fn, arg)` expression is
/// well-formed given `fn` is an object of the specified `t_FN` type and
/// `arg` is an object of the specified `t_ARGTYPE` type.  The
/// `INVOKE(fn, arg)` expression in this case is `arg1.*fn`.
template <class t_FN, class t_ARGTYPE>
struct InvokeResult_MemObjPtrImpDispatch<
    typename bslmf::VoidType<
        decltype(InvokeResult_ImpUtils::myDeclval<t_ARGTYPE>().*
                 InvokeResult_ImpUtils::myDeclval<t_FN>())>::type,
    /* t_ARG_DERIVES_FROM_CLASS */ true,
    /* t_ARG_IS_REFERENCE_WRAPPER */ false,
    t_FN,
    t_ARGTYPE> : InvokeResult_ImpUtils {

    // TYPES
    typedef decltype(myDeclval<t_ARGTYPE>().*myDeclval<t_FN>()) type;
    // For C++11 and later, the type of the 'INVOKE(fn, arg)' expression where
    // 'fn' is an object of the specified 't_FN' type, and 'arg' is an object
    // of the specified 't_ARGTYPE' type.  The 'INVOKE(fn, arg)' expression in
    // this case is 'arg1.*fn'.
};

/// Implementation of `invoke_result<t_FN, t_ARGTYPE>`.  This specialization
/// is instantiated in C++11 and later when `t_FN` is a
/// pointer-to-member-object type, `t_ARGTYPE` is a specialization of
/// `bsl::reference_wrapper`, and the `INVOKE(fn, arg)` expression is
/// well-formed given `fn` is an object of the specified `t_FN` type and
/// `arg` is an object of the specified `t_ARGTYPE` type.  The
/// `INVOKE(fn, arg)` expression in this case is `arg.get().*fn`.
template <class t_FN, class t_ARGTYPE>
struct InvokeResult_MemObjPtrImpDispatch<
    typename bslmf::VoidType<
        decltype(InvokeResult_ImpUtils::myDeclval<t_ARGTYPE>().get().*
                 InvokeResult_ImpUtils::myDeclval<t_FN>())>::type,
    /* t_ARG1_DERIVES_FROM_CLASS */ false,
    /* t_ARG1_IS_REFERENCE_WRAPPER */ true,
    t_FN,
    t_ARGTYPE> : InvokeResult_ImpUtils {

    // TYPES
    typedef decltype(myDeclval<t_ARGTYPE>().get().*myDeclval<t_FN>()) type;
    // For C++11 and later, the type of the 'INVOKE(fn, arg)' expression where
    // 'fn' is an object of the specified 't_FN' type, and 'arg' is an object
    // of the specified 't_ARGTYPE' type.  The 'INVOKE(fn, arg)' expression in
    // this case is 'arg.get().*fn'.
};

#endif // BSLMF_INVOKERESULT_SUPPORT_CPP17_SEMANTICS

#endif

}  // close package namespace
}  // close enterprise namespace

#endif // End C++11 code

#endif // ! defined(INCLUDED_BSLMF_INVOKERESULT)

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