From 09bb6b5371759b5b9d59ad7118c1dd72e883f377 Mon Sep 17 00:00:00 2001 From: Alexander Grund Date: Sun, 20 Apr 2025 12:41:45 +0200 Subject: [PATCH 1/9] Change MOCK_METHOD_EXT to add support for arbitrary amounts of method modifiers --- include/turtle/detail/mock_impl.hpp | 34 +++++++++++----- include/turtle/detail/pp_foreach.hpp | 58 ++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+), 9 deletions(-) create mode 100644 include/turtle/detail/pp_foreach.hpp diff --git a/include/turtle/detail/mock_impl.hpp b/include/turtle/detail/mock_impl.hpp index 59fe4e0..cbf5976 100644 --- a/include/turtle/detail/mock_impl.hpp +++ b/include/turtle/detail/mock_impl.hpp @@ -12,11 +12,15 @@ #include "function.hpp" #include "functor.hpp" +#include "pp_foreach.hpp" #include "signature.hpp" #include "signature_traits.hpp" #include "type_name.hpp" +#include +#include #include #include +#include #include namespace mock { namespace detail { @@ -65,17 +69,29 @@ namespace mock { namespace detail { return MOCK_ANONYMOUS_HELPER(identifier)(MOCK_FORWARD_PARAMS(arity, signature)); \ } -#define MOCK_METHOD_EXT(name, arity, signature, identifier) \ - MOCK_METHOD_AUX(name, arity, signature, identifier, ) \ - MOCK_METHOD_AUX(name, arity, signature, identifier, const) \ - MOCK_METHOD_HELPER(signature, identifier) -#define MOCK_CONST_METHOD_EXT(name, arity, signature, identifier) \ - MOCK_METHOD_AUX(name, arity, signature, identifier, const) \ - MOCK_METHOD_HELPER(signature, identifier) -#define MOCK_NON_CONST_METHOD_EXT(name, arity, signature, identifier) \ - MOCK_METHOD_AUX(name, arity, signature, identifier, ) \ +/// MOCK_METHOD_ITER((name, arity, signature, identifier), qualifier) +#define MOCK_METHOD_ITER(M_n_S_t, qualifier) \ + MOCK_METHOD_AUX(BOOST_PP_TUPLE_ELEM(0, M_n_S_t), \ + BOOST_PP_TUPLE_ELEM(1, M_n_S_t), \ + BOOST_PP_TUPLE_ELEM(2, M_n_S_t), \ + BOOST_PP_TUPLE_ELEM(3, M_n_S_t), \ + qualifier) + +/// MOCK_METHOD_EXT_IMPL( name, arity, signature, identifier, qualifiers... ) +#define MOCK_METHOD_EXT_IMPL(name, arity, signature, identifier, ...) \ + static_assert(arity == mock::detail::function_arity_t::value, "Arity mismatch"); \ + MOCK_PP_FOR_EACH(MOCK_METHOD_ITER, (name, arity, signature, identifier), __VA_ARGS__) \ MOCK_METHOD_HELPER(signature, identifier) +/// MOCK_METHOD_EXT( name, arity, signature, identifier [ , qualifiers...] ) +/// If qualifiers is not given, defaults to (const, ), i.e. const and non-const +#define MOCK_METHOD_EXT(...) \ + MOCK_METHOD_EXT_IMPL \ + BOOST_PP_IF(BOOST_PP_LESS_EQUAL(BOOST_PP_VARIADIC_SIZE(__VA_ARGS__), 4), (__VA_ARGS__, const, ), (__VA_ARGS__)) + +#define MOCK_CONST_METHOD_EXT(M, n, S, t) MOCK_METHOD_EXT(M, n, S, t, const) +#define MOCK_NON_CONST_METHOD_EXT(M, n, S, t) MOCK_METHOD_EXT(M, n, S, t, ) + #define MOCK_FUNCTION_HELPER(signature, identifier, prefix) \ prefix mock::detail::function& identifier##_mock(mock::detail::context& context, \ boost::unit_test::const_string instance) \ diff --git a/include/turtle/detail/pp_foreach.hpp b/include/turtle/detail/pp_foreach.hpp new file mode 100644 index 0000000..e5579dc --- /dev/null +++ b/include/turtle/detail/pp_foreach.hpp @@ -0,0 +1,58 @@ +// http://turtle.sourceforge.net +// +// Copyright 2025 Alexander Grund +// +// 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_PP_FOREACH_HPP_INCLUDED +#define MOCK_PP_FOREACH_HPP_INCLUDED + +#include +#include + +/// Expand to `macro(data, elem)` for each element passed, similar to BOOST_PP_*_FOR_EACH +/// It supports empty arguments, +/// without causing a C4003 "not enough arguments for function-like macro invocation" warning on MSVC +#if BOOST_PP_VARIADICS_MSVC +# define MOCK_PP_FOR_EACH(macro, data, ...) \ + BOOST_PP_CAT(BOOST_PP_OVERLOAD(MOCK_PP_INVOKE_, __VA_ARGS__)(macro, data, __VA_ARGS__), BOOST_PP_EMPTY()) +#else +# define MOCK_PP_FOR_EACH(macro, data, ...) BOOST_PP_OVERLOAD(MOCK_PP_INVOKE_, __VA_ARGS__)(macro, data, __VA_ARGS__) +#endif + +#define MOCK_PP_INVOKE_1(macro, data, x) macro(data, x) +#define MOCK_PP_INVOKE_2(macro, data, x, ...) macro(data, x) MOCK_PP_INVOKE_1(macro, data, __VA_ARGS__) +#define MOCK_PP_INVOKE_3(macro, data, x, ...) macro(data, x) MOCK_PP_INVOKE_2(macro, data, __VA_ARGS__) +#define MOCK_PP_INVOKE_4(macro, data, x, ...) macro(data, x) MOCK_PP_INVOKE_3(macro, data, __VA_ARGS__) +#define MOCK_PP_INVOKE_5(macro, data, x, ...) macro(data, x) MOCK_PP_INVOKE_4(macro, data, __VA_ARGS__) +#define MOCK_PP_INVOKE_6(macro, data, x, ...) macro(data, x) MOCK_PP_INVOKE_5(macro, data, __VA_ARGS__) +#define MOCK_PP_INVOKE_7(macro, data, x, ...) macro(data, x) MOCK_PP_INVOKE_6(macro, data, __VA_ARGS__) +#define MOCK_PP_INVOKE_8(macro, data, x, ...) macro(data, x) MOCK_PP_INVOKE_7(macro, data, __VA_ARGS__) +#define MOCK_PP_INVOKE_9(macro, data, x, ...) macro(data, x) MOCK_PP_INVOKE_8(macro, data, __VA_ARGS__) +#define MOCK_PP_INVOKE_10(macro, data, x, ...) macro(data, x) MOCK_PP_INVOKE_9(macro, data, __VA_ARGS__) +#define MOCK_PP_INVOKE_11(macro, data, x, ...) macro(data, x) MOCK_PP_INVOKE_10(macro, data, __VA_ARGS__) +#define MOCK_PP_INVOKE_12(macro, data, x, ...) macro(data, x) MOCK_PP_INVOKE_11(macro, data, __VA_ARGS__) +#define MOCK_PP_INVOKE_13(macro, data, x, ...) macro(data, x) MOCK_PP_INVOKE_12(macro, data, __VA_ARGS__) +#define MOCK_PP_INVOKE_14(macro, data, x, ...) macro(data, x) MOCK_PP_INVOKE_13(macro, data, __VA_ARGS__) +#define MOCK_PP_INVOKE_15(macro, data, x, ...) macro(data, x) MOCK_PP_INVOKE_14(macro, data, __VA_ARGS__) +#define MOCK_PP_INVOKE_16(macro, data, x, ...) macro(data, x) MOCK_PP_INVOKE_15(macro, data, __VA_ARGS__) +#define MOCK_PP_INVOKE_17(macro, data, x, ...) macro(data, x) MOCK_PP_INVOKE_16(macro, data, __VA_ARGS__) +#define MOCK_PP_INVOKE_18(macro, data, x, ...) macro(data, x) MOCK_PP_INVOKE_17(macro, data, __VA_ARGS__) +#define MOCK_PP_INVOKE_19(macro, data, x, ...) macro(data, x) MOCK_PP_INVOKE_18(macro, data, __VA_ARGS__) +#define MOCK_PP_INVOKE_20(macro, data, x, ...) macro(data, x) MOCK_PP_INVOKE_19(macro, data, __VA_ARGS__) +#define MOCK_PP_INVOKE_21(macro, data, x, ...) macro(data, x) MOCK_PP_INVOKE_20(macro, data, __VA_ARGS__) +#define MOCK_PP_INVOKE_22(macro, data, x, ...) macro(data, x) MOCK_PP_INVOKE_21(macro, data, __VA_ARGS__) +#define MOCK_PP_INVOKE_23(macro, data, x, ...) macro(data, x) MOCK_PP_INVOKE_22(macro, data, __VA_ARGS__) +#define MOCK_PP_INVOKE_24(macro, data, x, ...) macro(data, x) MOCK_PP_INVOKE_23(macro, data, __VA_ARGS__) +#define MOCK_PP_INVOKE_25(macro, data, x, ...) macro(data, x) MOCK_PP_INVOKE_24(macro, data, __VA_ARGS__) +#define MOCK_PP_INVOKE_26(macro, data, x, ...) macro(data, x) MOCK_PP_INVOKE_25(macro, data, __VA_ARGS__) +#define MOCK_PP_INVOKE_27(macro, data, x, ...) macro(data, x) MOCK_PP_INVOKE_26(macro, data, __VA_ARGS__) +#define MOCK_PP_INVOKE_28(macro, data, x, ...) macro(data, x) MOCK_PP_INVOKE_27(macro, data, __VA_ARGS__) +#define MOCK_PP_INVOKE_29(macro, data, x, ...) macro(data, x) MOCK_PP_INVOKE_28(macro, data, __VA_ARGS__) +#define MOCK_PP_INVOKE_30(macro, data, x, ...) macro(data, x) MOCK_PP_INVOKE_29(macro, data, __VA_ARGS__) +#define MOCK_PP_INVOKE_31(macro, data, x, ...) macro(data, x) MOCK_PP_INVOKE_30(macro, data, __VA_ARGS__) +#define MOCK_PP_INVOKE_32(macro, data, x, ...) macro(data, x) MOCK_PP_INVOKE_31(macro, data, __VA_ARGS__) + +#endif // MOCK_PP_FOREACH_HPP_INCLUDED From a8f0253753ef1fa996a91b36c0b84e27cf662dfb Mon Sep 17 00:00:00 2001 From: Alexander Grund Date: Sun, 20 Apr 2025 14:36:30 +0200 Subject: [PATCH 2/9] Take modifiers as tuple --- include/turtle/detail/mock_impl.hpp | 21 +++++++++++---------- include/turtle/detail/pp_foreach.hpp | 11 ++++++++--- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/include/turtle/detail/mock_impl.hpp b/include/turtle/detail/mock_impl.hpp index cbf5976..bfa82a7 100644 --- a/include/turtle/detail/mock_impl.hpp +++ b/include/turtle/detail/mock_impl.hpp @@ -77,20 +77,21 @@ namespace mock { namespace detail { BOOST_PP_TUPLE_ELEM(3, M_n_S_t), \ qualifier) -/// MOCK_METHOD_EXT_IMPL( name, arity, signature, identifier, qualifiers... ) -#define MOCK_METHOD_EXT_IMPL(name, arity, signature, identifier, ...) \ +#define MOCK_METHOD_EXT_IMPL(name, arity, signature, identifier, qualifiers) \ static_assert(arity == mock::detail::function_arity_t::value, "Arity mismatch"); \ - MOCK_PP_FOR_EACH(MOCK_METHOD_ITER, (name, arity, signature, identifier), __VA_ARGS__) \ + MOCK_PP_TUPLE_FOR_EACH(MOCK_METHOD_ITER, (name, arity, signature, identifier), qualifiers) \ MOCK_METHOD_HELPER(signature, identifier) -/// MOCK_METHOD_EXT( name, arity, signature, identifier [ , qualifiers...] ) +/// MOCK_METHOD_EXT( name, arity, signature, identifier [ , qualifiers] ) /// If qualifiers is not given, defaults to (const, ), i.e. const and non-const -#define MOCK_METHOD_EXT(...) \ - MOCK_METHOD_EXT_IMPL \ - BOOST_PP_IF(BOOST_PP_LESS_EQUAL(BOOST_PP_VARIADIC_SIZE(__VA_ARGS__), 4), (__VA_ARGS__, const, ), (__VA_ARGS__)) - -#define MOCK_CONST_METHOD_EXT(M, n, S, t) MOCK_METHOD_EXT(M, n, S, t, const) -#define MOCK_NON_CONST_METHOD_EXT(M, n, S, t) MOCK_METHOD_EXT(M, n, S, t, ) +#define MOCK_METHOD_EXT(name, arity, signature, ...) \ + MOCK_METHOD_EXT_IMPL \ + BOOST_PP_IF(BOOST_PP_LESS_EQUAL(BOOST_PP_VARIADIC_SIZE(__VA_ARGS__), 1), \ + (name, arity, signature, __VA_ARGS__, (const, )), \ + (name, arity, signature, __VA_ARGS__)) + +#define MOCK_CONST_METHOD_EXT(M, n, S, t) MOCK_METHOD_EXT(M, n, S, t, (const)) +#define MOCK_NON_CONST_METHOD_EXT(M, n, S, t) MOCK_METHOD_EXT(M, n, S, t, ()) #define MOCK_FUNCTION_HELPER(signature, identifier, prefix) \ prefix mock::detail::function& identifier##_mock(mock::detail::context& context, \ diff --git a/include/turtle/detail/pp_foreach.hpp b/include/turtle/detail/pp_foreach.hpp index e5579dc..c10e0dd 100644 --- a/include/turtle/detail/pp_foreach.hpp +++ b/include/turtle/detail/pp_foreach.hpp @@ -9,12 +9,17 @@ #ifndef MOCK_PP_FOREACH_HPP_INCLUDED #define MOCK_PP_FOREACH_HPP_INCLUDED -#include +#include +#include #include +#include -/// Expand to `macro(data, elem)` for each element passed, similar to BOOST_PP_*_FOR_EACH -/// It supports empty arguments, +/// Expand to `macro(data, elem)` for each element in the tuple. +/// Same as BOOST_PP_TUPLE_FOR_EACH but supports empty elements, /// without causing a C4003 "not enough arguments for function-like macro invocation" warning on MSVC +#define MOCK_PP_TUPLE_FOR_EACH(macro, data, tuple) MOCK_PP_FOR_EACH(macro, data, BOOST_PP_REM tuple) + +/// Expand to `macro(data, elem)` for each variadic element passed #if BOOST_PP_VARIADICS_MSVC # define MOCK_PP_FOR_EACH(macro, data, ...) \ BOOST_PP_CAT(BOOST_PP_OVERLOAD(MOCK_PP_INVOKE_, __VA_ARGS__)(macro, data, __VA_ARGS__), BOOST_PP_EMPTY()) From f6200f9024aec894291e0fc581b7363ca694a8f7 Mon Sep 17 00:00:00 2001 From: Alexander Grund Date: Sun, 20 Apr 2025 14:52:58 +0200 Subject: [PATCH 3/9] Require qualifiers for MOCK_METHOD_EXT --- include/turtle/detail/mock_impl.hpp | 12 +----------- include/turtle/mock.hpp | 3 ++- 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/include/turtle/detail/mock_impl.hpp b/include/turtle/detail/mock_impl.hpp index bfa82a7..81cc955 100644 --- a/include/turtle/detail/mock_impl.hpp +++ b/include/turtle/detail/mock_impl.hpp @@ -16,8 +16,6 @@ #include "signature.hpp" #include "signature_traits.hpp" #include "type_name.hpp" -#include -#include #include #include #include @@ -77,19 +75,11 @@ namespace mock { namespace detail { BOOST_PP_TUPLE_ELEM(3, M_n_S_t), \ qualifier) -#define MOCK_METHOD_EXT_IMPL(name, arity, signature, identifier, qualifiers) \ +#define MOCK_METHOD_EXT(name, arity, signature, identifier, qualifiers) \ static_assert(arity == mock::detail::function_arity_t::value, "Arity mismatch"); \ MOCK_PP_TUPLE_FOR_EACH(MOCK_METHOD_ITER, (name, arity, signature, identifier), qualifiers) \ MOCK_METHOD_HELPER(signature, identifier) -/// MOCK_METHOD_EXT( name, arity, signature, identifier [ , qualifiers] ) -/// If qualifiers is not given, defaults to (const, ), i.e. const and non-const -#define MOCK_METHOD_EXT(name, arity, signature, ...) \ - MOCK_METHOD_EXT_IMPL \ - BOOST_PP_IF(BOOST_PP_LESS_EQUAL(BOOST_PP_VARIADIC_SIZE(__VA_ARGS__), 1), \ - (name, arity, signature, __VA_ARGS__, (const, )), \ - (name, arity, signature, __VA_ARGS__)) - #define MOCK_CONST_METHOD_EXT(M, n, S, t) MOCK_METHOD_EXT(M, n, S, t, (const)) #define MOCK_NON_CONST_METHOD_EXT(M, n, S, t) MOCK_METHOD_EXT(M, n, S, t, ()) diff --git a/include/turtle/mock.hpp b/include/turtle/mock.hpp index 31d5dc8..4e543df 100644 --- a/include/turtle/mock.hpp +++ b/include/turtle/mock.hpp @@ -80,7 +80,8 @@ MOCK_METHOD_EXT(M, \ BOOST_PP_VARIADIC_ELEM(0, __VA_ARGS__, ), \ BOOST_PP_VARIADIC_ELEM(1, __VA_ARGS__, MOCK_SIGNATURE(M), ), \ - BOOST_PP_VARIADIC_ELEM(2, __VA_ARGS__, M, M, )) + BOOST_PP_VARIADIC_ELEM(2, __VA_ARGS__, M, M, ), \ + (const, )) /// MOCK_CONST_METHOD( [calling convention] name, arity[, signature[, identifier]] ) /// generates only the const version of the method /// The 'signature' can be omitted if it can be uniquely identified from the base class From dd5f7552b361ace401d9f6f0b897843dcfb77cfe Mon Sep 17 00:00:00 2001 From: Alexander Grund Date: Sun, 20 Apr 2025 18:32:46 +0200 Subject: [PATCH 4/9] Remove MOCK_(CONST_)_METHOD_EXT detail macros --- include/turtle/detail/mock_impl.hpp | 3 --- include/turtle/mock.hpp | 22 ++++++++++++---------- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/include/turtle/detail/mock_impl.hpp b/include/turtle/detail/mock_impl.hpp index 81cc955..1d57c54 100644 --- a/include/turtle/detail/mock_impl.hpp +++ b/include/turtle/detail/mock_impl.hpp @@ -80,9 +80,6 @@ namespace mock { namespace detail { MOCK_PP_TUPLE_FOR_EACH(MOCK_METHOD_ITER, (name, arity, signature, identifier), qualifiers) \ MOCK_METHOD_HELPER(signature, identifier) -#define MOCK_CONST_METHOD_EXT(M, n, S, t) MOCK_METHOD_EXT(M, n, S, t, (const)) -#define MOCK_NON_CONST_METHOD_EXT(M, n, S, t) MOCK_METHOD_EXT(M, n, S, t, ()) - #define MOCK_FUNCTION_HELPER(signature, identifier, prefix) \ prefix mock::detail::function& identifier##_mock(mock::detail::context& context, \ boost::unit_test::const_string instance) \ diff --git a/include/turtle/mock.hpp b/include/turtle/mock.hpp index 4e543df..0fc7fbc 100644 --- a/include/turtle/mock.hpp +++ b/include/turtle/mock.hpp @@ -86,20 +86,22 @@ /// generates only the const version of the method /// The 'signature' can be omitted if it can be uniquely identified from the base class /// if 'identifier' is omitted it will default to 'name' -#define MOCK_CONST_METHOD(M, ...) \ - MOCK_CONST_METHOD_EXT(M, \ - BOOST_PP_VARIADIC_ELEM(0, __VA_ARGS__, ), \ - BOOST_PP_VARIADIC_ELEM(1, __VA_ARGS__, MOCK_SIGNATURE(M), ), \ - BOOST_PP_VARIADIC_ELEM(2, __VA_ARGS__, M, M, )) +#define MOCK_CONST_METHOD(M, ...) \ + MOCK_METHOD_EXT(M, \ + BOOST_PP_VARIADIC_ELEM(0, __VA_ARGS__, ), \ + BOOST_PP_VARIADIC_ELEM(1, __VA_ARGS__, MOCK_SIGNATURE(M), ), \ + BOOST_PP_VARIADIC_ELEM(2, __VA_ARGS__, M, M, ), \ + (const)) /// MOCK_NON_CONST_METHOD( [calling convention] name, arity[, signature[, identifier]] ) /// generates only the non-const version of the method /// The 'signature' can be omitted if it can be uniquely identified from the base class /// if 'identifier' is omitted it will default to 'name' -#define MOCK_NON_CONST_METHOD(M, ...) \ - MOCK_NON_CONST_METHOD_EXT(M, \ - BOOST_PP_VARIADIC_ELEM(0, __VA_ARGS__, ), \ - BOOST_PP_VARIADIC_ELEM(1, __VA_ARGS__, MOCK_SIGNATURE(M), ), \ - BOOST_PP_VARIADIC_ELEM(2, __VA_ARGS__, M, M, )) +#define MOCK_NON_CONST_METHOD(M, ...) \ + MOCK_METHOD_EXT(M, \ + BOOST_PP_VARIADIC_ELEM(0, __VA_ARGS__, ), \ + BOOST_PP_VARIADIC_ELEM(1, __VA_ARGS__, MOCK_SIGNATURE(M), ), \ + BOOST_PP_VARIADIC_ELEM(2, __VA_ARGS__, M, M, ), \ + ()) /// MOCK_FUNCTION( [calling convention] name, arity, signature[, identifier] ) /// if 'identifier' is omitted it will default to 'name' From a6a1f7f291654f934d5200c3cdb008a2583373c3 Mon Sep 17 00:00:00 2001 From: Alexander Grund Date: Sun, 20 Apr 2025 20:21:05 +0200 Subject: [PATCH 5/9] Make MOCK_METHOD_EXT public and allow passing specifiers to MOCK_METHOD --- include/turtle/detail/mock_impl.hpp | 2 +- include/turtle/mock.hpp | 49 +++++++++++++++++------------ test/test_mock.cpp | 28 +++++++++++++++++ 3 files changed, 58 insertions(+), 21 deletions(-) diff --git a/include/turtle/detail/mock_impl.hpp b/include/turtle/detail/mock_impl.hpp index 1d57c54..dda080f 100644 --- a/include/turtle/detail/mock_impl.hpp +++ b/include/turtle/detail/mock_impl.hpp @@ -75,7 +75,7 @@ namespace mock { namespace detail { BOOST_PP_TUPLE_ELEM(3, M_n_S_t), \ qualifier) -#define MOCK_METHOD_EXT(name, arity, signature, identifier, qualifiers) \ +#define MOCK_METHOD_EXT_I(name, arity, signature, identifier, qualifiers) \ static_assert(arity == mock::detail::function_arity_t::value, "Arity mismatch"); \ MOCK_PP_TUPLE_FOR_EACH(MOCK_METHOD_ITER, (name, arity, signature, identifier), qualifiers) \ MOCK_METHOD_HELPER(signature, identifier) diff --git a/include/turtle/mock.hpp b/include/turtle/mock.hpp index 0fc7fbc..ecb0041 100644 --- a/include/turtle/mock.hpp +++ b/include/turtle/mock.hpp @@ -72,36 +72,45 @@ } \ MOCK_METHOD_HELPER(void(), identifier) -/// MOCK_METHOD( [calling convention] name, arity[, signature[, identifier]] ) -/// generates both const and non-const methods +/// MOCK_METHOD( [calling convention] name, arity[, signature[, identifier, [, specifiers]]] ) +/// generates both const and non-const methods by default, but can be changed by passing a specifier tuple. /// The 'signature' can be omitted if it can be uniquely identified from the base class /// if 'identifier' is omitted it will default to 'name' -#define MOCK_METHOD(M, ...) \ - MOCK_METHOD_EXT(M, \ - BOOST_PP_VARIADIC_ELEM(0, __VA_ARGS__, ), \ - BOOST_PP_VARIADIC_ELEM(1, __VA_ARGS__, MOCK_SIGNATURE(M), ), \ - BOOST_PP_VARIADIC_ELEM(2, __VA_ARGS__, M, M, ), \ - (const, )) +/// 'specifiers' is a potentially empty, tuple of method specifiers, e.g. (&, &&) or (const override) +#define MOCK_METHOD(M, ...) \ + MOCK_METHOD_EXT_I(M, \ + BOOST_PP_VARIADIC_ELEM(0, __VA_ARGS__, ), \ + BOOST_PP_VARIADIC_ELEM(1, __VA_ARGS__, MOCK_SIGNATURE(M), ), \ + BOOST_PP_VARIADIC_ELEM(2, __VA_ARGS__, M, M, ), \ + BOOST_PP_VARIADIC_ELEM(3, __VA_ARGS__, (const, ), (const, ), (const, ), )) /// MOCK_CONST_METHOD( [calling convention] name, arity[, signature[, identifier]] ) /// generates only the const version of the method /// The 'signature' can be omitted if it can be uniquely identified from the base class /// if 'identifier' is omitted it will default to 'name' -#define MOCK_CONST_METHOD(M, ...) \ - MOCK_METHOD_EXT(M, \ - BOOST_PP_VARIADIC_ELEM(0, __VA_ARGS__, ), \ - BOOST_PP_VARIADIC_ELEM(1, __VA_ARGS__, MOCK_SIGNATURE(M), ), \ - BOOST_PP_VARIADIC_ELEM(2, __VA_ARGS__, M, M, ), \ - (const)) +#define MOCK_CONST_METHOD(M, ...) \ + MOCK_METHOD_EXT_I(M, \ + BOOST_PP_VARIADIC_ELEM(0, __VA_ARGS__, ), \ + BOOST_PP_VARIADIC_ELEM(1, __VA_ARGS__, MOCK_SIGNATURE(M), ), \ + BOOST_PP_VARIADIC_ELEM(2, __VA_ARGS__, M, M, ), \ + (const)) /// MOCK_NON_CONST_METHOD( [calling convention] name, arity[, signature[, identifier]] ) /// generates only the non-const version of the method /// The 'signature' can be omitted if it can be uniquely identified from the base class /// if 'identifier' is omitted it will default to 'name' -#define MOCK_NON_CONST_METHOD(M, ...) \ - MOCK_METHOD_EXT(M, \ - BOOST_PP_VARIADIC_ELEM(0, __VA_ARGS__, ), \ - BOOST_PP_VARIADIC_ELEM(1, __VA_ARGS__, MOCK_SIGNATURE(M), ), \ - BOOST_PP_VARIADIC_ELEM(2, __VA_ARGS__, M, M, ), \ - ()) +#define MOCK_NON_CONST_METHOD(M, ...) \ + MOCK_METHOD_EXT_I(M, \ + BOOST_PP_VARIADIC_ELEM(0, __VA_ARGS__, ), \ + BOOST_PP_VARIADIC_ELEM(1, __VA_ARGS__, MOCK_SIGNATURE(M), ), \ + BOOST_PP_VARIADIC_ELEM(2, __VA_ARGS__, M, M, ), \ + ()) + +/// MOCK_METHOD_EXT( [calling convention] name, arity, qualifiers [, signature, [identifier]] ) +#define MOCK_METHOD_EXT(M, arity, ...) \ + MOCK_METHOD_EXT_I(M, \ + arity, \ + BOOST_PP_VARIADIC_ELEM(1, __VA_ARGS__, MOCK_SIGNATURE(M), ), \ + BOOST_PP_VARIADIC_ELEM(2, __VA_ARGS__, M, M, ), \ + BOOST_PP_VARIADIC_ELEM(0, __VA_ARGS__, )) /// MOCK_FUNCTION( [calling convention] name, arity, signature[, identifier] ) /// if 'identifier' is omitted it will default to 'name' diff --git a/test/test_mock.cpp b/test/test_mock.cpp index a61c305..eb4b562 100644 --- a/test/test_mock.cpp +++ b/test/test_mock.cpp @@ -327,6 +327,15 @@ struct base virtual void m1() = 0; virtual void m10() const = 0; virtual void m11() = 0; + virtual void m12() noexcept = 0; + virtual void m13() && = 0; + virtual void m14() = 0; + virtual void m14(int, float) = 0; + virtual void m14(int, int) = 0; + virtual void m14(int) = 0; + virtual void m14(int) const noexcept = 0; + virtual void m15() & = 0; + virtual void m15() && = 0; }; MOCK_BASE_CLASS(variadic, base) @@ -340,9 +349,28 @@ MOCK_BASE_CLASS(variadic, base) MOCK_NON_CONST_METHOD(m11, 0) MOCK_NON_CONST_METHOD(m6, 0, void()) MOCK_NON_CONST_METHOD(m7, 0, void(), m7) + + MOCK_METHOD_EXT(m12, 0, (noexcept override)) + MOCK_METHOD_EXT(m13, 0, (&&override)) + // Overloaded method + MOCK_METHOD(m14, 0, void(), m14_empty) + MOCK_METHOD(m14, 2, void(int, float), m14_int_float, ()) + MOCK_METHOD(m14, 2, void(int, int), m14_int_int, (override)) + MOCK_METHOD(m14, 1, void(int), m14_int, (override, const noexcept override)) + MOCK_METHOD_EXT(m15, 0, (&override, &&override), void()) MOCK_STATIC_METHOD(m8, 0, void()) MOCK_STATIC_METHOD(m9, 0, void(), m9) }; +void instantiate_class() +{ + variadic inst; // If this compiles all pure virtual methods were mocked + const variadic& cinst = inst; + static_assert(noexcept(inst.m12()), "noexcept should be kept"); + static_assert(!noexcept(inst.m14()), "noexcept should not be set"); + static_assert(noexcept(cinst.m14(1)), "noexcept should be kept"); + static_assert(!noexcept(inst.m14(1)), "noexcept should not be set"); + static_assert(noexcept(std::declval().m14(1)), "noexcept should be kept"); +} template MOCK_BASE_CLASS(variadic_tpl, base) From 1367d827d54c0c2328ed9fe9a4836e9818678e9e Mon Sep 17 00:00:00 2001 From: Alexander Grund Date: Mon, 21 Apr 2025 19:24:24 +0200 Subject: [PATCH 6/9] Add support for C++17 noexcept as part of function type --- include/turtle/detail/signature.hpp | 49 +++++++++++++++++------------ 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/include/turtle/detail/signature.hpp b/include/turtle/detail/signature.hpp index cb4b3eb..d931427 100644 --- a/include/turtle/detail/signature.hpp +++ b/include/turtle/detail/signature.hpp @@ -1,6 +1,7 @@ // http://turtle.sourceforge.net // // Copyright Mathieu Champlon 2012 +// Copyright 2020-2025 Alexander Grund // // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at @@ -10,36 +11,44 @@ #define MOCK_SIGNATURE_HPP_INCLUDED #include "../config.hpp" +#include #include namespace mock { namespace detail { -#define MOCK_NOARG -#define MOCK_STRIP_FUNCTION_QUALIFIERS(cv, ref) \ - template \ - struct strip_function_qualifiers \ - { \ - using type = R(Args...); \ - }; \ - template \ - struct strip_function_qualifiers \ - { \ - using type = R(Args..., ...); \ +#define MOCK_STRIP_FUNCTION_QUALIFIERS(ne, cv, ref) \ + template \ + struct strip_function_qualifiers \ + { \ + using type = R(Args...); \ + }; \ + template \ + struct strip_function_qualifiers \ + { \ + using type = R(Args..., ...); \ }; -#define MOCK_STRIP_FUNCTION_QUALIFIERS_REF(cv) \ - MOCK_STRIP_FUNCTION_QUALIFIERS(cv, ) \ - MOCK_STRIP_FUNCTION_QUALIFIERS(cv, &) \ - MOCK_STRIP_FUNCTION_QUALIFIERS(cv, &&) +#define MOCK_STRIP_FUNCTION_QUALIFIERS_REF(ne, cv) \ + MOCK_STRIP_FUNCTION_QUALIFIERS(ne, cv, ) \ + MOCK_STRIP_FUNCTION_QUALIFIERS(ne, cv, &) \ + MOCK_STRIP_FUNCTION_QUALIFIERS(ne, cv, &&) +#define MOCK_STRIP_FUNCTION_QUALIFIERS_CV_REF(except_spec) \ + MOCK_STRIP_FUNCTION_QUALIFIERS_REF(except_spec, BOOST_PP_EMPTY()) \ + MOCK_STRIP_FUNCTION_QUALIFIERS_REF(except_spec, const) \ + MOCK_STRIP_FUNCTION_QUALIFIERS_REF(except_spec, volatile) \ + MOCK_STRIP_FUNCTION_QUALIFIERS_REF(except_spec, const volatile) template struct strip_function_qualifiers; - MOCK_STRIP_FUNCTION_QUALIFIERS_REF(MOCK_NOARG) - MOCK_STRIP_FUNCTION_QUALIFIERS_REF(const) - MOCK_STRIP_FUNCTION_QUALIFIERS_REF(volatile) - MOCK_STRIP_FUNCTION_QUALIFIERS_REF(const volatile) -#undef MOCK_NOARG + MOCK_STRIP_FUNCTION_QUALIFIERS_CV_REF(BOOST_PP_EMPTY()) + + // C++17 includes noexcept in the function type +#if MOCK_CXX_VERSION >= 201703L + MOCK_STRIP_FUNCTION_QUALIFIERS_CV_REF(noexcept) +#endif + #undef MOCK_STRIP_FUNCTION_QUALIFIERS #undef MOCK_STRIP_FUNCTION_QUALIFIERS_REF +#undef MOCK_STRIP_FUNCTION_QUALIFIERS_CV_REF template struct signature; From 2880b24116fd5d01dc77634a95399b0d7701053e Mon Sep 17 00:00:00 2001 From: Flamefire Date: Sun, 27 Apr 2025 16:38:23 +0200 Subject: [PATCH 7/9] CMake: Disable Clang warning `inconsistent-missing-override` --- test/CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 486fdb7..c6974ad 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -20,6 +20,9 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clang" if(TURTLE_CXX_UNUSED_FUNCTION) target_compile_options(TurtleTestMain INTERFACE -Wno-unused-function) endif() + if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") + target_compile_options(TurtleTestMain INTERFACE -Wno-inconsistent-missing-override) + endif() if(TURTLE_WERROR) check_cxx_compiler_flag(-Wdeprecated-declarations TURTLE_CXX_DEPRECATED_DECLARATIONS) if(TURTLE_CXX_DEPRECATED_DECLARATIONS) From 59d531282e97703352d606c46c502b09215e472b Mon Sep 17 00:00:00 2001 From: Flamefire Date: Sun, 27 Apr 2025 17:25:42 +0200 Subject: [PATCH 8/9] Support for GCC/Clang of MOCK_PP_FOR_EACH Some compilers support detection of an empty variadic macro element in which case `BOOST_PP_VARIADIC_SIZE` returns "0" so `BOOST_PP_OVERLOAD` calls the `macro_0` overload. An empty variadic element should be considered as a single empty value. So add that overload with this in mind. --- include/turtle/detail/pp_foreach.hpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/turtle/detail/pp_foreach.hpp b/include/turtle/detail/pp_foreach.hpp index c10e0dd..ee1529e 100644 --- a/include/turtle/detail/pp_foreach.hpp +++ b/include/turtle/detail/pp_foreach.hpp @@ -27,6 +27,9 @@ # define MOCK_PP_FOR_EACH(macro, data, ...) BOOST_PP_OVERLOAD(MOCK_PP_INVOKE_, __VA_ARGS__)(macro, data, __VA_ARGS__) #endif +// When the compiler supports detection of empty variadic element (e.g. Clang & GCC) Boost.PP returns a size of zero. +// However an empty variadic is a valid case: A single invocation with an empty element. +#define MOCK_PP_INVOKE_0(macro, data, x) macro(data, x) #define MOCK_PP_INVOKE_1(macro, data, x) macro(data, x) #define MOCK_PP_INVOKE_2(macro, data, x, ...) macro(data, x) MOCK_PP_INVOKE_1(macro, data, __VA_ARGS__) #define MOCK_PP_INVOKE_3(macro, data, x, ...) macro(data, x) MOCK_PP_INVOKE_2(macro, data, __VA_ARGS__) From 21567894a547ae87b290cb1b002eafd3135f398c Mon Sep 17 00:00:00 2001 From: Alexander Grund Date: Tue, 29 Apr 2025 15:29:32 +0200 Subject: [PATCH 9/9] Avoid unused variable warning in test --- test/test_mock.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/test/test_mock.cpp b/test/test_mock.cpp index eb4b562..6ca5c52 100644 --- a/test/test_mock.cpp +++ b/test/test_mock.cpp @@ -365,6 +365,7 @@ void instantiate_class() { variadic inst; // If this compiles all pure virtual methods were mocked const variadic& cinst = inst; + (void)cinst; // Avoid unused variable warning static_assert(noexcept(inst.m12()), "noexcept should be kept"); static_assert(!noexcept(inst.m14()), "noexcept should not be set"); static_assert(noexcept(cinst.m14(1)), "noexcept should be kept");