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.
This commit is contained in:
Alexander Grund 2022-01-26 14:15:06 +01:00
parent 0c20ca1ce9
commit b802c51c2d
No known key found for this signature in database
GPG key ID: AA48A0760367A42B
5 changed files with 157 additions and 103 deletions

View file

@ -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 <boost/preprocessor/repetition/repeat.hpp>
#include <boost/preprocessor/stringize.hpp>
#include <type_traits>
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<typename T>
using unwrap_signature_t = std::remove_pointer_t<parameter_type_t<T>>;
struct arg_type;
template<typename T, typename U>
struct arg_type<T(U)>
{
using type = U;
};
/// Used in MOCK_PROTECT_SIGNATURE to unwrap the passed function signature
/// T is something like `void(std::map<int, float>)`
template<typename T>
using unwrap_signature_t = std::remove_pointer_t<typename arg_type<T>::type>;
}} // namespace mock::detail
#define MOCK_HELPER(t) t##_mock(mock::detail::root, BOOST_PP_STRINGIZE(t))
@ -48,7 +57,7 @@ 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<S>::value, "Arity mismatch"); \
static_assert(n == mock::detail::function_arity_t<S>::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) \
@ -75,11 +84,8 @@ namespace mock { namespace detail {
#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<S>::value, "Arity mismatch"); \
return MOCK_HELPER(t)(MOCK_FORWARD_PARAMS(n, S)); \
}
static_assert(n == mock::detail::function_arity_t<S>::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

View file

@ -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<class...>
struct tuple;
template<std::size_t I, class T>
struct tuple_element;
template<std::size_t I, class H, class... T>
struct tuple_element<I, tuple<H, T...>> : tuple_element<I - 1, tuple<T...>>
{};
template<class H, class... T>
struct tuple_element<0, tuple<H, T...>>
{
using type = H;
};
template<typename Signature>
struct result_type;
template<typename R, typename... Args>
struct result_type<R(Args...)>
{
using type = R;
};
template<typename Signature>
using result_type_t = typename result_type<Signature>::type;
template<typename Signature>
struct function_arity;
template<typename R, typename... Args>
struct function_arity<R(Args...)>
{
static constexpr size_t value = sizeof...(Args);
};
template<typename Signature>
struct parameter_types;
template<typename R, typename... Args>
struct parameter_types<R(Args...)>
{
using type = tuple<Args...>;
};
template<typename Signature, std::size_t n>
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<Signature>::value, "Parameter index exceeds the number of parameters");
using type = typename tuple_element<n, typename parameter_types<Signature>::type>::type;
};
template<typename T, std::size_t n>
using parameter_t = typename parameter<T, n>::type;
template<typename T>
struct parameter_type;
template<typename T, typename U>
struct parameter_type<T(U)>
{
using type = U;
};
template<typename T>
using parameter_type_t = typename parameter_type<T>::type;
}} // namespace mock::detail
#endif // MOCK_PARAMETER_HPP_INCLUDED

View file

@ -54,19 +54,21 @@ namespace mock { namespace detail {
struct signature<Sig(C::*)> : signature<typename strip_function_qualifiers<Sig>::type>
{};
/// Return the (non-member) function signature out of (any) signature
template<typename M>
using signature_t = typename signature<std::remove_cv_t<std::remove_reference_t<M>>>::type;
using signature_t = typename signature<M>::type;
/// CRTP class to define the base_type typedef
template<typename T>
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<typename T>
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) \

View file

@ -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 <cstddef>
namespace mock { namespace detail {
/// Helper class to store a tuple/list of types
template<class...>
struct tuple;
/// Get the type at the given index in the tuple
template<std::size_t index, class Tuple>
struct tuple_element;
template<std::size_t I, class H, class... T>
struct tuple_element<I, tuple<H, T...>> : tuple_element<I - 1, tuple<T...>>
{};
template<class H, class... T>
struct tuple_element<0, tuple<H, T...>>
{
using type = H;
};
/// Provides information about a given function signature
/// Member types: return_type, args
/// Member constant: arity
template<typename Signature>
struct signature_traits;
template<typename R, typename... Args>
struct signature_traits<R(Args...)>
{
using return_type = R;
static constexpr std::size_t arity = sizeof...(Args);
using args = tuple<Args...>;
};
/// Return the result type of the function signature
template<typename Signature>
using result_type_t = typename signature_traits<Signature>::return_type;
/// Return the arity of the function signature
template<typename Signature>
using function_arity_t = std::integral_constant<std::size_t, signature_traits<Signature>::arity>;
/// Return the type at the given index of the function signature
template<typename Signature, std::size_t idx>
using parameter_t = typename tuple_element<idx, typename signature_traits<Signature>::args>::type;
}} // namespace mock::detail
#endif // MOCK_PARAMETER_HPP_INCLUDED

View file

@ -6,21 +6,87 @@
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#include <turtle/detail/signature.hpp>
#include <turtle/mock.hpp>
#include <boost/test/unit_test.hpp>
#include <map>
#include <type_traits>
namespace {
struct base
{
void method_1();
float method_2(int) const;
float method_2(int);
// Using templates in result and argument types
std::map<int, float> method_3(std::map<int, int>, 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<mock::detail::result_type_t<function1>, void>::value, "!");
static_assert(mock::detail::function_arity_t<function1>::value == 0, "!");
using function2 = float(int);
static_assert(std::is_same<mock::detail::result_type_t<function2>, float>::value, "!");
static_assert(mock::detail::function_arity_t<function2>::value == 1, "!");
static_assert(std::is_same<mock::detail::parameter_t<function2, 0>, int>::value, "!");
using function3 = unsigned(short&, int, const char*, float, double, char, unsigned char, std::map<int, char>);
static_assert(std::is_same<mock::detail::result_type_t<function3>, unsigned>::value, "!");
static_assert(mock::detail::function_arity_t<function3>::value == 8, "!");
static_assert(std::is_same<mock::detail::parameter_t<function3, 0>, short&>::value, "!");
static_assert(std::is_same<mock::detail::parameter_t<function3, 1>, int>::value, "!");
static_assert(std::is_same<mock::detail::parameter_t<function3, 2>, const char*>::value, "!");
static_assert(std::is_same<mock::detail::parameter_t<function3, 3>, float>::value, "!");
static_assert(std::is_same<mock::detail::parameter_t<function3, 4>, double>::value, "!");
static_assert(std::is_same<mock::detail::parameter_t<function3, 5>, char>::value, "!");
static_assert(std::is_same<mock::detail::parameter_t<function3, 6>, unsigned char>::value, "!");
static_assert(std::is_same<mock::detail::parameter_t<function3, 7>, std::map<int, char>>::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<void(), MOCK_SIGNATURE(method_1)>::value, "!");
static_assert(std::is_same<float(int), MOCK_SIGNATURE(method_2)>::value, "!");
static_assert(std::is_same<std::map<int, float>(std::map<int, int>, short), MOCK_SIGNATURE(method_3)>::value, "!");
static_assert(std::is_same<float(int), MOCK_SIGNATURE(qual_1)>::value, "!");
static_assert(std::is_same<float(int), MOCK_SIGNATURE(qual_2)>::value, "!");
static_assert(std::is_same<float(int), MOCK_SIGNATURE(qual_3)>::value, "!");
static_assert(std::is_same<float(int), MOCK_SIGNATURE(qual_4)>::value, "!");
static_assert(std::is_same<float(int), MOCK_SIGNATURE(qual_5)>::value, "!");
static_assert(std::is_same<float(int), MOCK_SIGNATURE(qual_6)>::value, "!");
static_assert(std::is_same<float(int), MOCK_SIGNATURE(qual_7)>::value, "!");
static_assert(std::is_same<float(int), MOCK_SIGNATURE(qual_8)>::value, "!");
static_assert(std::is_same<float(int), MOCK_SIGNATURE(qual_9)>::value, "!");
static_assert(std::is_same<float(int), MOCK_SIGNATURE(qual_10)>::value, "!");
static_assert(std::is_same<float(int), MOCK_SIGNATURE(qual_11)>::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<void(), MOCK_PROTECT_SIGNATURE(
void())>::value, "!");
static_assert(std::is_same<float*(int*), MOCK_PROTECT_SIGNATURE(
float*(int*))>::value, "!");
static_assert(std::is_same<std::map<int, float>(std::map<int, int>, short), MOCK_PROTECT_SIGNATURE(
std::map<int, float>(std::map<int, int>, short))>::value, "!");
// clang-format on
}