From 8fa9461cc28e053d66f17132808d287ae51575e2 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Tue, 11 Apr 2023 10:54:26 -0700 Subject: [PATCH] Fix FunctionMocker compilation slowdown in 9d21db9e0a60a1ea61ec19331c9bc0dd33e907b1 The slowdown appears to be due to an implicit conversion of distinct (yet semantically identical) lambdas to `std::function`. Lifting out the lambdas into functors that don't get re-instantiated reduces compilation times by nearly half. Fixes #4156 PiperOrigin-RevId: 523447948 Change-Id: Ib0ae0761a54d7b1f2b706b14b2858eedf47e2297 --- .../include/gmock/gmock-spec-builders.h | 73 ++++++++++++++----- 1 file changed, 55 insertions(+), 18 deletions(-) diff --git a/googlemock/include/gmock/gmock-spec-builders.h b/googlemock/include/gmock/gmock-spec-builders.h index 4e498d8f..2a33acb1 100644 --- a/googlemock/include/gmock/gmock-spec-builders.h +++ b/googlemock/include/gmock/gmock-spec-builders.h @@ -204,6 +204,9 @@ class GTEST_API_ UntypedFunctionMockerBase { using UntypedExpectations = std::vector>; + struct UninterestingCallCleanupHandler; + struct FailureCleanupHandler; + // Returns an Expectation object that references and co-owns exp, // which must be an expectation on this mock function. Expectation GetHandleOf(ExpectationBase* exp); @@ -1396,6 +1399,41 @@ class Cleanup final { std::function f_; }; +struct UntypedFunctionMockerBase::UninterestingCallCleanupHandler { + CallReaction reaction; + std::stringstream& ss; + + ~UninterestingCallCleanupHandler() { + ReportUninterestingCall(reaction, ss.str()); + } +}; + +struct UntypedFunctionMockerBase::FailureCleanupHandler { + std::stringstream& ss; + std::stringstream& why; + std::stringstream& loc; + const ExpectationBase* untyped_expectation; + bool found; + bool is_excessive; + + ~FailureCleanupHandler() { + ss << "\n" << why.str(); + + if (!found) { + // No expectation matches this call - reports a failure. + Expect(false, nullptr, -1, ss.str()); + } else if (is_excessive) { + // We had an upper-bound violation and the failure message is in ss. + Expect(false, untyped_expectation->file(), untyped_expectation->line(), + ss.str()); + } else { + // We had an expected call and the matching expectation is + // described in ss. + Log(kInfo, loc.str() + ss.str(), 2); + } + } +}; + template class FunctionMocker; @@ -1794,8 +1832,15 @@ R FunctionMocker::InvokeWith(ArgumentTuple&& args) // // We use RAII to do the latter in case R is void or a non-moveable type. In // either case we can't assign it to a local variable. - const Cleanup report_uninteresting_call( - [&] { ReportUninterestingCall(reaction, ss.str()); }); + // + // Note that std::bind() is essential here. + // We *don't* use any local callback types (like lambdas). + // Doing so slows down compilation dramatically because the *constructor* of + // std::function is re-instantiated with different template + // parameters each time. + const UninterestingCallCleanupHandler report_uninteresting_call = { + reaction, ss + }; return PerformActionAndPrintResult(nullptr, std::move(args), ss.str(), ss); } @@ -1839,22 +1884,14 @@ R FunctionMocker::InvokeWith(ArgumentTuple&& args) // // We use RAII to do the latter in case R is void or a non-moveable type. In // either case we can't assign it to a local variable. - const Cleanup handle_failures([&] { - ss << "\n" << why.str(); - - if (!found) { - // No expectation matches this call - reports a failure. - Expect(false, nullptr, -1, ss.str()); - } else if (is_excessive) { - // We had an upper-bound violation and the failure message is in ss. - Expect(false, untyped_expectation->file(), untyped_expectation->line(), - ss.str()); - } else { - // We had an expected call and the matching expectation is - // described in ss. - Log(kInfo, loc.str() + ss.str(), 2); - } - }); + // + // Note that we *don't* use any local callback types (like lambdas) here. + // Doing so slows down compilation dramatically because the *constructor* of + // std::function is re-instantiated with different template + // parameters each time. + const FailureCleanupHandler handle_failures = { + ss, why, loc, untyped_expectation, found, is_excessive + }; return PerformActionAndPrintResult(untyped_action, std::move(args), ss.str(), ss);