Googletest export

Change ACTION{,_Pn,_TEMPLATE} macros to build functors rather than ActionInterface<> subclasses, thus changing the Action<> wrappers they create to use the modernized (non-const) argument tuple type, allowing these macros to mutate their arguments.

Functor-based Action<>s deep-copy the implementing object, so have the functors use a shared_ptr to the non-trivial state of bound value parameters.  No longer specialize that shared state to the particular action signature, encoding that information instead only in the instantiation of the implementation function.

PiperOrigin-RevId: 341116208
This commit is contained in:
Abseil Team 2020-11-06 17:09:13 -05:00 committed by Mark Barolak
parent 0c400f67fc
commit fbef0711cf
3 changed files with 212 additions and 221 deletions

View File

@ -1480,65 +1480,69 @@ namespace internal {
// TYPE DIRECTLY. // TYPE DIRECTLY.
struct ExcessiveArg {}; struct ExcessiveArg {};
// A helper class needed for implementing the ACTION* macros. // Builds an implementation of an Action<> for some particular signature, using
template <typename Result, class Impl> // a class defined by an ACTION* macro.
class ActionHelper { template <typename F, typename Impl> struct ActionImpl;
public:
template <typename... Ts> template <typename Impl>
static Result Perform(Impl* impl, const std::tuple<Ts...>& args) { struct ImplBase {
static constexpr size_t kMaxArgs = sizeof...(Ts) <= 10 ? sizeof...(Ts) : 10; struct Holder {
return Apply(impl, args, MakeIndexSequence<kMaxArgs>{}, // Allows each copy of the Action<> to get to the Impl.
MakeIndexSequence<10 - kMaxArgs>{}); explicit operator const Impl&() const { return *ptr; }
std::shared_ptr<Impl> ptr;
};
using type = typename std::conditional<std::is_constructible<Impl>::value,
Impl, Holder>::type;
};
template <typename R, typename... Args, typename Impl>
struct ActionImpl<R(Args...), Impl> : ImplBase<Impl>::type {
using Base = typename ImplBase<Impl>::type;
using function_type = R(Args...);
using args_type = std::tuple<Args...>;
ActionImpl() = default; // Only defined if appropriate for Base.
explicit ActionImpl(std::shared_ptr<Impl> impl) : Base{std::move(impl)} { }
R operator()(Args&&... arg) const {
static constexpr size_t kMaxArgs =
sizeof...(Args) <= 10 ? sizeof...(Args) : 10;
return Apply(MakeIndexSequence<kMaxArgs>{},
MakeIndexSequence<10 - kMaxArgs>{},
args_type{std::forward<Args>(arg)...});
} }
private: template <std::size_t... arg_id, std::size_t... excess_id>
template <typename... Ts, std::size_t... tuple_ids, std::size_t... rest_ids> R Apply(IndexSequence<arg_id...>, IndexSequence<excess_id...>,
static Result Apply(Impl* impl, const std::tuple<Ts...>& args, const args_type& args) const {
IndexSequence<tuple_ids...>, IndexSequence<rest_ids...>) { // Impl need not be specific to the signature of action being implemented;
return impl->template gmock_PerformImpl< // only the implementing function body needs to have all of the specific
typename std::tuple_element<tuple_ids, std::tuple<Ts...>>::type...>( // types instantiated. Up to 10 of the args that are provided by the
args, std::get<tuple_ids>(args)..., // args_type get passed, followed by a dummy of unspecified type for the
((void)rest_ids, ExcessiveArg())...); // remainder up to 10 explicit args.
static const ExcessiveArg kExcessArg;
return static_cast<const Impl&>(*this).template gmock_PerformImpl<
/*function_type=*/function_type, /*return_type=*/R,
/*args_type=*/args_type,
/*argN_type=*/typename std::tuple_element<arg_id, args_type>::type...>(
/*args=*/args, std::get<arg_id>(args)...,
((void)excess_id, kExcessArg)...);
} }
}; };
// A helper base class needed for implementing the ACTION* macros. // Stores a default-constructed Impl as part of the Action<>'s
// Implements constructor and conversion operator for Action. // std::function<>. The Impl should be trivial to copy.
// template <typename F, typename Impl>
// Template specialization for parameterless Action. ::testing::Action<F> MakeAction() {
template <typename Derived> return ::testing::Action<F>(ActionImpl<F, Impl>());
class ActionImpl {
public:
ActionImpl() = default;
template <typename F>
operator ::testing::Action<F>() const { // NOLINT(runtime/explicit)
return ::testing::Action<F>(new typename Derived::template gmock_Impl<F>());
}
};
// Template specialization for parameterized Action.
template <template <typename...> class Derived, typename... Ts>
class ActionImpl<Derived<Ts...>> {
public:
explicit ActionImpl(Ts... params) : params_(std::forward<Ts>(params)...) {}
template <typename F>
operator ::testing::Action<F>() const { // NOLINT(runtime/explicit)
return Apply<F>(MakeIndexSequence<sizeof...(Ts)>{});
} }
private: // Stores just the one given instance of Impl.
template <typename F, std::size_t... tuple_ids> template <typename F, typename Impl>
::testing::Action<F> Apply(IndexSequence<tuple_ids...>) const { ::testing::Action<F> MakeAction(std::shared_ptr<Impl> impl) {
return ::testing::Action<F>(new return ::testing::Action<F>(ActionImpl<F, Impl>(std::move(impl)));
typename Derived<Ts...>::template gmock_Impl<F>(
std::get<tuple_ids>(params_)...));
} }
std::tuple<Ts...> params_;
};
#define GMOCK_INTERNAL_ARG_UNUSED(i, data, el) \ #define GMOCK_INTERNAL_ARG_UNUSED(i, data, el) \
, const arg##i##_type& arg##i GTEST_ATTRIBUTE_UNUSED_ , const arg##i##_type& arg##i GTEST_ATTRIBUTE_UNUSED_
#define GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_ \ #define GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_ \
@ -1582,30 +1586,28 @@ class ActionImpl<Derived<Ts...>> {
#define GMOCK_INTERNAL_ACTION(name, full_name, params) \ #define GMOCK_INTERNAL_ACTION(name, full_name, params) \
template <GMOCK_ACTION_TYPENAME_PARAMS_(params)> \ template <GMOCK_ACTION_TYPENAME_PARAMS_(params)> \
class full_name : public ::testing::internal::ActionImpl< \ class full_name { \
full_name<GMOCK_ACTION_TYPE_PARAMS_(params)>> { \
using base_type = ::testing::internal::ActionImpl<full_name>; \
\
public: \ public: \
using base_type::base_type; \ explicit full_name(GMOCK_ACTION_TYPE_GVALUE_PARAMS_(params)) \
: impl_(std::make_shared<gmock_Impl>( \
GMOCK_ACTION_GVALUE_PARAMS_(params))) { } \
full_name(const full_name&) = default; \
full_name(full_name&&) noexcept = default; \
template <typename F> \ template <typename F> \
class gmock_Impl : public ::testing::ActionInterface<F> { \ operator ::testing::Action<F>() const { \
return ::testing::internal::MakeAction<F>(impl_); \
} \
private: \
class gmock_Impl { \
public: \ public: \
typedef F function_type; \
typedef typename ::testing::internal::Function<F>::Result return_type; \
typedef \
typename ::testing::internal::Function<F>::ArgumentTuple args_type; \
explicit gmock_Impl(GMOCK_ACTION_TYPE_GVALUE_PARAMS_(params)) \ explicit gmock_Impl(GMOCK_ACTION_TYPE_GVALUE_PARAMS_(params)) \
: GMOCK_ACTION_INIT_PARAMS_(params) {} \ : GMOCK_ACTION_INIT_PARAMS_(params) {} \
return_type Perform(const args_type& args) override { \ template <typename function_type, typename return_type, \
return ::testing::internal::ActionHelper<return_type, \ typename args_type, GMOCK_ACTION_TEMPLATE_ARGS_NAMES_> \
gmock_Impl>::Perform(this, \
args); \
} \
template <GMOCK_ACTION_TEMPLATE_ARGS_NAMES_> \
return_type gmock_PerformImpl(GMOCK_ACTION_ARG_TYPES_AND_NAMES_) const; \ return_type gmock_PerformImpl(GMOCK_ACTION_ARG_TYPES_AND_NAMES_) const; \
GMOCK_ACTION_FIELD_PARAMS_(params) \ GMOCK_ACTION_FIELD_PARAMS_(params) \
}; \ }; \
std::shared_ptr<const gmock_Impl> impl_; \
}; \ }; \
template <GMOCK_ACTION_TYPENAME_PARAMS_(params)> \ template <GMOCK_ACTION_TYPENAME_PARAMS_(params)> \
inline full_name<GMOCK_ACTION_TYPE_PARAMS_(params)> name( \ inline full_name<GMOCK_ACTION_TYPE_PARAMS_(params)> name( \
@ -1614,47 +1616,36 @@ class ActionImpl<Derived<Ts...>> {
GMOCK_ACTION_GVALUE_PARAMS_(params)); \ GMOCK_ACTION_GVALUE_PARAMS_(params)); \
} \ } \
template <GMOCK_ACTION_TYPENAME_PARAMS_(params)> \ template <GMOCK_ACTION_TYPENAME_PARAMS_(params)> \
template <typename F> \ template <typename function_type, typename return_type, typename args_type, \
template <GMOCK_ACTION_TEMPLATE_ARGS_NAMES_> \ GMOCK_ACTION_TEMPLATE_ARGS_NAMES_> \
typename ::testing::internal::Function<F>::Result \ return_type full_name<GMOCK_ACTION_TYPE_PARAMS_(params)>::gmock_Impl:: \
full_name<GMOCK_ACTION_TYPE_PARAMS_(params)>::gmock_Impl< \ gmock_PerformImpl(GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const
F>::gmock_PerformImpl(GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) \
const
} // namespace internal } // namespace internal
// Similar to GMOCK_INTERNAL_ACTION, but no bound parameters are stored.
#define ACTION(name) \ #define ACTION(name) \
class name##Action : public ::testing::internal::ActionImpl<name##Action> { \ class name##Action { \
using base_type = ::testing::internal::ActionImpl<name##Action>; \
\
public: \ public: \
using base_type::base_type; \ explicit name##Action() noexcept {} \
name##Action() = default; \ name##Action(const name##Action&) noexcept {} \
/* Work around https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82134 */ \
name##Action(const name##Action&) { } \
template <typename F> \ template <typename F> \
class gmock_Impl : public ::testing::ActionInterface<F> { \ operator ::testing::Action<F>() const { \
public: \ return ::testing::internal::MakeAction<F, gmock_Impl>(); \
typedef F function_type; \
typedef typename ::testing::internal::Function<F>::Result return_type; \
typedef \
typename ::testing::internal::Function<F>::ArgumentTuple args_type; \
gmock_Impl() {} \
return_type Perform(const args_type& args) override { \
return ::testing::internal::ActionHelper<return_type, \
gmock_Impl>::Perform(this, \
args); \
} \ } \
template <GMOCK_ACTION_TEMPLATE_ARGS_NAMES_> \ private: \
class gmock_Impl { \
public: \
template <typename function_type, typename return_type, \
typename args_type, GMOCK_ACTION_TEMPLATE_ARGS_NAMES_> \
return_type gmock_PerformImpl(GMOCK_ACTION_ARG_TYPES_AND_NAMES_) const; \ return_type gmock_PerformImpl(GMOCK_ACTION_ARG_TYPES_AND_NAMES_) const; \
}; \ }; \
}; \ }; \
inline name##Action name() GTEST_MUST_USE_RESULT_; \ inline name##Action name() GTEST_MUST_USE_RESULT_; \
inline name##Action name() { return name##Action(); } \ inline name##Action name() { return name##Action(); } \
template <typename F> \ template <typename function_type, typename return_type, typename args_type, \
template <GMOCK_ACTION_TEMPLATE_ARGS_NAMES_> \ GMOCK_ACTION_TEMPLATE_ARGS_NAMES_> \
typename ::testing::internal::Function<F>::Result \ return_type name##Action::gmock_Impl::gmock_PerformImpl( \
name##Action::gmock_Impl<F>::gmock_PerformImpl( \
GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const
#define ACTION_P(name, ...) \ #define ACTION_P(name, ...) \

View File

@ -296,7 +296,7 @@
// Defines the copy constructor // Defines the copy constructor
#define GMOCK_INTERNAL_DEFN_COPY_AND_0_VALUE_PARAMS() \ #define GMOCK_INTERNAL_DEFN_COPY_AND_0_VALUE_PARAMS() \
{} // Work around https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82134 noexcept {} // Avoid https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82134
#define GMOCK_INTERNAL_DEFN_COPY_AND_1_VALUE_PARAMS(...) = default; #define GMOCK_INTERNAL_DEFN_COPY_AND_1_VALUE_PARAMS(...) = default;
#define GMOCK_INTERNAL_DEFN_COPY_AND_2_VALUE_PARAMS(...) = default; #define GMOCK_INTERNAL_DEFN_COPY_AND_2_VALUE_PARAMS(...) = default;
#define GMOCK_INTERNAL_DEFN_COPY_AND_3_VALUE_PARAMS(...) = default; #define GMOCK_INTERNAL_DEFN_COPY_AND_3_VALUE_PARAMS(...) = default;
@ -434,34 +434,36 @@
GMOCK_INTERNAL_DECL_TYPE_##value_params> \ GMOCK_INTERNAL_DECL_TYPE_##value_params> \
class GMOCK_ACTION_CLASS_(name, value_params) { \ class GMOCK_ACTION_CLASS_(name, value_params) { \
public: \ public: \
explicit GMOCK_ACTION_CLASS_(name, value_params)\ explicit GMOCK_ACTION_CLASS_(name, value_params)( \
GMOCK_INTERNAL_INIT_##value_params {}\ GMOCK_INTERNAL_DECL_##value_params) \
GMOCK_PP_IF(GMOCK_PP_IS_EMPTY(GMOCK_INTERNAL_COUNT_##value_params), \
= default; , \
: impl_(std::make_shared<gmock_Impl>( \
GMOCK_INTERNAL_LIST_##value_params)) { }) \
GMOCK_ACTION_CLASS_(name, value_params)( \ GMOCK_ACTION_CLASS_(name, value_params)( \
const GMOCK_ACTION_CLASS_(name, value_params)<\ const GMOCK_ACTION_CLASS_(name, value_params)&) \
GMOCK_INTERNAL_LIST_##template_params\ GMOCK_INTERNAL_DEFN_COPY_##value_params \
GMOCK_INTERNAL_LIST_TYPE_##value_params>&)\ GMOCK_ACTION_CLASS_(name, value_params)( \
GMOCK_ACTION_CLASS_(name, value_params)&&) \
GMOCK_INTERNAL_DEFN_COPY_##value_params \ GMOCK_INTERNAL_DEFN_COPY_##value_params \
template <typename F> \ template <typename F> \
class gmock_Impl : public ::testing::ActionInterface<F> {\ operator ::testing::Action<F>() const { \
public:\ return GMOCK_PP_IF( \
typedef F function_type;\ GMOCK_PP_IS_EMPTY(GMOCK_INTERNAL_COUNT_##value_params), \
typedef typename ::testing::internal::Function<F>::Result return_type;\ (::testing::internal::MakeAction<F, gmock_Impl>()), \
typedef typename ::testing::internal::Function<F>::ArgumentTuple\ (::testing::internal::MakeAction<F>(impl_))); \
args_type;\
explicit gmock_Impl GMOCK_INTERNAL_INIT_##value_params {}\
return_type Perform(const args_type& args) override {\
return ::testing::internal::ActionHelper<return_type, gmock_Impl>::\
Perform(this, args);\
} \ } \
template <GMOCK_ACTION_TEMPLATE_ARGS_NAMES_>\ private: \
class gmock_Impl { \
public: \
explicit gmock_Impl GMOCK_INTERNAL_INIT_##value_params {} \
template <typename function_type, typename return_type, \
typename args_type, GMOCK_ACTION_TEMPLATE_ARGS_NAMES_> \
return_type gmock_PerformImpl(GMOCK_ACTION_ARG_TYPES_AND_NAMES_) const; \ return_type gmock_PerformImpl(GMOCK_ACTION_ARG_TYPES_AND_NAMES_) const; \
GMOCK_INTERNAL_DEFN_##value_params \ GMOCK_INTERNAL_DEFN_##value_params \
}; \ }; \
template <typename F> operator ::testing::Action<F>() const {\ GMOCK_PP_IF(GMOCK_PP_IS_EMPTY(GMOCK_INTERNAL_COUNT_##value_params), \
return ::testing::Action<F>(\ , std::shared_ptr<const gmock_Impl> impl_;) \
new gmock_Impl<F>(GMOCK_INTERNAL_LIST_##value_params));\
}\
GMOCK_INTERNAL_DEFN_##value_params\
}; \ }; \
template <GMOCK_INTERNAL_DECL_##template_params \ template <GMOCK_INTERNAL_DECL_##template_params \
GMOCK_INTERNAL_DECL_TYPE_##value_params> \ GMOCK_INTERNAL_DECL_TYPE_##value_params> \
@ -482,13 +484,11 @@
} \ } \
template <GMOCK_INTERNAL_DECL_##template_params \ template <GMOCK_INTERNAL_DECL_##template_params \
GMOCK_INTERNAL_DECL_TYPE_##value_params> \ GMOCK_INTERNAL_DECL_TYPE_##value_params> \
template <typename F>\ template <typename function_type, typename return_type, typename args_type, \
template <GMOCK_ACTION_TEMPLATE_ARGS_NAMES_>\ GMOCK_ACTION_TEMPLATE_ARGS_NAMES_> \
typename ::testing::internal::Function<F>::Result\ return_type GMOCK_ACTION_CLASS_(name, value_params)< \
GMOCK_ACTION_CLASS_(name, value_params)<\
GMOCK_INTERNAL_LIST_##template_params \ GMOCK_INTERNAL_LIST_##template_params \
GMOCK_INTERNAL_LIST_TYPE_##value_params>::gmock_Impl<F>::\ GMOCK_INTERNAL_LIST_TYPE_##value_params>::gmock_Impl::gmock_PerformImpl( \
gmock_PerformImpl(\
GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const
namespace testing { namespace testing {

View File

@ -181,7 +181,7 @@ $range j 0..i-1
$for i [[ $for i [[
#define GMOCK_INTERNAL_DEFN_COPY_AND_$i[[]]_VALUE_PARAMS$if i == 0[[() \ #define GMOCK_INTERNAL_DEFN_COPY_AND_$i[[]]_VALUE_PARAMS$if i == 0[[() \
{} // Work around https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82134 noexcept {} // Avoid https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82134
]] $else [[(...) = default;]] ]] $else [[(...) = default;]]
@ -248,34 +248,36 @@ $range k 0..n-1
GMOCK_INTERNAL_DECL_TYPE_##value_params> \ GMOCK_INTERNAL_DECL_TYPE_##value_params> \
class GMOCK_ACTION_CLASS_(name, value_params) { \ class GMOCK_ACTION_CLASS_(name, value_params) { \
public: \ public: \
explicit GMOCK_ACTION_CLASS_(name, value_params)\ explicit GMOCK_ACTION_CLASS_(name, value_params)( \
GMOCK_INTERNAL_INIT_##value_params {}\ GMOCK_INTERNAL_DECL_##value_params) \
GMOCK_PP_IF(GMOCK_PP_IS_EMPTY(GMOCK_INTERNAL_COUNT_##value_params), \
= default; , \
: impl_(std::make_shared<gmock_Impl>( \
GMOCK_INTERNAL_LIST_##value_params)) { }) \
GMOCK_ACTION_CLASS_(name, value_params)( \ GMOCK_ACTION_CLASS_(name, value_params)( \
const GMOCK_ACTION_CLASS_(name, value_params)<\ const GMOCK_ACTION_CLASS_(name, value_params)&) \
GMOCK_INTERNAL_LIST_##template_params\ GMOCK_INTERNAL_DEFN_COPY_##value_params \
GMOCK_INTERNAL_LIST_TYPE_##value_params>&)\ GMOCK_ACTION_CLASS_(name, value_params)( \
GMOCK_ACTION_CLASS_(name, value_params)&&) \
GMOCK_INTERNAL_DEFN_COPY_##value_params \ GMOCK_INTERNAL_DEFN_COPY_##value_params \
template <typename F> \ template <typename F> \
class gmock_Impl : public ::testing::ActionInterface<F> {\ operator ::testing::Action<F>() const { \
public:\ return GMOCK_PP_IF( \
typedef F function_type;\ GMOCK_PP_IS_EMPTY(GMOCK_INTERNAL_COUNT_##value_params), \
typedef typename ::testing::internal::Function<F>::Result return_type;\ (::testing::internal::MakeAction<F, gmock_Impl>()), \
typedef typename ::testing::internal::Function<F>::ArgumentTuple\ (::testing::internal::MakeAction<F>(impl_))); \
args_type;\
explicit gmock_Impl GMOCK_INTERNAL_INIT_##value_params {}\
return_type Perform(const args_type& args) override {\
return ::testing::internal::ActionHelper<return_type, gmock_Impl>::\
Perform(this, args);\
} \ } \
template <GMOCK_ACTION_TEMPLATE_ARGS_NAMES_>\ private: \
class gmock_Impl { \
public: \
explicit gmock_Impl GMOCK_INTERNAL_INIT_##value_params {} \
template <typename function_type, typename return_type, \
typename args_type, GMOCK_ACTION_TEMPLATE_ARGS_NAMES_> \
return_type gmock_PerformImpl(GMOCK_ACTION_ARG_TYPES_AND_NAMES_) const; \ return_type gmock_PerformImpl(GMOCK_ACTION_ARG_TYPES_AND_NAMES_) const; \
GMOCK_INTERNAL_DEFN_##value_params \ GMOCK_INTERNAL_DEFN_##value_params \
}; \ }; \
template <typename F> operator ::testing::Action<F>() const {\ GMOCK_PP_IF(GMOCK_PP_IS_EMPTY(GMOCK_INTERNAL_COUNT_##value_params), \
return ::testing::Action<F>(\ , std::shared_ptr<const gmock_Impl> impl_;) \
new gmock_Impl<F>(GMOCK_INTERNAL_LIST_##value_params));\
}\
GMOCK_INTERNAL_DEFN_##value_params\
}; \ }; \
template <GMOCK_INTERNAL_DECL_##template_params \ template <GMOCK_INTERNAL_DECL_##template_params \
GMOCK_INTERNAL_DECL_TYPE_##value_params> \ GMOCK_INTERNAL_DECL_TYPE_##value_params> \
@ -296,13 +298,11 @@ $range k 0..n-1
} \ } \
template <GMOCK_INTERNAL_DECL_##template_params \ template <GMOCK_INTERNAL_DECL_##template_params \
GMOCK_INTERNAL_DECL_TYPE_##value_params> \ GMOCK_INTERNAL_DECL_TYPE_##value_params> \
template <typename F>\ template <typename function_type, typename return_type, typename args_type, \
template <GMOCK_ACTION_TEMPLATE_ARGS_NAMES_>\ GMOCK_ACTION_TEMPLATE_ARGS_NAMES_> \
typename ::testing::internal::Function<F>::Result\ return_type GMOCK_ACTION_CLASS_(name, value_params)< \
GMOCK_ACTION_CLASS_(name, value_params)<\
GMOCK_INTERNAL_LIST_##template_params \ GMOCK_INTERNAL_LIST_##template_params \
GMOCK_INTERNAL_LIST_TYPE_##value_params>::gmock_Impl<F>::\ GMOCK_INTERNAL_LIST_TYPE_##value_params>::gmock_Impl::gmock_PerformImpl( \
gmock_PerformImpl(\
GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const
namespace testing { namespace testing {