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