userver: userver/utils/impl/wrapped_call.hpp Source File
⚠️ This is the documentation for an old userver version. Click here to switch to the latest version.
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages Concepts
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