xref: /llvm-project/libcxx/test/support/invocable_with_telemetry.h (revision 9803196849bd9c473aba7ead03da9aee5591f373)
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