//===----------------------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #ifndef TEST_SUPPORT_INVOCABLE_WITH_TELEMETRY_H #define TEST_SUPPORT_INVOCABLE_WITH_TELEMETRY_H #include #include #include #include #if TEST_STD_VER < 20 # error invocable_with_telemetry requires C++20 #else struct invocable_telemetry { int invocations; int moves; int copies; }; template class invocable_with_telemetry { public: constexpr invocable_with_telemetry(F f, invocable_telemetry& telemetry) : f_(f), telemetry_(&telemetry) {} constexpr invocable_with_telemetry(invocable_with_telemetry&& other) requires std::move_constructible : f_(std::move(other.f_)), telemetry_(assert(other.telemetry_ != nullptr), std::exchange(other.telemetry_, nullptr)) { ++telemetry_->moves; } constexpr invocable_with_telemetry(invocable_with_telemetry const& other) requires std::copy_constructible : f_(other.f_), telemetry_((assert(other.telemetry_ != nullptr), other.telemetry_)) { ++telemetry_->copies; } constexpr invocable_with_telemetry& operator=(invocable_with_telemetry&& other) requires std::movable { // Not using move-and-swap idiom to ensure that copies and moves remain accurate. assert(&other != this); assert(other.telemetry_ != nullptr); f_ = std::move(other.f_); telemetry_ = std::exchange(other.telemetry_, nullptr); ++telemetry_->moves; return *this; } constexpr invocable_with_telemetry& operator=(invocable_with_telemetry const& other) requires std::copyable { // Not using copy-and-swap idiom to ensure that copies and moves remain accurate. assert(&other != this); assert(other.telemetry_ != nullptr); f_ = other.f_; telemetry_ = other.telemetry_; ++telemetry_->copies; return *this; } template requires std::invocable constexpr decltype(auto) operator()(Args&&... args) noexcept(std::is_nothrow_invocable_v) { assert(telemetry_); ++telemetry_->invocations; return std::invoke(f_, std::forward(args)...); } private: F f_ = F(); invocable_telemetry* telemetry_ = nullptr; }; template invocable_with_telemetry(F f, int& invocations, int& moves, int& copies) -> invocable_with_telemetry; #endif // TEST_STD_VER < 20 #endif // TEST_SUPPORT_INVOCABLE_WITH_TELEMETRY_H