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