@@ -219,11 +219,8 @@ class Patch<ID, ReturnT(ArgTs...)>
219219 * \return a mocking_utils::Patch instance.
220220 */
221221 explicit Patch (const std::string & target, std::function<ReturnT(ArgTs...)> proxy)
222- : proxy_(proxy)
222+ : target_(target), proxy_(proxy)
223223 {
224- auto MMK_MANGLE (mock_type, create) =
225- PatchTraits<ID, ReturnT (ArgTs...)>::MMK_MANGLE (mock_type, create);
226- mock_ = mmk_mock (target.c_str (), mock_type);
227224 }
228225
229226 // Copy construction and assignment are disabled.
@@ -255,18 +252,14 @@ class Patch<ID, ReturnT(ArgTs...)>
255252 // / Inject a @p replacement for the patched function.
256253 Patch & then_call (std::function<ReturnT(ArgTs...)> replacement) &
257254 {
258- auto type_erased_trampoline =
259- reinterpret_cast <mmk_fn>(prepare_trampoline<ID>(replacement));
260- mmk_when (proxy_ (any<ArgTs>()...), .then_call = type_erased_trampoline);
255+ replace_with (replacement);
261256 return *this ;
262257 }
263258
264259 // / Inject a @p replacement for the patched function.
265260 Patch && then_call(std::function<ReturnT(ArgTs...)> replacement) &&
266261 {
267- auto type_erased_trampoline =
268- reinterpret_cast <mmk_fn>(prepare_trampoline<ID>(replacement));
269- mmk_when (proxy_ (any<ArgTs>()...), .then_call = type_erased_trampoline);
262+ replace_with (replacement);
270263 return std::move (*this );
271264 }
272265
@@ -276,7 +269,21 @@ class Patch<ID, ReturnT(ArgTs...)>
276269 template <typename T>
277270 T any () {return mmk_any (T);}
278271
279- mock_type mock_;
272+ void replace_with (std::function<ReturnT(ArgTs...)> replacement)
273+ {
274+ if (mock_) {
275+ throw std::logic_error (" Cannot configure patch more than once" );
276+ }
277+ auto type_erased_trampoline =
278+ reinterpret_cast <mmk_fn>(prepare_trampoline<ID>(replacement));
279+ auto MMK_MANGLE (mock_type, create) =
280+ PatchTraits<ID, ReturnT (ArgTs...)>::MMK_MANGLE (mock_type, create);
281+ mock_ = mmk_mock (target_.c_str (), mock_type);
282+ mmk_when (proxy_ (any<ArgTs>()...), .then_call = type_erased_trampoline);
283+ }
284+
285+ mock_type mock_{nullptr };
286+ std::string target_;
280287 std::function<ReturnT(ArgTs...)> proxy_;
281288};
282289
@@ -332,15 +339,29 @@ auto make_patch(const std::string & target, std::function<SignatureT> proxy)
332339#define MOCKING_UTILS_PATCH_TARGET (scope, function ) \
333340 (std::string(RCUTILS_STRINGIFY(function)) + " @" + (scope))
334341
335- // / Patch a `function` with a used-provided `replacement` in a given `scope`.
336- #define patch (scope, function, replacement ) \
342+ // / Prepare a mocking_utils::Patch for patching a `function` in a given `scope`
343+ // / but defer applying any changes.
344+ #define prepare_patch (scope, function ) \
337345 make_patch<__COUNTER__, decltype (function)>( \
338346 MOCKING_UTILS_PATCH_TARGET (scope, function), MOCKING_UTILS_PATCH_PROXY(function) \
339- ).then_call(replacement)
347+ )
340348
341- // / Patch a function with a function that only returns a value
342- #define patch_and_return (scope, function, return_value ) \
343- patch (scope, function, [&](auto && ...) {return return_value;})
349+ // / Patch a `function` with a used-provided `replacement` in a given `scope`.
350+ #define patch (scope, function, replacement ) \
351+ prepare_patch (scope, function).then_call(replacement)
352+
353+ // / Patch a `function` to always yield a given `return_code` in a given `scope`.
354+ #define patch_and_return (scope, function, return_code ) \
355+ patch (scope, function, [&](auto && ...) {return return_code;})
356+
357+ // / Patch a `function` to execute normally but always yield a given `return_code`
358+ // / in a given `scope`.
359+ #define inject_on_return (scope, function, return_code ) \
360+ patch ( \
361+ scope, function, ([&, base = function](auto && ... __args) { \
362+ static_cast <void >(base (std::forward<decltype (__args)>(__args)...)); \
363+ return return_code; \
364+ }))
344365
345366} // namespace mocking_utils
346367
0 commit comments