From 51767261238513893d0cbd3fe2d7822ea4cf57a9 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Mon, 2 May 2022 08:29:36 -0700 Subject: gmock-actions: support ByMove in a specialization of ReturnAction. Rather than branching on whether the return type is ByMoveWrapper within ReturnAction itself, hoist the distinction to outside. This allows the main class template to be modified without worrying about this special case, which means we can stop using a shared pointer to the value (introduced as a linked_ptr in commit 3d1c78b2bf to support ByMove) in this commit and simplify the class template further in a future commit with the eventual aim of directly supporting move-only result types. PiperOrigin-RevId: 445938943 Change-Id: I7bc71ea301d5e493ac6ecbe57d62738a48a2721a --- googlemock/include/gmock/gmock-actions.h | 59 ++++++++++++++++++-------------- 1 file changed, 34 insertions(+), 25 deletions(-) diff --git a/googlemock/include/gmock/gmock-actions.h b/googlemock/include/gmock/gmock-actions.h index f9fee55..578315f 100644 --- a/googlemock/include/gmock/gmock-actions.h +++ b/googlemock/include/gmock/gmock-actions.h @@ -900,12 +900,12 @@ struct ByMoveWrapper { // of gtl::Container() is passed into Return. // template -class ReturnAction { +class ReturnAction final { public: // Constructs a ReturnAction object from the value to be returned. // 'value' is passed by value instead of by const reference in order // to allow Return("string literal") to compile. - explicit ReturnAction(R value) : value_(new R(std::move(value))) {} + explicit ReturnAction(R value) : value_(std::move(value)) {} // This template type conversion operator allows Return(x) to be // used in ANY function that returns x's type. @@ -924,19 +924,19 @@ class ReturnAction { "use ReturnRef instead of Return to return a reference"); static_assert(!std::is_void::value, "Can't use Return() on an action expected to return `void`."); - return Action(new Impl(value_)); + return Action(new Impl(value_)); } private: // Implements the Return(x) action for a particular function type F. - template + template class Impl : public ActionInterface { public: typedef typename Function::Result Result; typedef typename Function::ArgumentTuple ArgumentTuple; - explicit Impl(const std::shared_ptr& value) - : value_before_cast_(*value), + explicit Impl(const R& value) + : value_before_cast_(value), // Make an implicit conversion to Result before initializing the // Result object we store, avoiding calling any explicit constructor // of Result from R. @@ -961,30 +961,39 @@ class ReturnAction { Impl& operator=(const Impl&) = delete; }; - // Partially specialize for ByMoveWrapper. This version of ReturnAction will - // move its contents instead. - template - class Impl, F> : public ActionInterface { - public: - typedef typename Function::Result Result; - typedef typename Function::ArgumentTuple ArgumentTuple; + R value_; +}; + +// A specialization of ReturnAction when R is ByMoveWrapper for some T. +// +// This version applies the type system-defeating hack of moving from T even in +// the const call operator, checking at runtime that it isn't called more than +// once, since the user has declared their intent to do so by using ByMove. +template +class ReturnAction> final { + public: + explicit ReturnAction(ByMoveWrapper wrapper) + : state_(new State(std::move(wrapper.payload))) {} - explicit Impl(const std::shared_ptr& wrapper) - : performed_(false), wrapper_(wrapper) {} + T operator()() const { + GTEST_CHECK_(!state_->called) + << "A ByMove() action must be performed at most once."; - Result Perform(const ArgumentTuple&) override { - GTEST_CHECK_(!performed_) - << "A ByMove() action should only be performed once."; - performed_ = true; - return std::move(wrapper_->payload); - } + state_->called = true; + return std::move(state_->value); + } - private: - bool performed_; - const std::shared_ptr wrapper_; + private: + // We store our state on the heap so that we are copyable as required by + // Action, despite the fact that we are stateful and T may not be copyable. + struct State { + explicit State(T&& value_in) : value(std::move(value_in)) {} + + T value; + bool called = false; }; - const std::shared_ptr value_; + const std::shared_ptr state_; }; // Implements the ReturnNull() action. -- cgit v0.12