#ifndef INVOKE_HELPERS_H #define INVOKE_HELPERS_H #include <type_traits> #include <cassert> #include <functional> #include "test_macros.h" template <int I> struct Int : public std::integral_constant<int, I> {}; template <bool P> struct Bool : public std::integral_constant<bool, P> {}; struct Q_None { template <class T> struct apply { typedef T type; }; }; struct Q_Const { template <class T> struct apply { typedef T const type; }; }; struct Q_Volatile { template <class T> struct apply { typedef T volatile type; }; }; struct Q_CV { template <class T> struct apply { typedef T const volatile type; }; }; // Caster - A functor object that performs cv-qualifier and value category // conversions. // QualTag - A metafunction type that applies cv-qualifiers to its argument. // RValue - True if the resulting object should be an RValue reference. // False otherwise. template <class QualTag, bool RValue = false> struct Caster { template <class T> struct apply { typedef typename std::remove_reference<T>::type RawType; typedef typename QualTag::template apply<RawType>::type CVType; #if TEST_STD_VER >= 11 typedef typename std::conditional<RValue, CVType&&, CVType& >::type type; #else typedef CVType& type; #endif }; template <class T> typename apply<T>::type operator()(T& obj) const { typedef typename apply<T>::type OutType; return static_cast<OutType>(obj); } }; typedef Caster<Q_None> LValueCaster; typedef Caster<Q_Const> ConstCaster; typedef Caster<Q_Volatile> VolatileCaster; typedef Caster<Q_CV> CVCaster; typedef Caster<Q_None, true> MoveCaster; typedef Caster<Q_Const, true> MoveConstCaster; typedef Caster<Q_Volatile, true> MoveVolatileCaster; typedef Caster<Q_CV, true> MoveCVCaster; // A shorter name for 'static_cast' template <class QualType, class Tp> QualType C_(Tp& v) { return static_cast<QualType>(v); }; //============================================================================== // ArgType - A non-copyable type intended to be used as a dummy argument type // to test functions. struct ArgType { int value; explicit ArgType(int val = 0) : value(val) {} private: ArgType(ArgType const&); ArgType& operator=(ArgType const&); }; //============================================================================== // DerivedFromBase - A type that derives from it's template argument 'Base' template <class Base> struct DerivedFromType : public Base { DerivedFromType() : Base() {} template <class Tp> explicit DerivedFromType(Tp const& t) : Base(t) {} }; //============================================================================== // DerefToType - A type that dereferences to it's template argument 'To'. // The cv-ref qualifiers of the 'DerefToType' object do not propagate // to the resulting 'To' object. template <class To> struct DerefToType { To object; DerefToType() {} template <class Up> explicit DerefToType(Up const& val) : object(val) {} To& operator*() const volatile { return const_cast<To&>(object); } }; //============================================================================== // DerefPropToType - A type that dereferences to it's template argument 'To'. // The cv-ref qualifiers of the 'DerefPropToType' object propagate // to the resulting 'To' object. template <class To> struct DerefPropType { To object; DerefPropType() {} template <class Up> explicit DerefPropType(Up const& val) : object(val) {} #if TEST_STD_VER < 11 To& operator*() { return object; } To const& operator*() const { return object; } To volatile& operator*() volatile { return object; } To const volatile& operator*() const volatile { return object; } #else To& operator*() & { return object; } To const& operator*() const & { return object; } To volatile& operator*() volatile & { return object; } To const volatile& operator*() const volatile & { return object; } To&& operator*() && { return static_cast<To &&>(object); } To const&& operator*() const && { return static_cast<To const&&>(object); } To volatile&& operator*() volatile && { return static_cast<To volatile&&>(object); } To const volatile&& operator*() const volatile && { return static_cast<To const volatile&&>(object); } #endif }; //============================================================================== // MethodID - A type that uniquely identifies a member function for a class. // This type is used to communicate between the member functions being tested // and the tests invoking them. // - Test methods should call 'setUncheckedCall()' whenever they are invoked. // - Tests consume the unchecked call using checkCall(<return-value>)` to assert // that the method has been called and that the return value of `__invoke` // matches what the method actually returned. template <class T> struct MethodID { typedef void* IDType; static int dummy; // A dummy memory location. static void* id; // The "ID" is the value of this pointer. static bool unchecked_call; // Has a call happened that has not been checked. static void*& setUncheckedCall() { assert(unchecked_call == false); unchecked_call = true; return id; } static bool checkCalled(void*& return_value) { bool old = unchecked_call; unchecked_call = false; return old && id == return_value && &id == &return_value; } }; template <class T> int MethodID<T>::dummy = 0; template <class T> void* MethodID<T>::id = (void*)&MethodID<T>::dummy; template <class T> bool MethodID<T>::unchecked_call = false; //============================================================================== // FunctionPtrID - Like MethodID but for free function pointers. template <class T, T*> struct FunctionPtrID { static int dummy; // A dummy memory location. static void* id; // The "ID" is the value of this pointer. static bool unchecked_call; // Has a call happened that has not been checked. static void*& setUncheckedCall() { assert(unchecked_call == false); unchecked_call = true; return id; } static bool checkCalled(void*& return_value) { bool old = unchecked_call; unchecked_call = false; return old && id == return_value && &id == &return_value; } }; template <class T, T* Ptr> int FunctionPtrID<T, Ptr>::dummy = 0; template <class T, T* Ptr> void* FunctionPtrID<T, Ptr>::id = (void*)&FunctionPtrID<T, Ptr>::dummy; template <class T, T* Ptr> bool FunctionPtrID<T, Ptr>::unchecked_call = false; //============================================================================== // BasicTest - The basic test structure for everything except // member object pointers. // ID - The "Function Identifier" type used either MethodID or FunctionPtrID. // Arity - The Arity of the call signature. // ObjectCaster - The object transformation functor type. // ArgCaster - The extra argument transformation functor type. template <class ID, int Arity, class ObjectCaster = LValueCaster, class ArgCaster = LValueCaster> struct BasicTest { template <class ObjectT> void runTest(ObjectT& object) { Int<Arity> A; runTestImp(A, object); } template <class MethodPtr, class ObjectT> void runTest(MethodPtr ptr, ObjectT& object) { Int<Arity> A; runTestImp(A, ptr, object); } private: typedef void*& CallRet; ObjectCaster object_cast; ArgCaster arg_cast; ArgType a0, a1, a2; //========================================================================== // BULLET 1 AND 2 TEST METHODS //========================================================================== template <class MethodPtr, class ObjectT> void runTestImp(Int<0>, MethodPtr ptr, ObjectT& object) { static_assert((std::is_same< decltype(std::__invoke(ptr, object_cast(object))) , CallRet>::value), ""); assert(ID::unchecked_call == false); CallRet ret = std::__invoke(ptr, object_cast(object)); assert(ID::checkCalled(ret)); } template <class MethodPtr, class ObjectT> void runTestImp(Int<1>, MethodPtr ptr, ObjectT& object) { static_assert((std::is_same< decltype(std::__invoke(ptr, object_cast(object), arg_cast(a0))) , CallRet>::value), ""); assert(ID::unchecked_call == false); CallRet ret = std::__invoke(ptr, object_cast(object), arg_cast(a0)); assert(ID::checkCalled(ret)); } template <class MethodPtr, class ObjectT> void runTestImp(Int<2>, MethodPtr ptr, ObjectT& object) { static_assert((std::is_same< decltype(std::__invoke(ptr, object_cast(object), arg_cast(a0), arg_cast(a1))) , CallRet>::value), ""); assert(ID::unchecked_call == false); CallRet ret = std::__invoke(ptr, object_cast(object), arg_cast(a0), arg_cast(a1)); assert(ID::checkCalled(ret)); } template <class MethodPtr, class ObjectT> void runTestImp(Int<3>, MethodPtr ptr, ObjectT& object) { static_assert((std::is_same< decltype(std::__invoke(ptr, object_cast(object), arg_cast(a0), arg_cast(a1), arg_cast(a2))) , CallRet>::value), ""); assert(ID::unchecked_call == false); CallRet ret = std::__invoke(ptr, object_cast(object), arg_cast(a0), arg_cast(a1), arg_cast(a2)); assert(ID::checkCalled(ret)); } //========================================================================== // BULLET 5 TEST METHODS //========================================================================== template <class ObjectT> void runTestImp(Int<0>, ObjectT& object) { static_assert((std::is_same< decltype(std::__invoke(object_cast(object))) , CallRet>::value), ""); assert(ID::unchecked_call == false); CallRet ret = std::__invoke(object_cast(object)); assert(ID::checkCalled(ret)); } template <class ObjectT> void runTestImp(Int<1>, ObjectT& object) { static_assert((std::is_same< decltype(std::__invoke(object_cast(object), arg_cast(a0))) , CallRet>::value), ""); assert(ID::unchecked_call == false); CallRet ret = std::__invoke(object_cast(object), arg_cast(a0)); assert(ID::checkCalled(ret)); } template <class ObjectT> void runTestImp(Int<2>, ObjectT& object) { static_assert((std::is_same< decltype(std::__invoke(object_cast(object), arg_cast(a0), arg_cast(a1))) , CallRet>::value), ""); assert(ID::unchecked_call == false); CallRet ret = std::__invoke(object_cast(object), arg_cast(a0), arg_cast(a1)); assert(ID::checkCalled(ret)); } template <class ObjectT> void runTestImp(Int<3>, ObjectT& object) { static_assert((std::is_same< decltype(std::__invoke(object_cast(object), arg_cast(a0), arg_cast(a1), arg_cast(a2))) , CallRet>::value), ""); assert(ID::unchecked_call == false); CallRet ret = std::__invoke(object_cast(object), arg_cast(a0), arg_cast(a1), arg_cast(a2)); assert(ID::checkCalled(ret)); } }; #endif // INVOKE_HELPERS_H