Skip to content

Commit 3551792

Browse files
committed
Added back .Return<non-ref-type>(val) to force capture for functions returning a reference, and improved ReturnCapture to construct the return type from the parameter if their reference is not compatible (to allow things like function-returning-std-string.ReturnCapture("string")).
1 parent 3db8fde commit 3551792

File tree

3 files changed

+179
-7
lines changed

3 files changed

+179
-7
lines changed

include/fakeit/StubbingProgress.hpp

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,9 @@ 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) {
7678
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.");
7779
return Return(r); // Only written to silence warning about not returning from a non-void function, but will never be executed.
@@ -88,19 +90,27 @@ namespace fakeit {
8890

8991
template<typename T>
9092
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+
// If a ref to T can be cast to a ref to R, then store T.
94+
// Otherwise, create an object R constructed from the received T and store it.
95+
using StoredType = typename std::conditional<
96+
std::is_constructible<fk_remove_cvref_t<R>&, fk_remove_cvref_t<T>&>::value,
97+
fk_remove_cvref_t<T>,
98+
fk_remove_cvref_t<R>>::type;
99+
auto store = std::make_shared<StoredType>(std::forward<T>(r));
94100
return Do([store](const typename fakeit::test_arg<arglist>::type...) mutable -> R {
95101
return std::forward<R>(*store);
96102
});
97103
}
98104

99105
template<typename T>
100106
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));
107+
// If a ref to T can be cast to a ref to R, then store T.
108+
// Otherwise, create an object R constructed from the received T and store it.
109+
using StoredType = typename std::conditional<
110+
std::is_constructible<fk_remove_cvref_t<R>&, fk_remove_cvref_t<T>&>::value,
111+
fk_remove_cvref_t<T>,
112+
fk_remove_cvref_t<R>>::type;
113+
auto store = std::make_shared<StoredType>(std::forward<T>(r));
104114
return AlwaysDo([store](const typename fakeit::test_arg<arglist>::type...) mutable -> R {
105115
return std::forward<R>(*store);
106116
});
@@ -127,6 +137,18 @@ namespace fakeit {
127137
void AlwaysReturn(const R &r) {
128138
return AlwaysDo([r](const typename fakeit::test_arg<arglist>::type...) -> R { return r; });
129139
}
140+
141+
MethodStubbingProgress<R, arglist...>& ReturnCapture(const R& r) {
142+
return Return(r);
143+
}
144+
145+
MethodStubbingProgress<R, arglist...>& ReturnCapture(R&& r) {
146+
return Return(std::move(r));
147+
}
148+
149+
void AlwaysReturnCapture(const R &r) {
150+
return AlwaysReturn(r);
151+
}
130152
};
131153

132154
template<typename R, typename ... arglist>
@@ -146,6 +168,14 @@ namespace fakeit {
146168
using helper::BasicReturnImplHelper<R, arglist...>::Return;
147169
using helper::BasicReturnImplHelper<R, arglist...>::AlwaysReturn;
148170

171+
// DEPRECATED: This should ideally be removed, it allows writing .Return<std::string>("ok") when a function
172+
// returns "const std::string&" (for example) to have the same behavior has .ReturnCapture("ok"). But it is prone
173+
// to errors (because you have to specify the type). .ReturnCapture("ok") is superior and should be used instead.
174+
template<typename TypeUsedToForceCapture, typename RealType, typename std::enable_if<!std::is_reference<TypeUsedToForceCapture>::value, bool>::type = true>
175+
MethodStubbingProgress<R, arglist...>& Return(RealType&& ret) {
176+
return this->ReturnCapture(TypeUsedToForceCapture(std::forward<RealType>(ret)));
177+
}
178+
149179
MethodStubbingProgress<R, arglist...> &
150180
Return(const Quantifier<R> &q) {
151181
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
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
/*
2+
* Copyright (c) 2014 Eran Pe'er.
3+
*
4+
* This program is made available under the terms of the MIT License.
5+
*
6+
* Created on Mar 10, 2014
7+
*/
8+
9+
#include "tpunit++.hpp"
10+
#include "fakeit.hpp"
11+
12+
using namespace fakeit;
13+
14+
struct ReturnTemplateSpecializationTests : tpunit::TestFixture {
15+
16+
ReturnTemplateSpecializationTests()
17+
: tpunit::TestFixture(
18+
TEST(ReturnTemplateSpecializationTests::return_ref_specialization_from_same_type_temp),
19+
TEST(ReturnTemplateSpecializationTests::return_ref_capture_from_same_type_temp),
20+
TEST(ReturnTemplateSpecializationTests::return_ref_specialization_from_other_type_temp),
21+
TEST(ReturnTemplateSpecializationTests::return_ref_capture_from_other_type_temp),
22+
TEST(ReturnTemplateSpecializationTests::return_ref_default_from_variable),
23+
TEST(ReturnTemplateSpecializationTests::return_ref_specialization_from_variable),
24+
TEST(ReturnTemplateSpecializationTests::return_ref_capture_from_variable)
25+
) {
26+
}
27+
28+
struct MoveOnly {
29+
int i = 0;
30+
MoveOnly(int pi) : i{pi} {}
31+
MoveOnly(const MoveOnly&) = delete;
32+
MoveOnly(MoveOnly&&) = default;
33+
};
34+
35+
struct SomeStruct {
36+
virtual ~SomeStruct() = default;
37+
38+
virtual const std::string& returnRef() = 0;
39+
40+
virtual const MoveOnly& returnRefMo() = 0;
41+
42+
virtual std::string returnVal() = 0;
43+
};
44+
45+
void return_ref_specialization_from_same_type_temp() {
46+
Mock<SomeStruct> mock;
47+
48+
When(Method(mock, returnRef)).Return<std::string>(std::string("something"));
49+
ASSERT_EQUAL(mock.get().returnRef(), "something");
50+
Verify(Method(mock, returnRef)).Once();
51+
52+
When(Method(mock, returnRefMo)).Return<MoveOnly>(MoveOnly{5});
53+
ASSERT_EQUAL(mock.get().returnRefMo().i, 5);
54+
Verify(Method(mock, returnRefMo)).Once();
55+
}
56+
57+
void return_ref_capture_from_same_type_temp() {
58+
Mock<SomeStruct> mock;
59+
60+
When(Method(mock, returnRef)).ReturnCapture(std::string("something"));
61+
ASSERT_EQUAL(mock.get().returnRef(), "something");
62+
Verify(Method(mock, returnRef)).Once();
63+
64+
When(Method(mock, returnRefMo)).ReturnCapture(MoveOnly{5});
65+
ASSERT_EQUAL(mock.get().returnRefMo().i, 5);
66+
Verify(Method(mock, returnRefMo)).Once();
67+
}
68+
69+
void return_ref_specialization_from_other_type_temp() {
70+
Mock<SomeStruct> mock;
71+
72+
When(Method(mock, returnRef)).Return<std::string>("something");
73+
ASSERT_EQUAL(mock.get().returnRef(), "something");
74+
Verify(Method(mock, returnRef)).Once();
75+
76+
When(Method(mock, returnRefMo)).Return<MoveOnly>(5);
77+
ASSERT_EQUAL(mock.get().returnRefMo().i, 5);
78+
Verify(Method(mock, returnRefMo)).Once();
79+
}
80+
81+
void return_ref_capture_from_other_type_temp() {
82+
Mock<SomeStruct> mock;
83+
84+
When(Method(mock, returnRef)).ReturnCapture("something");
85+
ASSERT_EQUAL(mock.get().returnRef(), "something");
86+
Verify(Method(mock, returnRef)).Once();
87+
88+
When(Method(mock, returnRefMo)).ReturnCapture(5);
89+
ASSERT_EQUAL(mock.get().returnRefMo().i, 5);
90+
Verify(Method(mock, returnRefMo)).Once();
91+
}
92+
93+
void return_ref_default_from_variable() {
94+
std::string something = "something";
95+
MoveOnly mo = 5;
96+
Mock<SomeStruct> mock;
97+
98+
When(Method(mock, returnRef)).Return(something);
99+
something = "a different thing";
100+
ASSERT_EQUAL(mock.get().returnRef(), "a different thing");
101+
Verify(Method(mock, returnRef)).Once();
102+
103+
When(Method(mock, returnRefMo)).Return(mo);
104+
mo.i = 10;
105+
ASSERT_EQUAL(mock.get().returnRefMo().i, 10);
106+
Verify(Method(mock, returnRefMo)).Once();
107+
}
108+
109+
void return_ref_specialization_from_variable() {
110+
std::string something = "something";
111+
MoveOnly mo = 5;
112+
Mock<SomeStruct> mock;
113+
114+
When(Method(mock, returnRef)).Return<std::string>(something);
115+
something = "a different thing";
116+
ASSERT_EQUAL(mock.get().returnRef(), "something");
117+
Verify(Method(mock, returnRef)).Once();
118+
119+
When(Method(mock, returnRefMo)).Return<MoveOnly>(std::move(mo));
120+
mo.i = 10;
121+
ASSERT_EQUAL(mock.get().returnRefMo().i, 5);
122+
Verify(Method(mock, returnRefMo)).Once();
123+
}
124+
125+
void return_ref_capture_from_variable() {
126+
std::string something = "something";
127+
MoveOnly mo = 5;
128+
Mock<SomeStruct> mock;
129+
130+
When(Method(mock, returnRef)).ReturnCapture(something);
131+
something = "a different thing";
132+
ASSERT_EQUAL(mock.get().returnRef(), "something");
133+
Verify(Method(mock, returnRef)).Once();
134+
135+
When(Method(mock, returnRefMo)).ReturnCapture(std::move(mo));
136+
mo.i = 10;
137+
ASSERT_EQUAL(mock.get().returnRefMo().i, 5);
138+
Verify(Method(mock, returnRefMo)).Once();
139+
}
140+
141+
} __ReturnTemplateSpecializationTests;

0 commit comments

Comments
 (0)