Support move-only and &&-qualified actions in WithArgs.
PiperOrigin-RevId: 444671005 Change-Id: I7df5f038caf17afb60d4fb35434ff0b656d4c954
This commit is contained in:
parent
e33c2b24ca
commit
c144d78f82
@ -1279,17 +1279,60 @@ class IgnoreResultAction {
|
|||||||
|
|
||||||
template <typename InnerAction, size_t... I>
|
template <typename InnerAction, size_t... I>
|
||||||
struct WithArgsAction {
|
struct WithArgsAction {
|
||||||
InnerAction action;
|
InnerAction inner_action;
|
||||||
|
|
||||||
// The inner action could be anything convertible to Action<X>.
|
// The signature of the function as seen by the inner action, given an out
|
||||||
// We use the conversion operator to detect the signature of the inner Action.
|
// action with the given result and argument types.
|
||||||
template <typename R, typename... Args>
|
template <typename R, typename... Args>
|
||||||
operator Action<R(Args...)>() const { // NOLINT
|
using InnerSignature =
|
||||||
using TupleType = std::tuple<Args...>;
|
R(typename std::tuple_element<I, std::tuple<Args...>>::type...);
|
||||||
Action<R(typename std::tuple_element<I, TupleType>::type...)> converted(
|
|
||||||
action);
|
|
||||||
|
|
||||||
return [converted](Args... args) -> R {
|
// Rather than a call operator, we must define conversion operators to
|
||||||
|
// particular action types. This is necessary for embedded actions like
|
||||||
|
// DoDefault(), which rely on an action conversion operators rather than
|
||||||
|
// providing a call operator because even with a particular set of arguments
|
||||||
|
// they don't have a fixed return type.
|
||||||
|
|
||||||
|
template <typename R, typename... Args,
|
||||||
|
typename std::enable_if<
|
||||||
|
std::is_convertible<
|
||||||
|
InnerAction,
|
||||||
|
// Unfortunately we can't use the InnerSignature alias here;
|
||||||
|
// MSVC complains about the I parameter pack not being
|
||||||
|
// expanded (error C3520) despite it being expanded in the
|
||||||
|
// type alias.
|
||||||
|
OnceAction<R(typename std::tuple_element<
|
||||||
|
I, std::tuple<Args...>>::type...)>>::value,
|
||||||
|
int>::type = 0>
|
||||||
|
operator OnceAction<R(Args...)>() && { // NOLINT
|
||||||
|
struct OA {
|
||||||
|
OnceAction<InnerSignature<R, Args...>> inner_action;
|
||||||
|
|
||||||
|
R operator()(Args&&... args) && {
|
||||||
|
return std::move(inner_action)
|
||||||
|
.Call(std::get<I>(
|
||||||
|
std::forward_as_tuple(std::forward<Args>(args)...))...);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return OA{std::move(inner_action)};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename R, typename... Args,
|
||||||
|
typename std::enable_if<
|
||||||
|
std::is_convertible<
|
||||||
|
const InnerAction&,
|
||||||
|
// Unfortunately we can't use the InnerSignature alias here;
|
||||||
|
// MSVC complains about the I parameter pack not being
|
||||||
|
// expanded (error C3520) despite it being expanded in the
|
||||||
|
// type alias.
|
||||||
|
Action<R(typename std::tuple_element<
|
||||||
|
I, std::tuple<Args...>>::type...)>>::value,
|
||||||
|
int>::type = 0>
|
||||||
|
operator Action<R(Args...)>() const { // NOLINT
|
||||||
|
Action<InnerSignature<R, Args...>> converted(inner_action);
|
||||||
|
|
||||||
|
return [converted](Args&&... args) -> R {
|
||||||
return converted.Perform(std::forward_as_tuple(
|
return converted.Perform(std::forward_as_tuple(
|
||||||
std::get<I>(std::forward_as_tuple(std::forward<Args>(args)...))...));
|
std::get<I>(std::forward_as_tuple(std::forward<Args>(args)...))...));
|
||||||
};
|
};
|
||||||
|
@ -1444,8 +1444,29 @@ TEST(WithArgsTest, ReturnReference) {
|
|||||||
|
|
||||||
TEST(WithArgsTest, InnerActionWithConversion) {
|
TEST(WithArgsTest, InnerActionWithConversion) {
|
||||||
Action<Derived*()> inner = [] { return nullptr; };
|
Action<Derived*()> inner = [] { return nullptr; };
|
||||||
Action<Base*(double)> a = testing::WithoutArgs(inner);
|
|
||||||
EXPECT_EQ(nullptr, a.Perform(std::make_tuple(1.1)));
|
MockFunction<Base*(double)> mock;
|
||||||
|
EXPECT_CALL(mock, Call)
|
||||||
|
.WillOnce(WithoutArgs(inner))
|
||||||
|
.WillRepeatedly(WithoutArgs(inner));
|
||||||
|
|
||||||
|
EXPECT_EQ(nullptr, mock.AsStdFunction()(1.1));
|
||||||
|
EXPECT_EQ(nullptr, mock.AsStdFunction()(1.1));
|
||||||
|
}
|
||||||
|
|
||||||
|
// It should be possible to use an &&-qualified inner action as long as the
|
||||||
|
// whole shebang is used as an rvalue with WillOnce.
|
||||||
|
TEST(WithArgsTest, RefQualifiedInnerAction) {
|
||||||
|
struct SomeAction {
|
||||||
|
int operator()(const int arg) && {
|
||||||
|
EXPECT_EQ(17, arg);
|
||||||
|
return 19;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
MockFunction<int(int, int)> mock;
|
||||||
|
EXPECT_CALL(mock, Call).WillOnce(WithArg<1>(SomeAction{}));
|
||||||
|
EXPECT_EQ(19, mock.AsStdFunction()(0, 17));
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !GTEST_OS_WINDOWS_MOBILE
|
#if !GTEST_OS_WINDOWS_MOBILE
|
||||||
|
Loading…
Reference in New Issue
Block a user