userver: userver/components/component_context.hpp Source File
Loading...
Searching...
No Matches
component_context.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file userver/components/component_context.hpp
4/// @brief @copybrief components::ComponentContext
5
6#include <stdexcept>
7#include <string_view>
8
9#include <userver/compiler/demangle.hpp>
10#include <userver/components/component_fwd.hpp>
11#include <userver/components/raw_component_base.hpp>
12#include <userver/engine/task/task_processor_fwd.hpp>
13#include <userver/utils/impl/internal_tag.hpp>
14
15// TODO remove extra includes
16#include <functional>
17#include <memory>
18#include <string>
19#include <vector>
20
21USERVER_NAMESPACE_BEGIN
22
23namespace engine::impl {
24class TaskContext;
25} // namespace engine::impl
26
27namespace components {
28
29namespace impl {
30
31class Manager;
32class ComponentContextImpl;
33class ComponentInfo;
34
35template <class T>
36constexpr auto NameFromComponentType() -> decltype(std::string_view{T::kName}) {
37 return T::kName;
38}
39
40template <class T, class... Args>
41constexpr auto NameFromComponentType(Args...) {
42 static_assert(
43 !sizeof(T),
44 "Component does not have a 'kName' member convertible to "
45 "std::string_view. You have to explicitly specify the name: "
46 "context.FindComponent<T>(name) or "
47 "context.FindComponentOptional<T>(name)."
48 );
49 return std::string_view{};
50}
51
52} // namespace impl
53
54/// @brief Exception that is thrown from
55/// components::ComponentContext::FindComponent() if a component load failed.
56class ComponentsLoadCancelledException : public std::runtime_error {
57public:
58 ComponentsLoadCancelledException();
59 explicit ComponentsLoadCancelledException(std::string_view message);
60};
61
62namespace impl {
63class ScopeBase;
64}
65using ScopePtr = std::unique_ptr<impl::ScopeBase>;
66
67/// @brief Class to retrieve other components.
68///
69/// Only the const member functions of this class are meant for usage in
70/// component constructor (because of that this class is always passed as a
71/// const reference to the constructors).
72///
73/// @warning Don't store references to `ComponentContext` in your component!
74/// Lifetime of the passed `ComponentContext` ends as soon as
75/// the constructor ends.
76///
77/// For usage outside the component constructor see components::State.
78///
79/// @see @ref userver_components
80class ComponentContext final {
81public:
82 ComponentContext(ComponentContext&&) = delete;
83 ComponentContext& operator=(ComponentContext&&) = delete;
84
85 /// @brief Finds a component of type T with specified name (if any) and
86 /// returns the component after it was initialized.
87 ///
88 /// Can only be called from other component's constructor in a task where
89 /// that constructor was called.
90 /// May block and asynchronously wait for the creation of the requested
91 /// component.
92 /// @throw ComponentsLoadCancelledException if components loading was
93 /// cancelled due to errors in the creation of other component.
94 /// @throw std::runtime_error if component missing in `component_list` was
95 /// requested.
96 template <typename T>
97 T& FindComponent() const {
98 return FindComponent<T>(impl::NameFromComponentType<T>());
99 }
100
101 /// @overload T& FindComponent()
102 template <typename T>
103 T& FindComponent(std::string_view name) const {
104 if (!Contains(name)) {
105 ThrowNonRegisteredComponent(name, compiler::GetTypeName<T>());
106 }
107
108 auto* component_base = DoFindComponent(name);
109 T* ptr = dynamic_cast<T*>(component_base);
110 if (!ptr) {
111 ThrowComponentTypeMismatch(name, compiler::GetTypeName<T>(), component_base);
112 }
113
114 return *ptr;
115 }
116
117 template <typename T>
118 T& FindComponent(std::string_view /*name*/ = {}) {
119 return ReportMisuse<T>();
120 }
121
122 /// @brief If there's no component with specified type and name return
123 /// nullptr; otherwise behaves as FindComponent().
124 template <typename T>
126 return FindComponentOptional<T>(impl::NameFromComponentType<T>());
127 }
128
129 /// @overload T* FindComponentOptional()
130 template <typename T>
131 T* FindComponentOptional(std::string_view name) const {
132 if (!Contains(name)) {
133 return nullptr;
134 }
135 return dynamic_cast<T*>(DoFindComponent(name));
136 }
137
138 template <typename T>
139 T& FindComponentOptional(std::string_view /*name*/ = {}) {
140 return ReportMisuse<T>();
141 }
142
143 /// @brief Returns an engine::TaskProcessor with the specified name.
144 engine::TaskProcessor& GetTaskProcessor(std::string_view name) const;
145
146 template <typename T>
147 engine::TaskProcessor& GetTaskProcessor(const T&) {
148 return ReportMisuse<T>();
149 }
150
151 /// @brief Returns the current component name. This is helpful in cases
152 /// where multiple instances of the component class may be created using
153 /// `component_list.Append<T>("custom-name")` syntax.
154 ///
155 /// @warning The lifetime of the returned string ends as soon as
156 /// the current component's constructor completes. Store it
157 /// as an `std::string` if needed.
158 std::string_view GetComponentName() const;
159
160 /// @brief Registers a functor to register some resource that will be
161 /// called after the component is succesfully created (including all
162 /// class descendants). The functor must return a RAII-style handle object
163 /// that unregisters the previously registered resource. The returned handle's
164 /// destructor is called just before the component destructor is called.
165 ///
166 /// @note callback is not called if the component is not created OR
167 /// any previously registered callback throws an exception.
168 /// @note if you don't have an existing RAII-ish class, but still want
169 /// to do a cleanup, you might want to use @ref utils::FastScopeGuard
170 /// to wrap the cleanup function.
171 void RegisterScope(ScopePtr) const;
172
173 /// @cond
174 // For internal use only.
175 ComponentContext(utils::impl::InternalTag, impl::ComponentContextImpl& impl, impl::ComponentInfo& component_info)
176 noexcept;
177
178 // For internal use only.
179 impl::ComponentContextImpl& GetImpl(utils::impl::InternalTag) const;
180
181 // For internal use only.
182 const impl::Manager& GetManager(utils::impl::InternalTag) const;
183 /// @endcond
184
185private:
186 /// @returns true if there is a component with the specified name and it
187 /// could be found via FindComponent()
188 bool Contains(std::string_view name) const noexcept;
189
190 template <typename T>
191 bool Contains(const T&) {
192 return ReportMisuse<T>();
193 }
194
195 template <class T>
196 decltype(auto) ReportMisuse() {
197 static_assert(
198 !sizeof(T),
199 "components::ComponentContext should be accepted by "
200 "a constant reference, i.e. "
201 "`MyComponent(const components::ComponentConfig& config, "
202 "const components::ComponentContext& context)`"
203 );
204 return 0;
205 }
206
207 [[noreturn]] void ThrowNonRegisteredComponent(std::string_view name, std::string_view type) const;
208
209 [[noreturn]] void ThrowComponentTypeMismatch(
210 std::string_view name,
211 std::string_view type,
212 RawComponentBase* component
213 ) const;
214
215 RawComponentBase* DoFindComponent(std::string_view name) const;
216
217 impl::ComponentContextImpl& impl_;
218 impl::ComponentInfo& component_info_;
219};
220
221} // namespace components
222
223USERVER_NAMESPACE_END