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 utils {
28class ResourceScopeStorage;
29} // namespace utils
30
31namespace components {
32
33namespace impl {
34
35class Manager;
36class ComponentContextImpl;
37class ComponentInfo;
38
39template <class T>
40constexpr auto NameFromComponentType() -> decltype(std::string_view{T::kName}) {
41 return T::kName;
42}
43
44template <class T, class... Args>
45constexpr auto NameFromComponentType(Args...) {
46 static_assert(
47 !sizeof(T),
48 "Component does not have a 'kName' member convertible to "
49 "std::string_view. You have to explicitly specify the name: "
50 "context.FindComponent<T>(name) or "
51 "context.FindComponentOptional<T>(name)."
52 );
53 return std::string_view{};
54}
55
56} // namespace impl
57
58/// @brief Exception that is thrown from
59/// components::ComponentContext::FindComponent() if a component load failed.
60class ComponentsLoadCancelledException : public std::runtime_error {
61public:
62 ComponentsLoadCancelledException();
63 explicit ComponentsLoadCancelledException(std::string_view message);
64};
65
66/// @brief Class to retrieve other components.
67///
68/// Only the const member functions of this class are meant for usage in
69/// component constructor (because of that this class is always passed as a
70/// const reference to the constructors).
71///
72/// @warning Don't store references to `ComponentContext` in your component!
73/// Lifetime of the passed `ComponentContext` ends as soon as
74/// the constructor ends.
75///
76/// For usage outside the component constructor see components::State.
77///
78/// @see @ref userver_components
79class ComponentContext final {
80public:
81 ComponentContext(ComponentContext&&) = delete;
82 ComponentContext& operator=(ComponentContext&&) = delete;
83
84 /// @brief Finds a component of type T with specified name (if any) and
85 /// returns the component after it was initialized.
86 ///
87 /// Can only be called from other component's constructor in a task where
88 /// that constructor was called.
89 /// May block and asynchronously wait for the creation of the requested
90 /// component.
91 /// @throw ComponentsLoadCancelledException if components loading was
92 /// cancelled due to errors in the creation of other component.
93 /// @throw std::runtime_error if component missing in `component_list` was
94 /// requested.
95 template <typename T>
96 T& FindComponent() const {
97 return FindComponent<T>(impl::NameFromComponentType<T>());
98 }
99
100 /// @overload T& FindComponent()
101 template <typename T>
102 T& FindComponent(std::string_view name) const {
103 if (!Contains(name)) {
104 ThrowNonRegisteredComponent(name, compiler::GetTypeName<T>());
105 }
106
107 auto* component_base = DoFindComponent(name);
108 T* ptr = dynamic_cast<T*>(component_base);
109 if (!ptr) {
110 ThrowComponentTypeMismatch(name, compiler::GetTypeName<T>(), component_base);
111 }
112
113 return *ptr;
114 }
115
116 template <typename T>
117 T& FindComponent(std::string_view /*name*/ = {}) {
118 return ReportMisuse<T>();
119 }
120
121 /// @brief If there's no component with specified type and name return
122 /// nullptr; otherwise behaves as FindComponent().
123 template <typename T>
125 return FindComponentOptional<T>(impl::NameFromComponentType<T>());
126 }
127
128 /// @overload T* FindComponentOptional()
129 template <typename T>
130 T* FindComponentOptional(std::string_view name) const {
131 if (!Contains(name)) {
132 return nullptr;
133 }
134 return dynamic_cast<T*>(DoFindComponent(name));
135 }
136
137 template <typename T>
138 T& FindComponentOptional(std::string_view /*name*/ = {}) {
139 return ReportMisuse<T>();
140 }
141
142 /// @brief Returns an engine::TaskProcessor with the specified name.
143 engine::TaskProcessor& GetTaskProcessor(std::string_view name) const;
144
145 template <typename T>
146 engine::TaskProcessor& GetTaskProcessor(const T&) {
147 return ReportMisuse<T>();
148 }
149
150 /// @brief Returns the current component name. This is helpful in cases
151 /// where multiple instances of the component class may be created using
152 /// `component_list.Append<T>("custom-name")` syntax.
153 ///
154 /// @warning The lifetime of the returned string ends as soon as
155 /// the current component's constructor completes. Store it
156 /// as an `std::string` if needed.
157 std::string_view GetComponentName() const;
158
159 /// @brief Returns @ref utils::ResourceScopeStorage that can be used
160 /// (by a component or any other class) to register user resources.
161 utils::ResourceScopeStorage& Scopes() const;
162
163 /// @cond
164 // For internal use only.
165 ComponentContext(utils::impl::InternalTag, impl::ComponentContextImpl& impl, impl::ComponentInfo& component_info)
166 noexcept;
167
168 // For internal use only.
169 impl::ComponentContextImpl& GetImpl(utils::impl::InternalTag) const;
170
171 // For internal use only.
172 const impl::Manager& GetManager(utils::impl::InternalTag) const;
173 /// @endcond
174
175private:
176 /// @returns true if there is a component with the specified name and it
177 /// could be found via FindComponent()
178 bool Contains(std::string_view name) const noexcept;
179
180 template <typename T>
181 bool Contains(const T&) {
182 return ReportMisuse<T>();
183 }
184
185 template <class T>
186 decltype(auto) ReportMisuse() {
187 static_assert(
188 !sizeof(T),
189 "components::ComponentContext should be accepted by "
190 "a constant reference, i.e. "
191 "`MyComponent(const components::ComponentConfig& config, "
192 "const components::ComponentContext& context)`"
193 );
194 return 0;
195 }
196
197 [[noreturn]] void ThrowNonRegisteredComponent(std::string_view name, std::string_view type) const;
198
199 [[noreturn]] void ThrowComponentTypeMismatch(
200 std::string_view name,
201 std::string_view type,
202 RawComponentBase* component
203 ) const;
204
205 RawComponentBase* DoFindComponent(std::string_view name) const;
206
207 impl::ComponentContextImpl& impl_;
208 impl::ComponentInfo& component_info_;
209};
210
211} // namespace components
212
213USERVER_NAMESPACE_END