xref: /llvm-project/libcxx/test/support/invocable_with_telemetry.h (revision 9803196849bd9c473aba7ead03da9aee5591f373)
139034388SChristopher Di Bella //===----------------------------------------------------------------------===//
239034388SChristopher Di Bella //
339034388SChristopher Di Bella // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
439034388SChristopher Di Bella // See https://llvm.org/LICENSE.txt for license information.
539034388SChristopher Di Bella // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
639034388SChristopher Di Bella //
739034388SChristopher Di Bella //===----------------------------------------------------------------------===//
839034388SChristopher Di Bella 
939034388SChristopher Di Bella #ifndef TEST_SUPPORT_INVOCABLE_WITH_TELEMETRY_H
1039034388SChristopher Di Bella #define TEST_SUPPORT_INVOCABLE_WITH_TELEMETRY_H
1139034388SChristopher Di Bella 
1239034388SChristopher Di Bella #include <cassert>
1339034388SChristopher Di Bella #include <concepts>
1439034388SChristopher Di Bella #include <functional>
1539034388SChristopher Di Bella #include <utility>
1639034388SChristopher Di Bella 
1739034388SChristopher Di Bella #if TEST_STD_VER < 20
1839034388SChristopher Di Bella #  error invocable_with_telemetry requires C++20
1939034388SChristopher Di Bella #else
2039034388SChristopher Di Bella struct invocable_telemetry {
2139034388SChristopher Di Bella   int invocations;
2239034388SChristopher Di Bella   int moves;
2339034388SChristopher Di Bella   int copies;
2439034388SChristopher Di Bella };
2539034388SChristopher Di Bella 
2639034388SChristopher Di Bella template <class F>
2739034388SChristopher Di Bella class invocable_with_telemetry {
2839034388SChristopher Di Bella public:
invocable_with_telemetry(F f,invocable_telemetry & telemetry)2939034388SChristopher Di Bella   constexpr invocable_with_telemetry(F f, invocable_telemetry& telemetry) : f_(f), telemetry_(&telemetry) {}
3039034388SChristopher Di Bella 
invocable_with_telemetry(invocable_with_telemetry && other)3139034388SChristopher Di Bella   constexpr invocable_with_telemetry(invocable_with_telemetry&& other)
3239034388SChristopher Di Bella     requires std::move_constructible<F>
3339034388SChristopher Di Bella       : f_(std::move(other.f_)),
34*98031968SKrystian Stasiowski         telemetry_((assert(other.telemetry_ != nullptr), std::exchange(other.telemetry_, nullptr))) {
3539034388SChristopher Di Bella     ++telemetry_->moves;
3639034388SChristopher Di Bella   }
3739034388SChristopher Di Bella 
invocable_with_telemetry(invocable_with_telemetry const & other)3839034388SChristopher Di Bella   constexpr invocable_with_telemetry(invocable_with_telemetry const& other)
3939034388SChristopher Di Bella     requires std::copy_constructible<F>
4039034388SChristopher Di Bella       : f_(other.f_), telemetry_((assert(other.telemetry_ != nullptr), other.telemetry_)) {
4139034388SChristopher Di Bella     ++telemetry_->copies;
4239034388SChristopher Di Bella   }
4339034388SChristopher Di Bella 
4439034388SChristopher Di Bella   constexpr invocable_with_telemetry& operator=(invocable_with_telemetry&& other)
4539034388SChristopher Di Bella     requires std::movable<F>
4639034388SChristopher Di Bella   {
4739034388SChristopher Di Bella     // Not using move-and-swap idiom to ensure that copies and moves remain accurate.
4839034388SChristopher Di Bella     assert(&other != this);
4939034388SChristopher Di Bella     assert(other.telemetry_ != nullptr);
5039034388SChristopher Di Bella 
5139034388SChristopher Di Bella     f_         = std::move(other.f_);
5239034388SChristopher Di Bella     telemetry_ = std::exchange(other.telemetry_, nullptr);
5339034388SChristopher Di Bella 
5439034388SChristopher Di Bella     ++telemetry_->moves;
5539034388SChristopher Di Bella     return *this;
5639034388SChristopher Di Bella   }
5739034388SChristopher Di Bella 
5839034388SChristopher Di Bella   constexpr invocable_with_telemetry& operator=(invocable_with_telemetry const& other)
5939034388SChristopher Di Bella     requires std::copyable<F>
6039034388SChristopher Di Bella   {
6139034388SChristopher Di Bella     // Not using copy-and-swap idiom to ensure that copies and moves remain accurate.
6239034388SChristopher Di Bella     assert(&other != this);
6339034388SChristopher Di Bella     assert(other.telemetry_ != nullptr);
6439034388SChristopher Di Bella 
6539034388SChristopher Di Bella     f_         = other.f_;
6639034388SChristopher Di Bella     telemetry_ = other.telemetry_;
6739034388SChristopher Di Bella 
6839034388SChristopher Di Bella     ++telemetry_->copies;
6939034388SChristopher Di Bella     return *this;
7039034388SChristopher Di Bella   }
7139034388SChristopher Di Bella 
7239034388SChristopher Di Bella   template <class... Args>
7339034388SChristopher Di Bella     requires std::invocable<F&, Args...>
decltype(auto)7439034388SChristopher Di Bella   constexpr decltype(auto) operator()(Args&&... args) noexcept(std::is_nothrow_invocable_v<F&, Args...>) {
7539034388SChristopher Di Bella     assert(telemetry_);
7639034388SChristopher Di Bella     ++telemetry_->invocations;
7739034388SChristopher Di Bella     return std::invoke(f_, std::forward<Args>(args)...);
7839034388SChristopher Di Bella   }
7939034388SChristopher Di Bella 
8039034388SChristopher Di Bella private:
8139034388SChristopher Di Bella   F f_                            = F();
8239034388SChristopher Di Bella   invocable_telemetry* telemetry_ = nullptr;
8339034388SChristopher Di Bella };
8439034388SChristopher Di Bella 
8539034388SChristopher Di Bella template <class F>
8639034388SChristopher Di Bella invocable_with_telemetry(F f, int& invocations, int& moves, int& copies) -> invocable_with_telemetry<F>;
8739034388SChristopher Di Bella 
8839034388SChristopher Di Bella #endif // TEST_STD_VER < 20
8939034388SChristopher Di Bella #endif // TEST_SUPPORT_INVOCABLE_WITH_TELEMETRY_H
90