| * Trompeloeil C++ mocking framework | * Trompeloeil C++ mocking framework | ||||
| * | * | ||||
| * Copyright Björn Fahller 2014-2018 | * Copyright Björn Fahller 2014-2018 | ||||
| * Copyright (C) 2017 Andrew Paxie | |||||
| * Copyright (C) 2017, 2018 Andrew Paxie | |||||
| * | * | ||||
| * Use, modification and distribution is subject to the | * Use, modification and distribution is subject to the | ||||
| * Boost Software License, Version 1.0. (See accompanying | * Boost Software License, Version 1.0. (See accompanying | ||||
| #if defined(__clang__) | #if defined(__clang__) | ||||
| #define TROMPELOEIL_CLANG 1 | |||||
| #define TROMPELOEIL_GCC 0 | |||||
| #define TROMPELOEIL_MSVC 0 | |||||
| # define TROMPELOEIL_CLANG 1 | |||||
| # define TROMPELOEIL_GCC 0 | |||||
| # define TROMPELOEIL_MSVC 0 | |||||
| #define TROMPELOEIL_GCC_VERSION 0 | |||||
| # define TROMPELOEIL_CLANG_VERSION \ | |||||
| (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) | |||||
| #define TROMPELOEIL_CPLUSPLUS __cplusplus | |||||
| # define TROMPELOEIL_GCC_VERSION 0 | |||||
| # define TROMPELOEIL_CPLUSPLUS __cplusplus | |||||
| #elif defined(__GNUC__) | #elif defined(__GNUC__) | ||||
| #define TROMPELOEIL_CLANG 0 | |||||
| #define TROMPELOEIL_GCC 1 | |||||
| #define TROMPELOEIL_MSVC 0 | |||||
| # define TROMPELOEIL_CLANG 0 | |||||
| # define TROMPELOEIL_GCC 1 | |||||
| # define TROMPELOEIL_MSVC 0 | |||||
| #define TROMPELOEIL_GCC_VERSION \ | |||||
| # define TROMPELOEIL_CLANG_VERSION 0 | |||||
| # define TROMPELOEIL_GCC_VERSION \ | |||||
| (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) | (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) | ||||
| #define TROMPELOEIL_CPLUSPLUS __cplusplus | |||||
| # define TROMPELOEIL_CPLUSPLUS __cplusplus | |||||
| #elif defined(_MSC_VER) | #elif defined(_MSC_VER) | ||||
| #define TROMPELOEIL_CLANG 0 | |||||
| #define TROMPELOEIL_GCC 0 | |||||
| #define TROMPELOEIL_MSVC 1 | |||||
| # define TROMPELOEIL_CLANG 0 | |||||
| # define TROMPELOEIL_GCC 0 | |||||
| # define TROMPELOEIL_MSVC 1 | |||||
| #define TROMPELOEIL_GCC_VERSION 0 | |||||
| # define TROMPELOEIL_CLANG_VERSION 0 | |||||
| # define TROMPELOEIL_GCC_VERSION 0 | |||||
| /* MSVC is an amalgam of C++ versions, with no provision to | |||||
| * force C++11 mode. It also has a __cplusplus macro stuck at 199711L. | |||||
| * Assume the C++14 code path. | |||||
| */ | |||||
| #define TROMPELOEIL_CPLUSPLUS 201401L | |||||
| # if defined(_MSVC_LANG) | |||||
| // Compiler is at least Microsoft Visual Studio 2015 Update 3. | |||||
| # define TROMPELOEIL_CPLUSPLUS _MSVC_LANG | |||||
| # else /* defined(_MSVC_LANG) */ | |||||
| /* | |||||
| * This version of Microsoft Visual C++ is released | |||||
| * in a version of Microsoft Visual Studio between | |||||
| * 2015 RC and less than 2015 Update 3. | |||||
| * | |||||
| * It is an amalgam of C++ versions, with no provision | |||||
| * to specify C++11 mode. | |||||
| * | |||||
| * It also has a __cplusplus macro stuck at 199711L with | |||||
| * no way to change it, such as /Zc:__cplusplus. | |||||
| * | |||||
| * Assume the C++14 code path, but don't promise that it is a | |||||
| * fully conforming implementation of C++14 either. | |||||
| * Hence a value of 201401L, which less than 201402L, | |||||
| * the standards conforming value of __cplusplus. | |||||
| */ | |||||
| # define TROMPELOEIL_CPLUSPLUS 201401L | |||||
| # endif /* !defined(_MSVC_LANG) */ | |||||
| #endif | #endif | ||||
| return {}; | return {}; | ||||
| } | } | ||||
| template <typename ...> | |||||
| struct void_t_ | |||||
| { | |||||
| using type = void; | |||||
| }; | |||||
| template <typename ... T> | |||||
| using void_t = typename void_t_<T...>::type; | |||||
| template <template <typename ...> class, typename, typename ...> | |||||
| struct is_detected_{ | |||||
| using type = std::false_type; | |||||
| }; | |||||
| template <template <typename ...> class D, typename ... Ts> | |||||
| struct is_detected_<D, void_t<D<Ts...>>, Ts...> | |||||
| { | |||||
| using type = std::true_type; | |||||
| }; | |||||
| template <template <typename ...> class D, typename ... Ts> | |||||
| using is_detected = typename is_detected_<D, void, Ts...>::type; | |||||
| # if (TROMPELOEIL_CPLUSPLUS == 201103L) | # if (TROMPELOEIL_CPLUSPLUS == 201103L) | ||||
| namespace detail | namespace detail | ||||
| # endif /* !(TROMPELOEIL_CPLUSPLUS == 201103L) */ | # endif /* !(TROMPELOEIL_CPLUSPLUS == 201103L) */ | ||||
| # if TROMPELOEIL_CPLUSPLUS >= 201703L | |||||
| # if TROMPELOEIL_CLANG && TROMPELOEIL_CLANG_VERSION >= 60000 | |||||
| // these are mostly added to work around clang++ bugs | |||||
| // https://bugs.llvm.org/show_bug.cgi?id=38033 | |||||
| // https://bugs.llvm.org/show_bug.cgi?id=38010 | |||||
| template <typename T> | |||||
| using move_construct_type = decltype(T(std::declval<T&&>())); | |||||
| template <typename T> | |||||
| using copy_construct_type = decltype(T(std::declval<const T&>())); | |||||
| template <typename T> | |||||
| using can_move_construct = is_detected<move_construct_type, detail::decay_t<T>>; | |||||
| template <typename T> | |||||
| using can_copy_construct = is_detected<copy_construct_type, detail::decay_t<T>>; | |||||
| # else | |||||
| template <typename T> | |||||
| using can_move_construct = std::is_move_constructible<T>; | |||||
| template <typename T> | |||||
| using can_copy_construct = std::is_copy_constructible<T>; | |||||
| # endif | |||||
| template <typename F, typename ... A> | |||||
| using invoke_result_type = decltype(std::declval<F&>()(std::declval<A>()...)); | |||||
| # else | |||||
| template <typename T> | |||||
| using can_move_construct = std::is_move_constructible<T>; | |||||
| template <typename T> | |||||
| using can_copy_construct = std::is_copy_constructible<T>; | |||||
| template <typename F, typename ... A> | |||||
| using invoke_result_type = decltype(std::declval<F&>()(std::declval<A>()...)); | |||||
| # endif | |||||
| class specialized; | class specialized; | ||||
| namespace { | namespace { | ||||
| unsigned long line, | unsigned long line, | ||||
| std::string const &call) = 0; | std::string const &call) = 0; | ||||
| protected: | protected: | ||||
| tracer() | |||||
| noexcept | |||||
| : previous{set_tracer(this)} {} | |||||
| tracer() = default; | |||||
| tracer(tracer const&) = delete; | tracer(tracer const&) = delete; | ||||
| tracer& operator=(tracer const&) = delete; | tracer& operator=(tracer const&) = delete; | ||||
| virtual | virtual | ||||
| set_tracer(previous); | set_tracer(previous); | ||||
| } | } | ||||
| private: | private: | ||||
| tracer* previous = nullptr; | |||||
| tracer* previous = set_tracer(this); | |||||
| }; | }; | ||||
| class stream_tracer : public tracer | class stream_tracer : public tracer | ||||
| } | } | ||||
| }; | }; | ||||
| template <typename ...> | |||||
| struct void_t_ | |||||
| struct matcher { }; | |||||
| struct wildcard : public matcher | |||||
| { | { | ||||
| using type = void; | |||||
| }; | |||||
| // This abomination of constructor seems necessary for g++ 4.9 and 5.1 | |||||
| template <typename ... T> | |||||
| constexpr | |||||
| wildcard( | |||||
| T&& ...) | |||||
| noexcept | |||||
| {} | |||||
| template <typename ... T> | |||||
| using void_t = typename void_t_<T...>::type; | |||||
| #if (!TROMPELOEIL_GCC) || \ | |||||
| (TROMPELOEIL_GCC && TROMPELOEIL_GCC_VERSION >= 40900) | |||||
| template <template <typename ...> class, typename, typename ...> | |||||
| struct is_detected_{ | |||||
| using type = std::false_type; | |||||
| }; | |||||
| // g++ 4.8 gives a "conversion from <T> to <U> is ambiguous" error | |||||
| // if this operator is defined. | |||||
| template <typename T, | |||||
| typename = detail::enable_if_t<!std::is_lvalue_reference<T>::value>> | |||||
| operator T&&() | |||||
| const; | |||||
| template <template <typename ...> class D, typename ... Ts> | |||||
| struct is_detected_<D, void_t<D<Ts...>>, Ts...> | |||||
| { | |||||
| using type = std::true_type; | |||||
| #endif | |||||
| template < | |||||
| typename T, | |||||
| typename = detail::enable_if_t< | |||||
| can_copy_construct<T>::value | |||||
| || !can_move_construct<T>::value | |||||
| > | |||||
| > | |||||
| operator T&() | |||||
| const; | |||||
| template <typename T> | |||||
| constexpr | |||||
| bool | |||||
| matches( | |||||
| T const&) | |||||
| const | |||||
| noexcept | |||||
| { | |||||
| return true; | |||||
| } | |||||
| friend | |||||
| std::ostream& | |||||
| operator<<( | |||||
| std::ostream& os, | |||||
| wildcard const&) | |||||
| noexcept | |||||
| { | |||||
| return os << " matching _"; | |||||
| } | |||||
| }; | }; | ||||
| template <template <typename ...> class D, typename ... Ts> | |||||
| using is_detected = typename is_detected_<D, void, Ts...>::type; | |||||
| static constexpr wildcard const _{}; | |||||
| struct matcher { }; | |||||
| template <typename T> | |||||
| template <typename T> | |||||
| using matcher_access = decltype(static_cast<matcher*>(std::declval<typename std::add_pointer<T>::type>())); | using matcher_access = decltype(static_cast<matcher*>(std::declval<typename std::add_pointer<T>::type>())); | ||||
| template <typename T> | template <typename T> | ||||
| template <typename T, | template <typename T, | ||||
| typename = decltype(std::declval<T>() == nullptr), | typename = decltype(std::declval<T>() == nullptr), | ||||
| typename = detail::enable_if_t<std::is_copy_constructible<T>::value>> | |||||
| typename = detail::enable_if_t<can_copy_construct<T>::value>> | |||||
| operator T&()const; | operator T&()const; | ||||
| template <typename T, typename C> | template <typename T, typename C> | ||||
| // g++ 4.8 gives a "conversion from <T> to <U> is ambiguous" error | // g++ 4.8 gives a "conversion from <T> to <U> is ambiguous" error | ||||
| // if this operator is defined. | // if this operator is defined. | ||||
| template <typename V, | template <typename V, | ||||
| typename = decltype(std::declval<Pred>()(std::declval<V&&>(), std::declval<T>()...))> | |||||
| typename = detail::enable_if_t<!is_matcher<V>{}>, | |||||
| typename = invoke_result_type<Pred, V&&, T...>> | |||||
| operator V&&() const; | operator V&&() const; | ||||
| #endif | #endif | ||||
| template <typename V, | template <typename V, | ||||
| typename = decltype(std::declval<Pred>()(std::declval<V&>(), std::declval<T>()...))> | |||||
| typename = detail::enable_if_t<!is_matcher<V>{}>, | |||||
| typename = invoke_result_type<Pred, V&, T...>> | |||||
| operator V&() const; | operator V&() const; | ||||
| }; | }; | ||||
| matchers.push_back(m); | matchers.push_back(m); | ||||
| } | } | ||||
| struct wildcard : public matcher | |||||
| { | |||||
| // This abomination of constructor seems necessary for g++ 4.9 and 5.1 | |||||
| template <typename ... T> | |||||
| constexpr | |||||
| wildcard( | |||||
| T&& ...) | |||||
| noexcept | |||||
| {} | |||||
| #if (!TROMPELOEIL_GCC) || \ | |||||
| (TROMPELOEIL_GCC && TROMPELOEIL_GCC_VERSION >= 40900) | |||||
| // g++ 4.8 gives a "conversion from <T> to <U> is ambiguous" error | |||||
| // if this operator is defined. | |||||
| template <typename T, | |||||
| typename = detail::enable_if_t<!std::is_lvalue_reference<T>::value>> | |||||
| operator T&&() | |||||
| const; | |||||
| #endif | |||||
| template <typename T, | |||||
| typename = detail::enable_if_t<std::is_copy_constructible<T>::value | |||||
| || !std::is_move_constructible<T>::value>> | |||||
| operator T&() | |||||
| const; | |||||
| template <typename T> | |||||
| constexpr | |||||
| bool | |||||
| matches( | |||||
| T const&) | |||||
| const | |||||
| noexcept | |||||
| { | |||||
| return true; | |||||
| } | |||||
| friend | |||||
| std::ostream& | |||||
| operator<<( | |||||
| std::ostream& os, | |||||
| wildcard const&) | |||||
| noexcept | |||||
| { | |||||
| return os << " matching _"; | |||||
| } | |||||
| }; | |||||
| static constexpr wildcard const _{}; | |||||
| template <typename T> | template <typename T> | ||||
| void can_match_parameter(T&); | void can_match_parameter(T&); | ||||