From b802c51c2d15434b521e71b4211e7f59f1873b97 Mon Sep 17 00:00:00 2001 From: Alexander Grund Date: Wed, 26 Jan 2022 14:15:06 +0100 Subject: [PATCH] Reduce number of template instantiations Combine result_type, function_arity, parameter_types into 1 trait. This reduces the amount of template classes instantiated by a factor of 3 which should improve compile times and memory consumption. Also improve tests and documentation of touched classes/traits. --- include/turtle/detail/mock_impl.hpp | 30 ++++---- include/turtle/detail/parameter.hpp | 82 ---------------------- include/turtle/detail/signature.hpp | 12 ++-- include/turtle/detail/signature_traits.hpp | 62 ++++++++++++++++ test/detail/test_signature.cpp | 74 +++++++++++++++++-- 5 files changed, 157 insertions(+), 103 deletions(-) delete mode 100644 include/turtle/detail/parameter.hpp create mode 100644 include/turtle/detail/signature_traits.hpp diff --git a/include/turtle/detail/mock_impl.hpp b/include/turtle/detail/mock_impl.hpp index d501002..211e81c 100644 --- a/include/turtle/detail/mock_impl.hpp +++ b/include/turtle/detail/mock_impl.hpp @@ -11,17 +11,26 @@ #include "function.hpp" #include "functor.hpp" -#include "parameter.hpp" #include "signature.hpp" +#include "signature_traits.hpp" #include "type_name.hpp" #include #include #include namespace mock { namespace detail { - /// Used in MOCK_PROTECT_SIGNATURE to unwrap the passed function signature + /// Simplified trait to extract the argument type of a function signature with 1 argument template - using unwrap_signature_t = std::remove_pointer_t>; + struct arg_type; + template + struct arg_type + { + using type = U; + }; + /// Used in MOCK_PROTECT_SIGNATURE to unwrap the passed function signature + /// T is something like `void(std::map)` + template + using unwrap_signature_t = std::remove_pointer_t::type>; }} // namespace mock::detail #define MOCK_HELPER(t) t##_mock(mock::detail::root, BOOST_PP_STRINGIZE(t)) @@ -47,8 +56,8 @@ namespace mock { namespace detail { #define MOCK_FORWARD_PARAM(z, n, d) BOOST_PP_COMMA_IF(n) d, n >> (p##n) #define MOCK_FORWARD_PARAMS(n, S) BOOST_PP_REPEAT(n, MOCK_FORWARD_PARAM, std::forward < MOCK_PARAM(S)) -#define MOCK_METHOD_AUX(M, n, S, t, c) \ - static_assert(n == mock::detail::function_arity::value, "Arity mismatch"); \ +#define MOCK_METHOD_AUX(M, n, S, t, c) \ + static_assert(n == mock::detail::function_arity_t::value, "Arity mismatch"); \ MOCK_DECL(M, n, S, c) { return MOCK_ANONYMOUS_HELPER(t)(MOCK_FORWARD_PARAMS(n, S)); } #define MOCK_METHOD_EXT(M, n, S, t) \ @@ -73,13 +82,10 @@ namespace mock { namespace detail { T(MOCK_DECL_PARAMS(n, void A)) { MOCK_HELPER(t)(MOCK_FORWARD_PARAMS(n, void A)); } \ MOCK_FUNCTION_HELPER(void A, t, static) -#define MOCK_FUNCTION_AUX(F, n, S, t, s) \ - MOCK_FUNCTION_HELPER(S, t, s) \ - s MOCK_DECL(F, n, S, ) \ - { \ - static_assert(n == mock::detail::function_arity::value, "Arity mismatch"); \ - return MOCK_HELPER(t)(MOCK_FORWARD_PARAMS(n, S)); \ - } +#define MOCK_FUNCTION_AUX(F, n, S, t, s) \ + MOCK_FUNCTION_HELPER(S, t, s) \ + static_assert(n == mock::detail::function_arity_t::value, "Arity mismatch"); \ + s MOCK_DECL(F, n, S, ) { return MOCK_HELPER(t)(MOCK_FORWARD_PARAMS(n, S)); } #define MOCK_VARIADIC_ELEM_0(e0, ...) e0 #define MOCK_VARIADIC_ELEM_1(e0, e1, ...) e1 diff --git a/include/turtle/detail/parameter.hpp b/include/turtle/detail/parameter.hpp deleted file mode 100644 index b21a19a..0000000 --- a/include/turtle/detail/parameter.hpp +++ /dev/null @@ -1,82 +0,0 @@ -// http://turtle.sourceforge.net -// -// Copyright Mathieu Champlon 2012 -// -// Distributed under the Boost Software License, Version 1.0. -// (See accompanying file LICENSE_1_0.txt or copy at -// http://www.boost.org/LICENSE_1_0.txt) - -#ifndef MOCK_PARAMETER_HPP_INCLUDED -#define MOCK_PARAMETER_HPP_INCLUDED - -#include "../config.hpp" - -namespace mock { namespace detail { - template - struct tuple; - - template - struct tuple_element; - - template - struct tuple_element> : tuple_element> - {}; - - template - struct tuple_element<0, tuple> - { - using type = H; - }; - - template - struct result_type; - - template - struct result_type - { - using type = R; - }; - template - using result_type_t = typename result_type::type; - - template - struct function_arity; - - template - struct function_arity - { - static constexpr size_t value = sizeof...(Args); - }; - - template - struct parameter_types; - - template - struct parameter_types - { - using type = tuple; - }; - - template - struct parameter - { - // This assertion is usually triggered when the arity passed to e.g. MOCK_METHOD exceeds the number of - // parameters of the mocked function or the passed function signature - static_assert(n < function_arity::value, "Parameter index exceeds the number of parameters"); - using type = typename tuple_element::type>::type; - }; - template - using parameter_t = typename parameter::type; - - template - struct parameter_type; - template - struct parameter_type - { - using type = U; - }; - template - using parameter_type_t = typename parameter_type::type; -}} // namespace mock::detail - -#endif // MOCK_PARAMETER_HPP_INCLUDED diff --git a/include/turtle/detail/signature.hpp b/include/turtle/detail/signature.hpp index ee8e577..cb4b3eb 100644 --- a/include/turtle/detail/signature.hpp +++ b/include/turtle/detail/signature.hpp @@ -54,19 +54,21 @@ namespace mock { namespace detail { struct signature : signature::type> {}; + /// Return the (non-member) function signature out of (any) signature template - using signature_t = typename signature>>::type; + using signature_t = typename signature::type; + /// CRTP class to define the base_type typedef template struct base { - typedef T base_type; + using base_type = T; }; - // if an error is generated by the line below it means the method is ambiguous: - // specify its signature to disambiguate + // If an error is generated by the line below it means the method is ambiguous. + // Specify its signature to disambiguate template - T& ambiguous_method_requires_to_specify_signature(const T&); + T ambiguous_method_requires_to_specify_signature(const T&); }} // namespace mock::detail #define MOCK_SIGNATURE(M) \ diff --git a/include/turtle/detail/signature_traits.hpp b/include/turtle/detail/signature_traits.hpp new file mode 100644 index 0000000..e6948f6 --- /dev/null +++ b/include/turtle/detail/signature_traits.hpp @@ -0,0 +1,62 @@ +// http://turtle.sourceforge.net +// +// Copyright Mathieu Champlon 2012 +// +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef MOCK_PARAMETER_HPP_INCLUDED +#define MOCK_PARAMETER_HPP_INCLUDED + +#include "../config.hpp" +#include + +namespace mock { namespace detail { + /// Helper class to store a tuple/list of types + template + struct tuple; + + /// Get the type at the given index in the tuple + template + struct tuple_element; + + template + struct tuple_element> : tuple_element> + {}; + + template + struct tuple_element<0, tuple> + { + using type = H; + }; + + /// Provides information about a given function signature + /// Member types: return_type, args + /// Member constant: arity + template + struct signature_traits; + + template + struct signature_traits + { + using return_type = R; + static constexpr std::size_t arity = sizeof...(Args); + using args = tuple; + }; + + /// Return the result type of the function signature + template + using result_type_t = typename signature_traits::return_type; + + /// Return the arity of the function signature + template + using function_arity_t = std::integral_constant::arity>; + + /// Return the type at the given index of the function signature + template + using parameter_t = typename tuple_element::args>::type; + +}} // namespace mock::detail + +#endif // MOCK_PARAMETER_HPP_INCLUDED diff --git a/test/detail/test_signature.cpp b/test/detail/test_signature.cpp index ad8228d..fd4e3ef 100644 --- a/test/detail/test_signature.cpp +++ b/test/detail/test_signature.cpp @@ -6,21 +6,87 @@ // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) -#include +#include #include +#include #include namespace { struct base { void method_1(); - float method_2(int) const; + float method_2(int); + // Using templates in result and argument types + std::map method_3(std::map, short); + + // Qualified methods + float qual_1(int) &; + float qual_2(int) &&; + float qual_3(int) const; + float qual_4(int) const&; + float qual_5(int) const&&; + float qual_6(int) volatile; + float qual_7(int) volatile&; + float qual_8(int) volatile&&; + float qual_9(int) const volatile; + float qual_10(int) const volatile&; + float qual_11(int) const volatile&&; }; -typedef base base_type; } // namespace -BOOST_AUTO_TEST_CASE(mock_signature_generates_signature) +BOOST_AUTO_TEST_CASE(signature_traits_return_correct_values) { + using function1 = void(); + static_assert(std::is_same, void>::value, "!"); + static_assert(mock::detail::function_arity_t::value == 0, "!"); + + using function2 = float(int); + static_assert(std::is_same, float>::value, "!"); + static_assert(mock::detail::function_arity_t::value == 1, "!"); + static_assert(std::is_same, int>::value, "!"); + + using function3 = unsigned(short&, int, const char*, float, double, char, unsigned char, std::map); + static_assert(std::is_same, unsigned>::value, "!"); + static_assert(mock::detail::function_arity_t::value == 8, "!"); + static_assert(std::is_same, short&>::value, "!"); + static_assert(std::is_same, int>::value, "!"); + static_assert(std::is_same, const char*>::value, "!"); + static_assert(std::is_same, float>::value, "!"); + static_assert(std::is_same, double>::value, "!"); + static_assert(std::is_same, char>::value, "!"); + static_assert(std::is_same, unsigned char>::value, "!"); + static_assert(std::is_same, std::map>::value, "!"); +} + +BOOST_AUTO_TEST_CASE(MOCK_SIGNATURE_generates_signature) +{ + using base_type = base; // MOCK_SIGNATURE requires a visible base_type typedef in the current scope static_assert(std::is_same::value, "!"); static_assert(std::is_same::value, "!"); + static_assert(std::is_same(std::map, short), MOCK_SIGNATURE(method_3)>::value, "!"); + static_assert(std::is_same::value, "!"); + static_assert(std::is_same::value, "!"); + static_assert(std::is_same::value, "!"); + static_assert(std::is_same::value, "!"); + static_assert(std::is_same::value, "!"); + static_assert(std::is_same::value, "!"); + static_assert(std::is_same::value, "!"); + static_assert(std::is_same::value, "!"); + static_assert(std::is_same::value, "!"); + static_assert(std::is_same::value, "!"); + static_assert(std::is_same::value, "!"); +} + +BOOST_AUTO_TEST_CASE(MOCK_PROTECT_SIGNATURE_keeps_signature) +{ + // MOCK_PROTECT_SIGNATURE is basically a no-op regarding its argument + // and only required to get it through a VAR_ARGS macro + // clang-format off + static_assert(std::is_same::value, "!"); + static_assert(std::is_same::value, "!"); + static_assert(std::is_same(std::map, short), MOCK_PROTECT_SIGNATURE( + std::map(std::map, short))>::value, "!"); + // clang-format on }