1 //===----------------------------------------------------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #ifndef TEST_SUPPORT_INVOCABLE_WITH_TELEMETRY_H 10 #define TEST_SUPPORT_INVOCABLE_WITH_TELEMETRY_H 11 12 #include <cassert> 13 #include <concepts> 14 #include <functional> 15 #include <utility> 16 17 #if TEST_STD_VER < 20 18 # error invocable_with_telemetry requires C++20 19 #else 20 struct invocable_telemetry { 21 int invocations; 22 int moves; 23 int copies; 24 }; 25 26 template <class F> 27 class invocable_with_telemetry { 28 public: invocable_with_telemetry(F f,invocable_telemetry & telemetry)29 constexpr invocable_with_telemetry(F f, invocable_telemetry& telemetry) : f_(f), telemetry_(&telemetry) {} 30 invocable_with_telemetry(invocable_with_telemetry && other)31 constexpr invocable_with_telemetry(invocable_with_telemetry&& other) 32 requires std::move_constructible<F> 33 : f_(std::move(other.f_)), 34 telemetry_((assert(other.telemetry_ != nullptr), std::exchange(other.telemetry_, nullptr))) { 35 ++telemetry_->moves; 36 } 37 invocable_with_telemetry(invocable_with_telemetry const & other)38 constexpr invocable_with_telemetry(invocable_with_telemetry const& other) 39 requires std::copy_constructible<F> 40 : f_(other.f_), telemetry_((assert(other.telemetry_ != nullptr), other.telemetry_)) { 41 ++telemetry_->copies; 42 } 43 44 constexpr invocable_with_telemetry& operator=(invocable_with_telemetry&& other) 45 requires std::movable<F> 46 { 47 // Not using move-and-swap idiom to ensure that copies and moves remain accurate. 48 assert(&other != this); 49 assert(other.telemetry_ != nullptr); 50 51 f_ = std::move(other.f_); 52 telemetry_ = std::exchange(other.telemetry_, nullptr); 53 54 ++telemetry_->moves; 55 return *this; 56 } 57 58 constexpr invocable_with_telemetry& operator=(invocable_with_telemetry const& other) 59 requires std::copyable<F> 60 { 61 // Not using copy-and-swap idiom to ensure that copies and moves remain accurate. 62 assert(&other != this); 63 assert(other.telemetry_ != nullptr); 64 65 f_ = other.f_; 66 telemetry_ = other.telemetry_; 67 68 ++telemetry_->copies; 69 return *this; 70 } 71 72 template <class... Args> 73 requires std::invocable<F&, Args...> decltype(auto)74 constexpr decltype(auto) operator()(Args&&... args) noexcept(std::is_nothrow_invocable_v<F&, Args...>) { 75 assert(telemetry_); 76 ++telemetry_->invocations; 77 return std::invoke(f_, std::forward<Args>(args)...); 78 } 79 80 private: 81 F f_ = F(); 82 invocable_telemetry* telemetry_ = nullptr; 83 }; 84 85 template <class F> 86 invocable_with_telemetry(F f, int& invocations, int& moves, int& copies) -> invocable_with_telemetry<F>; 87 88 #endif // TEST_STD_VER < 20 89 #endif // TEST_SUPPORT_INVOCABLE_WITH_TELEMETRY_H 90