//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

// <functional>

// INVOKE (f, t1, t2, ..., tN)

//------------------------------------------------------------------------------
// TESTING INVOKE(f, t1, t2, ..., tN)
//   - Bullet 7 -- f(t2, ..., tN)
//
// Overview:
//    Bullet 7 handles the cases where the first argument is not a member
//   function.
//
// Concerns:
//   1) Different types of callable objects are supported. Including
//      1a) Free Function pointers and references.
//      1b) Classes which provide a call operator
//      1c) lambdas
//   2) The callable objects are perfect forwarded.
//   3) The arguments are perfect forwarded.
//   4) Signatures which include varargs are supported.
//   5) In C++03 3 extra arguments should be allowed.
//
// Plan:
//  1) Define a set of free functions, 'SF', and class types with call
//     operators, 'SC', that address concerns 4 and 5. The free functions should
//     return 'FunctionID::setUncheckedCall()' and the call operators should
//     return 'MethodID::setUncheckedCall()'.
//
//  2) For each function 'f' in 'SF' and 'SC' attempt to call 'f'
//     using the correct number of arguments and cv-ref qualifiers. Check that
//     'f' has been called using 'FunctionID::checkCall()' if 'f' is a free
//     function and 'MethodID::checkCall()' otherwise.



#include <functional>
#include <type_traits>
#include <cassert>

#include "test_macros.h"
#include "invoke_helpers.h"


//==============================================================================
// freeFunction03 - A C++03 free function.
void*& freeFunction03() {
    return FunctionPtrID<void*&(), freeFunction03>::setUncheckedCall();
}

void*& freeFunction03(...) {
    return FunctionPtrID<void*&(...), freeFunction03>::setUncheckedCall();
}

template <class A0>
void*& freeFunction03(A0&) {
    return FunctionPtrID<void*&(A0&), freeFunction03>::setUncheckedCall();
}


template <class A0>
void*& freeFunction03(A0&, ...) {
    return FunctionPtrID<void*&(A0&, ...), freeFunction03>::setUncheckedCall();
}

template <class A0, class A1>
void*& freeFunction03(A0&, A1&) {
    return FunctionPtrID<void*&(A0&, A1&), freeFunction03>::setUncheckedCall();
}


template <class A0, class A1>
void*& freeFunction03(A0&, A1&, ...) {
    return FunctionPtrID<void*&(A0&, A1&, ...), freeFunction03>::setUncheckedCall();
}

template <class A0, class A1, class A2>
void*& freeFunction03(A0&, A1&, A2&) {
    return FunctionPtrID<void*&(A0&, A1&, A2&), freeFunction03>::setUncheckedCall();
}

template <class A0, class A1, class A2>
void*& freeFunction03(A0&, A1&, A2&, ...) {
    return FunctionPtrID<void*&(A0&, A1&, A2&, ...), freeFunction03>::setUncheckedCall();
}

//==============================================================================
// Functor03 - C++03 compatible functor object
struct Functor03 {
    typedef void*& R;
    typedef Functor03 C;
#define F(Args, ...) \
    __VA_ARGS__ R operator() Args { return MethodID<R(C::*) Args>::setUncheckedCall(); } \
    __VA_ARGS__ R operator() Args const { return MethodID<R(C::*) Args const>::setUncheckedCall(); } \
    __VA_ARGS__ R operator() Args volatile { return MethodID<R(C::*) Args volatile>::setUncheckedCall(); } \
    __VA_ARGS__ R operator() Args const volatile { return MethodID<R(C::*) Args const volatile>::setUncheckedCall(); }
#
    F(())
    F((A0&), template <class A0>)
    F((A0&, A1&), template <class A0, class A1>)
    F((A0&, A1&, A2&), template <class A0, class A1, class A2>)
#undef F
public:
    Functor03() {}
private:
    Functor03(Functor03 const&);
    Functor03& operator=(Functor03 const&);
};


#if TEST_STD_VER >= 11

//==============================================================================
// freeFunction11 - A C++11 free function.
template <class ...Args>
void*& freeFunction11(Args&&...) {
    return FunctionPtrID<void*&(Args&&...), freeFunction11>::setUncheckedCall();
}

template <class ...Args>
void*& freeFunction11(Args&&...,...) {
    return FunctionPtrID<void*&(Args&&...,...), freeFunction11>::setUncheckedCall();
}

//==============================================================================
// Functor11 - C++11 reference qualified test member functions.
struct Functor11 {
    typedef void*& R;
    typedef Functor11 C;

#define F(CV) \
    template <class ...Args> \
    R operator()(Args&&...) CV { return MethodID<R(C::*)(Args&&...) CV>::setUncheckedCall(); }
#
    F(&)
    F(const &)
    F(volatile &)
    F(const volatile &)
    F(&&)
    F(const &&)
    F(volatile &&)
    F(const volatile &&)
#undef F
public:
    Functor11() {}
private:
    Functor11(Functor11 const&);
    Functor11& operator=(Functor11 const&);
};

#endif // TEST_STD_VER >= 11


//==============================================================================
// TestCaseFunctorImp - A test case for an operator() class method.
//   ClassType - The type of the call object.
//   CallSig   - The function signature of the call operator being tested.
//   Arity     - the arity of 'CallSig'
//   ObjCaster - Transformation function applied to call object.
//   ArgCaster - Transformation function applied to the extra arguments.
template <class ClassType, class CallSig, int Arity,
          class ObjCaster, class ArgCaster = LValueCaster>
struct TestCaseFunctorImp {
public:
    static void run() {
        typedef MethodID<CallSig ClassType::*> MID;
        BasicTest<MID, Arity, ObjCaster, ArgCaster> t;
        typedef ClassType T;
        typedef DerivedFromType<T> D;
        T obj;
        D der;
        t.runTest(obj);
        t.runTest(der);
    }
};

//==============================================================================
// TestCaseFreeFunction - A test case for a free function.
//   CallSig   - The function signature of the free function being tested.
//   FnPtr     - The function being tested.
//   Arity     - the arity of 'CallSig'
//   ArgCaster - Transformation function to be applied to the extra arguments.
template <class CallSig, CallSig* FnPtr, int Arity, class ArgCaster>
struct TestCaseFreeFunction {
public:
    static void run() {
        typedef FunctionPtrID<CallSig, FnPtr> FID;
        BasicTest<FID, Arity, LValueCaster, ArgCaster> t;

        DerefToType<CallSig*> deref_to(FnPtr);
        DerefToType<CallSig&> deref_to_ref(*FnPtr);

        t.runTest(FnPtr);
        t.runTest(*FnPtr);
        t.runTest(deref_to);
        t.runTest(deref_to_ref);
    }
};

//==============================================================================
//                          runTest Helpers
//==============================================================================
#if TEST_STD_VER >= 11
template <class Sig, int Arity, class ArgCaster>
void runFunctionTestCase11() {
    TestCaseFreeFunction<Sig, freeFunction11, Arity, ArgCaster>();
}
#endif

template <class Sig, int Arity, class ArgCaster>
void runFunctionTestCase() {
    TestCaseFreeFunction<Sig, freeFunction03, Arity, ArgCaster>();
#if TEST_STD_VER >= 11
    runFunctionTestCase11<Sig, Arity, ArgCaster>();
#endif
}

template <class Sig, int Arity, class ObjCaster, class ArgCaster>
void runFunctorTestCase() {
    TestCaseFunctorImp<Functor03, Sig, Arity, ObjCaster, ArgCaster>::run();
}

template <class Sig, int Arity, class ObjCaster>
void runFunctorTestCase() {
    TestCaseFunctorImp<Functor03, Sig, Arity, ObjCaster>::run();
}

#if TEST_STD_VER >= 11
// runTestCase - Run a test case for C++11 class functor types
template <class Sig, int Arity, class ObjCaster, class ArgCaster = LValueCaster>
void runFunctorTestCase11() {
    TestCaseFunctorImp<Functor11, Sig, Arity, ObjCaster, ArgCaster>::run();
}
#endif

// runTestCase - Run a test case for both function and functor types.
template <class Sig, int Arity, class ArgCaster>
void runTestCase() {
    runFunctionTestCase<Sig, Arity, ArgCaster>();
    runFunctorTestCase <Sig, Arity, LValueCaster, ArgCaster>();
};

int main(int, char**) {
    typedef void*& R;
    typedef ArgType A;
    typedef A const CA;

    runTestCase< R(),                                   0, LValueCaster      >();
    runTestCase< R(A&),                                 1, LValueCaster      >();
    runTestCase< R(A&, A&),                             2, LValueCaster      >();
    runTestCase< R(A&, A&, A&),                         3, LValueCaster      >();
    runTestCase< R(CA&),                                1, ConstCaster       >();
    runTestCase< R(CA&, CA&),                           2, ConstCaster       >();
    runTestCase< R(CA&, CA&, CA&),                      3, ConstCaster       >();

    runFunctionTestCase<R(...),                         0, LValueCaster      >();
    runFunctionTestCase<R(A&, ...),                     1, LValueCaster      >();
    runFunctionTestCase<R(A&, A&, ...),                 2, LValueCaster      >();
    runFunctionTestCase<R(A&, A&, A&, ...),             3, LValueCaster      >();

#if TEST_STD_VER >= 11
    runFunctionTestCase11<R(A&&),                       1, MoveCaster        >();
    runFunctionTestCase11<R(A&&, ...),                  1, MoveCaster        >();
#endif

    runFunctorTestCase<R(),                             0, LValueCaster      >();
    runFunctorTestCase<R() const,                       0, ConstCaster       >();
    runFunctorTestCase<R() volatile,                    0, VolatileCaster    >();
    runFunctorTestCase<R() const volatile,              0, CVCaster          >();
    runFunctorTestCase<R(A&),                           1, LValueCaster      >();
    runFunctorTestCase<R(A&) const,                     1, ConstCaster       >();
    runFunctorTestCase<R(A&) volatile,                  1, VolatileCaster    >();
    runFunctorTestCase<R(A&) const volatile,            1, CVCaster          >();
    runFunctorTestCase<R(A&, A&),                       2, LValueCaster      >();
    runFunctorTestCase<R(A&, A&) const,                 2, ConstCaster       >();
    runFunctorTestCase<R(A&, A&) volatile,              2, VolatileCaster    >();
    runFunctorTestCase<R(A&, A&) const volatile,        2, CVCaster          >();
    runFunctorTestCase<R(A&, A&, A&),                   3, LValueCaster      >();
    runFunctorTestCase<R(A&, A&, A&) const,             3, ConstCaster       >();
    runFunctorTestCase<R(A&, A&, A&) volatile,          3, VolatileCaster    >();
    runFunctorTestCase<R(A&, A&, A&) const volatile,    3, CVCaster          >();
    {
    typedef ConstCaster CC;
    runFunctorTestCase<R(CA&),                          1, LValueCaster,   CC>();
    runFunctorTestCase<R(CA&) const,                    1, ConstCaster,    CC>();
    runFunctorTestCase<R(CA&) volatile,                 1, VolatileCaster, CC>();
    runFunctorTestCase<R(CA&) const volatile,           1, CVCaster,       CC>();
    runFunctorTestCase<R(CA&, CA&),                     2, LValueCaster,   CC>();
    runFunctorTestCase<R(CA&, CA&) const,               2, ConstCaster,    CC>();
    runFunctorTestCase<R(CA&, CA&) volatile,            2, VolatileCaster, CC>();
    runFunctorTestCase<R(CA&, CA&) const volatile,      2, CVCaster,       CC>();
    runFunctorTestCase<R(CA&, CA&, CA&),                3, LValueCaster,   CC>();
    runFunctorTestCase<R(CA&, CA&, CA&) const,          3, ConstCaster,    CC>();
    runFunctorTestCase<R(CA&, CA&, CA&) volatile,       3, VolatileCaster, CC>();
    runFunctorTestCase<R(CA&, CA&, CA&) const volatile, 3, CVCaster,       CC>();
    }

#if TEST_STD_VER >= 11
    runFunctorTestCase11<R() &,                    0, LValueCaster          >();
    runFunctorTestCase11<R() const &,              0, ConstCaster           >();
    runFunctorTestCase11<R() volatile &,           0, VolatileCaster        >();
    runFunctorTestCase11<R() const volatile &,     0, CVCaster              >();
    runFunctorTestCase11<R() &&,                   0, MoveCaster            >();
    runFunctorTestCase11<R() const &&,             0, MoveConstCaster       >();
    runFunctorTestCase11<R() volatile &&,          0, MoveVolatileCaster    >();
    runFunctorTestCase11<R() const volatile &&,    0, MoveCVCaster          >();
    {
    typedef MoveCaster MC;
    runFunctorTestCase11<R(A&&) &,                 1, LValueCaster,       MC>();
    runFunctorTestCase11<R(A&&) const &,           1, ConstCaster,        MC>();
    runFunctorTestCase11<R(A&&) volatile &,        1, VolatileCaster,     MC>();
    runFunctorTestCase11<R(A&&) const volatile &,  1, CVCaster,           MC>();
    runFunctorTestCase11<R(A&&) &&,                1, MoveCaster,         MC>();
    runFunctorTestCase11<R(A&&) const &&,          1, MoveConstCaster,    MC>();
    runFunctorTestCase11<R(A&&) volatile &&,       1, MoveVolatileCaster, MC>();
    runFunctorTestCase11<R(A&&) const volatile &&, 1, MoveCVCaster,       MC>();
    }
#endif

  return 0;
}
