// bslmf_resulttype.h                                                 -*-C++-*-
#ifndef INCLUDED_BSLMF_RESULTTYPE
#define INCLUDED_BSLMF_RESULTTYPE

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

//@PURPOSE: Provide access to 'result_type' or 'ResultType' nested type.
//
//@CLASSES:
//  bslmf::ResultType: metafunction to return result type
//
//@SEE_ALSO:
//
//@DESCRIPTION: A number of functor classes provide a 'typedef' alias for the
// returned type when invoking the functor.  Unfortunately, standard types name
// this 'typedef' 'result_type' whereas BDE types name this 'typedef'
// 'ResultType'.  This component facilitates writing code that depends on these
// 'typedef's by providing a uniform interface, 'bslmf::ResultType', for
// extracting the correct alias.  'bslmf::ResultType<t_FUNC>::type' is
// identical to 't_FUNC::result_type' if such a type exists; otherwise, it is
// identical to 't_FUNC::ResultType' if that type exists; otherwise, it is
// undefined.  A fallback type can be optionally specified such that
// 'bslmf::ResultType<t_FUNC, t_FALLBACK>::type' is identical to 't_FALLBACK'
// if neither 't_FUNC::result_type' nor 't_FUNC::ResultType' is defined.
//
// Note that 'ResultType' checks only for a nested type within its 't_FUNC'
// parameter; it does not attempt to deduce the return type of calling
// 'operator()' the way C++11 'std::result_of' does.
//
///Usage
///-----
// In this section we show intended use of this component.
//
///Usage Example 1
///- - - - - - - -
// In this example, we write a C++03-compatible function template,
// 'wrapInvoke<t_FUNC>(t_A1 a1, t_A2 a2)', that constructs an instance of
// functor 't_FUNC', invokes it with arguments 'a1' and 'a2', and translates
// any thrown exception to a generic exception type.
//
// First, we declare the generic exception type:
//..
//  struct InvocationException { };
//..
// Now, we declare 'wrapInvoke'.  The return type of 'wrapInvoke' should be the
// same as the return type of invoking an object of type 't_FUNC'.  There is no
// non-intrusive way to deduce the return type of 't_FUNC' in C++03.  We
// therefore require that 't_FUNC' provide either a 'result_type' nested type
// (the idiom used in standard library functors) or a 'ResultType' nested type
// (the idiom used in BDE library functors).  We use 'bslmf::ResultType' to
// automatically select the correct idiom:
//..
//  template <class t_FUNC, class t_A1, class t_A2>
//  typename bslmf::ResultType<t_FUNC>::type
//  wrapInvoke(t_A1 a1, t_A2 a2)
//  {
//      t_FUNC f;
//      try {
//          return f(a1, a2);                                         // RETURN
//      }
//      catch (...) {
//          throw InvocationException();
//      }
//  }
//..
// Next, we declare a functor class that compares its arguments, returns the
// string "less" if 'a1 < a2', returns "greater" if 'a2 > a1', and throws an
// exception if neither is true.  The return type of this functor is declared
// using the BDE-style 'ResultType' nested type:
//..
//  struct LessGreater {
//      typedef const char *ResultType;
//      struct BadArgs { };  // exception class
//
//      const char *operator()(long a1, long a2);
//          // For the specified long integers 'a1' and 'a2', return the
//          // null-terminated string "less" if 'a1 < a2' and "greater" if
//          // 'a2 < a1'; otherwise, throw 'BadArgs()'.
//  };
//
//  const char *LessGreater::operator()(long a1, long a2)
//  {
//      if (a1 < a2) {
//          return "less";                                            // RETURN
//      }
//      else if (a2 < a1) {
//          return "greater";                                         // RETURN
//      }
//      else {
//          throw BadArgs();
//      }
//  }
//..
// For comparison, let's also define a 'plus' functor that conforms to the
// C++11 standard definition of 'std::plus':
//..
//  template <class TYPE>
//  struct plus {
//      typedef TYPE first_argument_type;
//      typedef TYPE second_argument_type;
//      typedef TYPE result_type;
//      TYPE operator()(const TYPE& x, const TYPE& y) const { return x + y; }
//  };
//..
// Now, we can use 'wrapInvoke' with our 'LessGreater' functor:
//..
//  int main()
//  {
//      const char *s = wrapInvoke<LessGreater>(5, -2);
//      assert(0 == std::strcmp(s, "greater"));
//..
// Finally, we confirm that we can also use 'wrapInvoke' with the functor
// 'plus<int>':
//..
//      int sum = wrapInvoke<plus<int> >(5, -2);
//      assert(3 == sum);
//
//      return 0;
//  }
//..
///Usage Example 2
///- - - - - - - -
// This example extends the previous one by considering a functor that does not
// declare either 'result_type' or 'ResultType'.  The 'PostIncrement' functor
// performs the operation '*a += b' and returns the old value of '*a':
//..
//  struct PostIncrement {
//      int operator()(int *a, int b)
//      {
//          unsigned tmp = *a;
//          *a += b;
//          return tmp;
//      }
//  };
//..
// Unfortunately, the following innocent-looking code is ill-formed; even
// though the return value is being discarded, the return type of 'wrapInvoke'
// is undefined because 'bslmf::ResultType<PostIncrement>::type' is undefined:
//..
//  // int x = 10;
//  // wrapInvoke<PostIncrement>(x, 2);  // ill-formed
//..
// To make 'wrapInvoke' usable in these situations, we define a new version,
// 'wrapInvoke2', that will fall back to a 'void' return type if neither
// 't_FUNC::result_type' nor 't_FUNC::ResultType' is defined:
//..
//  template <class t_FUNC, class t_A1, class t_A2>
//  typename bslmf::ResultType<t_FUNC, void>::type
//  wrapInvoke2(t_A1 a1, t_A2 a2)
//  {
//      typedef typename bslmf::ResultType<t_FUNC, void>::type RetType;
//      t_FUNC f;
//      try {
//          // C-style cast needed for some compilers
//          return (RetType)f(a1, a2);                                // RETURN
//      }
//      catch (...) {
//          throw InvocationException();
//      }
//  }
//..
// This use of the fallback parameter allows us to use 'bslmf::ResultType' in a
// context where the return type of a function might be ignored:
//..
//  int main()
//  {
//      int x = 10;
//      wrapInvoke2<PostIncrement>(&x, 2);
//      assert(12 == x);
//      return 0;
//  }
//..

#include <bslscm_version.h>

#include <bslmf_nil.h>
#include <bslmf_voidtype.h>

namespace BloombergLP {
namespace bslmf {

template <class t_FUNC, class t_FALLBACK, class = void>
struct ResultType_BdeIdiom;

                        // ================
                        // class ResultType
                        // ================

template <class t_FUNC, class t_FALLBACK = bslmf::Nil, class = void>
struct ResultType : ResultType_BdeIdiom<t_FUNC, t_FALLBACK> {
    // Metafunction to return the result type of the specified functor type
    // 't_FUNC'.  The nested 'type' is identical to 't_FUNC::result_type' if
    // such a type exists; otherwise, it is identical to 't_FUNC::ResultType'
    // if that type exists; otherwise, it is identical to the 't_FALLBACK'
    // template parameter if it was specified; otherwise, it is undefined.
};

template <class t_FUNC, class t_FALLBACK>
struct ResultType<t_FUNC,
                  t_FALLBACK,
                  BSLMF_VOIDTYPE(typename t_FUNC::result_type)> {
    // Specialization of 'ResultType' for when 't_FUNC::result_type' exists,
    // i.e., when the C++ standard idiom for 'result_type' is used.

    typedef typename t_FUNC::result_type type;
};

// ============================================================================
//                TEMPLATE AND INLINE FUNCTION IMPLEMENTATIONS
// ============================================================================

template <class t_FALLBACK>
struct ResultType_Fallback {
    // Metafunction that defines 'type' to be the specified 't_FALLBACK' type
    // unless 't_FALLBACK' is 'bslmf::Nil'.

    typedef t_FALLBACK type;
};

template <>
struct ResultType_Fallback<bslmf::Nil> {
    // Specialization of 'ResultType_Fallback' that does not define 'type' if
    // 'bslmf::Nil' is specified as the fallback parameter.
};

template <class t_FUNC, class t_FALLBACK, class>
struct ResultType_BdeIdiom : ResultType_Fallback<t_FALLBACK> {
    // Metafunction to detect the BDE 'ResultType' idiom as part of the
    // implementation of 'bslmf::ResultType'.  This 'struct' is instantiated
    // when 't_FUNC::result_type' doesn't exist.  This primary template is
    // matched when 't_FUNC::ResultType' also does not exist.  The 't_FALLBACK'
    // type, if any, is produced.
};

template <class t_FUNC, class t_FALLBACK>
struct ResultType_BdeIdiom<t_FUNC,
                           t_FALLBACK,
                           BSLMF_VOIDTYPE(typename t_FUNC::ResultType)> {
    // Metafunction to detect the BDE 'ResultType' idiom as part of the
    // implementation of 'bslmf::ResultType'.  This specialization is
    // instantiated by 'ResultType' only if 't_FUNC::result_type' does not
    // exist and 't_FUNC::ResultType' does exist.

    typedef typename t_FUNC::ResultType type;
};

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

#endif

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