userver: userver/utils/impl/wrapped_call.hpp Source File
Loading...
Searching...
No Matches
wrapped_call.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file userver/utils/impl/wrapped_call.hpp
4/// @brief @copybrief utils::impl::WrappedCall
5
6#include <cstddef>
7#include <functional>
8#include <new>
9#include <optional>
10#include <tuple>
11#include <type_traits>
12#include <utility>
13
14#include <userver/utils/assert.hpp>
15#include <userver/utils/impl/wrapped_call_base.hpp>
16#include <userver/utils/lazy_prvalue.hpp>
17#include <userver/utils/result_store.hpp>
18
19USERVER_NAMESPACE_BEGIN
20
21namespace utils::impl {
22
23/// std::packaged_task replacement with noncopyable types support
24template <typename T>
25class WrappedCall : public WrappedCallBase {
26 public:
27 /// Returns (or rethrows) the result of wrapped call invocation
28 T Retrieve() { return result_.Retrieve(); }
29
30 /// Returns (or rethrows) the result of wrapped call invocation
31 decltype(auto) Get() const& { return result_.Get(); }
32
33 void RethrowErrorResult() const final { (void)result_.Get(); }
34
35 protected:
36 WrappedCall() noexcept = default;
37
38 ResultStore<T>& GetResultStore() noexcept { return result_; }
39
40 private:
41 ResultStore<T> result_;
42};
43
44template <typename T>
45WrappedCall<T>& CastWrappedCall(WrappedCallBase& wrapped_call) noexcept {
46 UASSERT(dynamic_cast<WrappedCall<T>*>(&wrapped_call) != nullptr);
47 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-static-cast-downcast)
48 return static_cast<WrappedCall<T>&>(wrapped_call);
49}
50
51template <typename T>
52class OptionalSetNoneGuard final {
53 public:
54 OptionalSetNoneGuard(std::optional<T>& o) noexcept : o_(o) {}
55
56 ~OptionalSetNoneGuard() { o_ = std::nullopt; }
57
58 private:
59 std::optional<T>& o_;
60};
61
62template <typename T>
63struct UnrefImpl final {
64 using type = T;
65};
66
67template <typename T>
68struct UnrefImpl<std::reference_wrapper<T>> final {
69 using type = T&;
70};
71
72template <typename Func>
73struct UnrefImpl<utils::LazyPrvalue<Func>> final {
74 using type = std::invoke_result_t<Func&&>;
75};
76
77template <typename T>
78using DecayUnref = typename UnrefImpl<std::decay_t<T>>::type;
79
80// Stores passed arguments and function. Invokes function later with argument
81// types exactly matching the initial types of arguments passed to WrapCall.
82template <typename Function, typename... Args>
83class WrappedCallImpl final
84 : public WrappedCall<std::invoke_result_t<Function&&, Args&&...>> {
85 public:
86 using ResultType = std::invoke_result_t<Function&&, Args&&...>;
87
88 template <typename RawFunction, typename RawArgsTuple>
89 explicit WrappedCallImpl(RawFunction&& func, RawArgsTuple&& args)
90 : data_(std::in_place, std::forward<RawFunction>(func),
91 std::forward<RawArgsTuple>(args)) {}
92
93 void Perform() override {
94 UASSERT(data_);
95 if constexpr (std::is_pointer_v<Function>) {
96 UASSERT(data_->func != nullptr);
97 }
98
99 OptionalSetNoneGuard guard(data_);
100 auto& result = this->GetResultStore();
101
102 // This is the point at which stacktrace is cut,
103 // see 'logging/stacktrace_cache.cpp'.
104 try {
105 if constexpr (std::is_void_v<ResultType>) {
106 std::apply(std::forward<Function>(data_->func), std::move(data_->args));
107 result.SetValue();
108 } else {
109 result.SetValue(std::apply(std::forward<Function>(data_->func),
110 std::move(data_->args)));
111 }
112 } catch (const std::exception&) {
113 result.SetException(std::current_exception());
114 }
115 }
116
117 private:
118 struct Data final {
119 // TODO remove after paren-init for aggregates in C++20
120 template <typename RawFunction, typename RawArgsTuple>
121 explicit Data(RawFunction&& func, RawArgsTuple&& args)
122 // NOLINTNEXTLINE(clang-analyzer-cplusplus.Move)
123 : func(std::forward<RawFunction>(func)),
124 args(std::forward<RawArgsTuple>(args)) {}
125
126 Function func;
127 std::tuple<Args...> args;
128 };
129
130 std::optional<Data> data_;
131};
132
133template <typename Function, typename... Args>
134using WrappedCallImplType =
135 WrappedCallImpl<DecayUnref<Function>, DecayUnref<Args>...>;
136
137/// Construct a WrappedCallImplType at `storage`. See WrappedCall and
138/// WrappedCallBase for the API of the result. Note: using `reinterpret_cast` to
139/// cast `storage` to WrappedCallImpl is UB, use the function return value.
140template <typename Function, typename... Args>
141[[nodiscard]] auto& PlacementNewWrapCall(std::byte* storage, Function&& f,
142 Args&&... args) {
143 static_assert(
144 (!std::is_array_v<std::remove_reference_t<Args>> && ...),
145 "Passing C arrays to Async is forbidden. Use std::array instead");
146
147 return *new (storage) WrappedCallImplType<Function, Args...>(
148 std::forward<Function>(f),
149 std::forward_as_tuple(std::forward<Args>(args)...));
150}
151
152} // namespace utils::impl
153
154USERVER_NAMESPACE_END