Skip to content

Commit b9862b2

Browse files
authored
Merge pull request #356 from eranpeer/make-return-with-spe-works-again-and-bonuses
Fix lot of stuffs with .*Return*.
2 parents 1eaa63c + cece5b1 commit b9862b2

File tree

4 files changed

+995
-53
lines changed

4 files changed

+995
-53
lines changed

include/fakeit/StubbingProgress.hpp

Lines changed: 95 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -71,40 +71,62 @@ namespace fakeit {
7171
return Do([&r](const typename fakeit::test_arg<arglist>::type...) -> R { return r; });
7272
}
7373

74-
template <typename U = R>
74+
// The std::enable_if is only there to disambiguate with the deprecated version of .Return<type>(val), and
75+
// can be removed once that deprecated version is removed.
76+
template <typename U = R, typename std::enable_if<std::is_reference<U>::value, bool>::type = true>
7577
MethodStubbingProgress<R, arglist...>& Return(fk_remove_cvref_t<R>&& r) {
76-
static_assert(sizeof(U) != sizeof(U), "Return() cannot take an rvalue references for functions returning a reference because it would make it dangling, use ReturnCapture() instead.");
78+
static_assert(sizeof(U) != sizeof(U), "Return() cannot take an rvalue references for functions returning a reference because it would make it dangling, use ReturnValCapt() instead.");
7779
return Return(r); // Only written to silence warning about not returning from a non-void function, but will never be executed.
7880
}
7981

8082
void AlwaysReturn(const R &r) {
81-
return AlwaysDo([&r](const typename fakeit::test_arg<arglist>::type...) -> R { return r; });
83+
AlwaysDo([&r](const typename fakeit::test_arg<arglist>::type...) -> R { return r; });
8284
}
8385

84-
template <typename U = R>
86+
// The std::enable_if is only there to disambiguate with the deprecated version of .AlwaysReturn<type>(val), and
87+
// can be removed once that deprecated version is removed.
88+
template <typename U = R, typename std::enable_if<std::is_reference<U>::value, bool>::type = true>
8589
void AlwaysReturn(fk_remove_cvref_t<R>&&) {
86-
static_assert(sizeof(U) != sizeof(U), "AlwaysReturn() cannot take an rvalue references for functions returning a reference because it would make it dangling, use AlwaysReturnCapture() instead.");
90+
static_assert(sizeof(U) != sizeof(U), "AlwaysReturn() cannot take an rvalue references for functions returning a reference because it would make it dangling, use AlwaysReturnValCapt() instead.");
8791
}
8892

89-
template<typename T>
90-
MethodStubbingProgress<R, arglist...>& ReturnCapture(T&& r) {
91-
static_assert(std::is_constructible<fk_remove_cvref_t<R>&, fk_remove_cvref_t<T>&>::value,
92-
"The type captured by ReturnCapture() (named T) must be compatible with the return type of the function (named R), i.e. T t{...}; R& r = t; must compile without creating temporaries.");
93-
auto store = std::make_shared<fk_remove_cvref_t<T>>(std::forward<T>(r));
93+
template<typename T = R>
94+
MethodStubbingProgress<R, arglist...>& ReturnValCapt(T&& r) {
95+
// If a ref to T can be cast to a ref to R, then store T.
96+
// Otherwise, create an object R constructed from the received T and store it.
97+
using StoredType = typename std::conditional<
98+
std::is_constructible<fk_remove_cvref_t<R>&, fk_remove_cvref_t<T>&>::value,
99+
fk_remove_cvref_t<T>,
100+
fk_remove_cvref_t<R>>::type;
101+
auto store = std::make_shared<StoredType>(std::forward<T>(r));
94102
return Do([store](const typename fakeit::test_arg<arglist>::type...) mutable -> R {
95103
return std::forward<R>(*store);
96104
});
97105
}
98106

99-
template<typename T>
100-
void AlwaysReturnCapture(T&& r) {
101-
static_assert(std::is_constructible<fk_remove_cvref_t<R>&, fk_remove_cvref_t<T>&>::value,
102-
"The type captured by AlwaysReturnCapture() (named T) must be compatible with the return type of the function (named R), i.e. T t{...}; R& r = t; must compile without creating temporaries.");
103-
auto store = std::make_shared<fk_remove_cvref_t<T>>(std::forward<T>(r));
104-
return AlwaysDo([store](const typename fakeit::test_arg<arglist>::type...) mutable -> R {
107+
template<typename T = R>
108+
void AlwaysReturnValCapt(T&& r) {
109+
// If a ref to T can be cast to a ref to R, then store T.
110+
// Otherwise, create an object R constructed from the received T and store it.
111+
using StoredType = typename std::conditional<
112+
std::is_constructible<fk_remove_cvref_t<R>&, fk_remove_cvref_t<T>&>::value,
113+
fk_remove_cvref_t<T>,
114+
fk_remove_cvref_t<R>>::type;
115+
auto store = std::make_shared<StoredType>(std::forward<T>(r));
116+
AlwaysDo([store](const typename fakeit::test_arg<arglist>::type...) mutable -> R {
105117
return std::forward<R>(*store);
106118
});
107119
}
120+
121+
template<typename T>
122+
MethodStubbingProgress<R, arglist...>& ReturnRefCapt(T&& r) {
123+
return Return(std::forward<T>(r));
124+
}
125+
126+
template<typename T>
127+
void AlwaysReturnRefCapt(T&& r) {
128+
AlwaysReturn(std::forward<T>(r));
129+
}
108130
};
109131

110132
// If R is not a reference.
@@ -125,7 +147,31 @@ namespace fakeit {
125147
}
126148

127149
void AlwaysReturn(const R &r) {
128-
return AlwaysDo([r](const typename fakeit::test_arg<arglist>::type...) -> R { return r; });
150+
AlwaysDo([r](const typename fakeit::test_arg<arglist>::type...) -> R { return r; });
151+
}
152+
153+
MethodStubbingProgress<R, arglist...>& ReturnValCapt(const R& r) {
154+
return Return(r);
155+
}
156+
157+
MethodStubbingProgress<R, arglist...>& ReturnValCapt(R&& r) {
158+
return Return(std::move(r));
159+
}
160+
161+
void AlwaysReturnValCapt(const R &r) {
162+
AlwaysReturn(r);
163+
}
164+
165+
template<typename T>
166+
MethodStubbingProgress<R, arglist...>& ReturnRefCapt(T&& r) {
167+
static_assert(std::is_lvalue_reference<T>::value, "ReturnRefCapt() cannot take an rvalue references because it would make it dangling, use ReturnValCapt() instead.");
168+
return Do([&r](const typename fakeit::test_arg<arglist>::type...) -> R { return r; });
169+
}
170+
171+
template<typename T>
172+
void AlwaysReturnRefCapt(T&& r) {
173+
static_assert(std::is_lvalue_reference<T>::value, "AlwaysReturnRefCapt() cannot take an rvalue references because it would make it dangling, use AlwaysReturnValCapt() instead.");
174+
AlwaysDo([&r](const typename fakeit::test_arg<arglist>::type...) -> R { return r; });
129175
}
130176
};
131177

@@ -146,6 +192,38 @@ namespace fakeit {
146192
using helper::BasicReturnImplHelper<R, arglist...>::Return;
147193
using helper::BasicReturnImplHelper<R, arglist...>::AlwaysReturn;
148194

195+
// DEPRECATED: This should ideally be removed, it allows writing .Return<std::string>("ok") when a function
196+
// returns "const std::string&" (for example) to have the same behavior has .ReturnValCapt("ok"). But it is prone
197+
// to errors (because you have to specify the type). .ReturnValCapt("ok") is superior and should be used instead.
198+
template<typename TypeUsedToForceCapture, typename RealType, typename std::enable_if<!std::is_reference<TypeUsedToForceCapture>::value, bool>::type = true>
199+
MethodStubbingProgress<R, arglist...>& Return(RealType&& ret) {
200+
return this->ReturnValCapt(TypeUsedToForceCapture(std::forward<RealType>(ret)));
201+
}
202+
203+
// DEPRECATED: This should ideally be removed, it allows writing .AlwaysReturn<std::string>("ok") when a function
204+
// returns "const std::string&" (for example) to have the same behavior has .AlwaysReturnValCapt("ok"). But it is prone
205+
// to errors (because you have to specify the type). .AlwaysReturnValCapt("ok") is superior and should be used instead.
206+
template<typename TypeUsedToForceCapture, typename RealType, typename std::enable_if<!std::is_reference<TypeUsedToForceCapture>::value, bool>::type = true>
207+
void AlwaysReturn(RealType&& ret) {
208+
this->AlwaysReturnValCapt(TypeUsedToForceCapture(std::forward<RealType>(ret)));
209+
}
210+
211+
// DEPRECATED: This should ideally be removed, it allows writing .Return<std::string&>(str) when a function
212+
// returns "std::string" (for example) to have the same behavior has .ReturnRefCapt(str). But it is prone
213+
// to errors (because you have to specify the type). .ReturnRefCapt(str) is superior and should be used instead.
214+
template<typename TypeUsedToForceCapture, typename RealType, typename std::enable_if<std::is_reference<TypeUsedToForceCapture>::value, bool>::type = true>
215+
MethodStubbingProgress<R, arglist...>& Return(RealType&& ret) {
216+
return this->ReturnRefCapt(std::forward<RealType>(ret));
217+
}
218+
219+
// DEPRECATED: This should ideally be removed, it allows writing .AlwaysReturn<std::string&>(str) when a function
220+
// returns "std::string" (for example) to have the same behavior has .AlwaysReturnRefCapt(str). But it is prone
221+
// to errors (because you have to specify the type). .AlwaysReturnRefCapt(str) is superior and should be used instead.
222+
template<typename TypeUsedToForceCapture, typename RealType, typename std::enable_if<std::is_reference<TypeUsedToForceCapture>::value, bool>::type = true>
223+
void AlwaysReturn(RealType&& ret) {
224+
this->AlwaysReturnRefCapt(std::forward<RealType>(ret));
225+
}
226+
149227
MethodStubbingProgress<R, arglist...> &
150228
Return(const Quantifier<R> &q) {
151229
const R &value = q.value;

tests/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ add_executable(FakeIt_tests
1818
overloadded_methods_tests.cpp
1919
referece_types_tests.cpp
2020
remove_const_volatile_tests.cpp
21+
return_template_specialization.cpp
2122
rvalue_arguments_tests.cpp
2223
sequence_verification_tests.cpp
2324
spying_tests.cpp

tests/referece_types_tests.cpp

Lines changed: 36 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -240,10 +240,10 @@ struct ReferenceTypesTests: tpunit::TestFixture {
240240
ConcreteType concrete{"myConcreteType"};
241241

242242
// explicit copy here
243-
When(Method(mock, returnStringByConstRef)).ReturnCapture(aString);
244-
When(Method(mock, returnStringByRValRef)).ReturnCapture(bString);
245-
When(Method(mock, returnIntByRef)).ReturnCapture(num);
246-
When(Method(mock, returnAbstractTypeByRef)).ReturnCapture(concrete);
243+
When(Method(mock, returnStringByConstRef)).ReturnValCapt(aString);
244+
When(Method(mock, returnStringByRValRef)).ReturnValCapt(bString);
245+
When(Method(mock, returnIntByRef)).ReturnValCapt(num);
246+
When(Method(mock, returnAbstractTypeByRef)).ReturnValCapt(concrete);
247247

248248
// modify now so know whether or not is was copied
249249
aString = "modified";
@@ -276,13 +276,13 @@ struct ReferenceTypesTests: tpunit::TestFixture {
276276
ConcreteType concrete{"myConcreteType"};
277277

278278
// explicit move here
279-
When(Method(mock, returnStringByConstRef)).ReturnCapture(std::move(aString));
280-
When(Method(mock, returnStringByRef)).ReturnCapture(std::move(bString));
281-
When(Method(mock, returnStringByRValRef)).ReturnCapture(std::move(cString));
282-
When(Method(mock, returnMoveOnlyByConstRef)).ReturnCapture(std::move(aPtrString));
283-
When(Method(mock, returnMoveOnlyByRef)).ReturnCapture(std::move(bPtrString));
284-
When(Method(mock, returnMoveOnlyByRValRef)).ReturnCapture(std::move(cPtrString));
285-
When(Method(mock, returnAbstractTypeByRef)).ReturnCapture(std::move(concrete));
279+
When(Method(mock, returnStringByConstRef)).ReturnValCapt(std::move(aString));
280+
When(Method(mock, returnStringByRef)).ReturnValCapt(std::move(bString));
281+
When(Method(mock, returnStringByRValRef)).ReturnValCapt(std::move(cString));
282+
When(Method(mock, returnMoveOnlyByConstRef)).ReturnValCapt(std::move(aPtrString));
283+
When(Method(mock, returnMoveOnlyByRef)).ReturnValCapt(std::move(bPtrString));
284+
When(Method(mock, returnMoveOnlyByRValRef)).ReturnValCapt(std::move(cPtrString));
285+
When(Method(mock, returnAbstractTypeByRef)).ReturnValCapt(std::move(concrete));
286286

287287
// Verify objects were moved.
288288
EXPECT_TRUE(aString.empty());
@@ -311,13 +311,13 @@ struct ReferenceTypesTests: tpunit::TestFixture {
311311
Mock<ReferenceInterface> mock;
312312

313313
{
314-
When(Method(mock, returnStringByConstRef)).ReturnCapture(std::string{"aString"});
315-
When(Method(mock, returnStringByRef)).ReturnCapture(std::string{"bString"});
316-
When(Method(mock, returnStringByRValRef)).ReturnCapture(std::string{"cString"});
317-
When(Method(mock, returnMoveOnlyByConstRef)).ReturnCapture(std::unique_ptr<std::string>(new std::string{"aPtrString"}));
318-
When(Method(mock, returnMoveOnlyByRef)).ReturnCapture(std::unique_ptr<std::string>(new std::string{"bPtrString"}));
319-
When(Method(mock, returnMoveOnlyByRValRef)).ReturnCapture(std::unique_ptr<std::string>(new std::string{"cPtrString"}));
320-
When(Method(mock, returnAbstractTypeByRef)).ReturnCapture(ConcreteType{"myConcreteType"});
314+
When(Method(mock, returnStringByConstRef)).ReturnValCapt(std::string{"aString"});
315+
When(Method(mock, returnStringByRef)).ReturnValCapt(std::string{"bString"});
316+
When(Method(mock, returnStringByRValRef)).ReturnValCapt(std::string{"cString"});
317+
When(Method(mock, returnMoveOnlyByConstRef)).ReturnValCapt(std::unique_ptr<std::string>(new std::string{"aPtrString"}));
318+
When(Method(mock, returnMoveOnlyByRef)).ReturnValCapt(std::unique_ptr<std::string>(new std::string{"bPtrString"}));
319+
When(Method(mock, returnMoveOnlyByRValRef)).ReturnValCapt(std::unique_ptr<std::string>(new std::string{"cPtrString"}));
320+
When(Method(mock, returnAbstractTypeByRef)).ReturnValCapt(ConcreteType{"myConcreteType"});
321321
}
322322

323323
ReferenceInterface& i = mock.get();
@@ -344,10 +344,10 @@ struct ReferenceTypesTests: tpunit::TestFixture {
344344
ConcreteType concrete{"myConcreteType"};
345345

346346
// explicit copy here
347-
When(Method(mock, returnStringByConstRef)).AlwaysReturnCapture(aString);
348-
When(Method(mock, returnStringByRValRef)).AlwaysReturnCapture(bString);
349-
When(Method(mock, returnIntByRef)).AlwaysReturnCapture(num);
350-
When(Method(mock, returnAbstractTypeByRef)).AlwaysReturnCapture(concrete);
347+
When(Method(mock, returnStringByConstRef)).AlwaysReturnValCapt(aString);
348+
When(Method(mock, returnStringByRValRef)).AlwaysReturnValCapt(bString);
349+
When(Method(mock, returnIntByRef)).AlwaysReturnValCapt(num);
350+
When(Method(mock, returnAbstractTypeByRef)).AlwaysReturnValCapt(concrete);
351351

352352
// modify now so know whether or not is was copied
353353
aString = "modified";
@@ -395,13 +395,13 @@ struct ReferenceTypesTests: tpunit::TestFixture {
395395
ConcreteType concrete{"myConcreteType"};
396396

397397
// explicit move here
398-
When(Method(mock, returnStringByConstRef)).AlwaysReturnCapture(std::move(aString));
399-
When(Method(mock, returnStringByRef)).AlwaysReturnCapture(std::move(bString));
400-
When(Method(mock, returnStringByRValRef)).AlwaysReturnCapture(std::move(cString));
401-
When(Method(mock, returnMoveOnlyByConstRef)).AlwaysReturnCapture(std::move(aPtrString));
402-
When(Method(mock, returnMoveOnlyByRef)).AlwaysReturnCapture(std::move(bPtrString));
403-
When(Method(mock, returnMoveOnlyByRValRef)).AlwaysReturnCapture(std::move(cPtrString));
404-
When(Method(mock, returnAbstractTypeByRef)).AlwaysReturnCapture(std::move(concrete));
398+
When(Method(mock, returnStringByConstRef)).AlwaysReturnValCapt(std::move(aString));
399+
When(Method(mock, returnStringByRef)).AlwaysReturnValCapt(std::move(bString));
400+
When(Method(mock, returnStringByRValRef)).AlwaysReturnValCapt(std::move(cString));
401+
When(Method(mock, returnMoveOnlyByConstRef)).AlwaysReturnValCapt(std::move(aPtrString));
402+
When(Method(mock, returnMoveOnlyByRef)).AlwaysReturnValCapt(std::move(bPtrString));
403+
When(Method(mock, returnMoveOnlyByRValRef)).AlwaysReturnValCapt(std::move(cPtrString));
404+
When(Method(mock, returnAbstractTypeByRef)).AlwaysReturnValCapt(std::move(concrete));
405405

406406
// Verify objects were moved.
407407
EXPECT_TRUE(aString.empty());
@@ -453,13 +453,13 @@ struct ReferenceTypesTests: tpunit::TestFixture {
453453
Mock<ReferenceInterface> mock;
454454

455455
{
456-
When(Method(mock, returnStringByConstRef)).AlwaysReturnCapture(std::string{"aString"});
457-
When(Method(mock, returnStringByRef)).AlwaysReturnCapture(std::string{"bString"});
458-
When(Method(mock, returnStringByRValRef)).AlwaysReturnCapture(std::string{"cString"});
459-
When(Method(mock, returnMoveOnlyByConstRef)).AlwaysReturnCapture(std::unique_ptr<std::string>(new std::string{"aPtrString"}));
460-
When(Method(mock, returnMoveOnlyByRef)).AlwaysReturnCapture(std::unique_ptr<std::string>(new std::string{"bPtrString"}));
461-
When(Method(mock, returnMoveOnlyByRValRef)).AlwaysReturnCapture(std::unique_ptr<std::string>(new std::string{"cPtrString"}));
462-
When(Method(mock, returnAbstractTypeByRef)).AlwaysReturnCapture(ConcreteType{"myConcreteType"});
456+
When(Method(mock, returnStringByConstRef)).AlwaysReturnValCapt(std::string{"aString"});
457+
When(Method(mock, returnStringByRef)).AlwaysReturnValCapt(std::string{"bString"});
458+
When(Method(mock, returnStringByRValRef)).AlwaysReturnValCapt(std::string{"cString"});
459+
When(Method(mock, returnMoveOnlyByConstRef)).AlwaysReturnValCapt(std::unique_ptr<std::string>(new std::string{"aPtrString"}));
460+
When(Method(mock, returnMoveOnlyByRef)).AlwaysReturnValCapt(std::unique_ptr<std::string>(new std::string{"bPtrString"}));
461+
When(Method(mock, returnMoveOnlyByRValRef)).AlwaysReturnValCapt(std::unique_ptr<std::string>(new std::string{"cPtrString"}));
462+
When(Method(mock, returnAbstractTypeByRef)).AlwaysReturnValCapt(ConcreteType{"myConcreteType"});
463463
}
464464

465465
ReferenceInterface& i = mock.get();

0 commit comments

Comments
 (0)