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/impl/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::impl::ComponentBase>(
37 const components::ComponentContext&)>;
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(!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 return std::string_view{};
52}
53
54} // namespace impl
55
56/// @brief Exception that is thrown from
57/// components::ComponentContext::FindComponent() if a component load failed.
58class ComponentsLoadCancelledException : public std::runtime_error {
59 public:
60 ComponentsLoadCancelledException();
61 explicit ComponentsLoadCancelledException(const std::string& message);
62};
63
64/// @brief Class to retrieve other components.
65///
66/// Only the const member functions of this class are meant for usage in
67/// component constructor (because of that this class is always passed as a
68/// const reference to the constructors).
69///
70/// For usage outside of the component constructor see components::State
71///
72/// @see @ref userver_components
73class ComponentContext final {
74 public:
75 /// @brief Finds a component of type T with specified name (if any) and
76 /// returns the component after it was initialized.
77 ///
78 /// Can only be called from other component's constructor in a task where
79 /// that constructor was called.
80 /// May block and asynchronously wait for the creation of the requested
81 /// component.
82 /// @throw ComponentsLoadCancelledException if components loading was
83 /// cancelled due to errors in the creation of other component.
84 /// @throw std::runtime_error if component missing in `component_list` was
85 /// requested.
86 template <typename T>
87 T& FindComponent() const {
88 return FindComponent<T>(impl::NameFromComponentType<T>());
89 }
90
91 /// @overload T& FindComponent()
92 template <typename T>
93 T& FindComponent(std::string_view name) const {
94 if (!Contains(name)) {
95 ThrowNonRegisteredComponent(name, compiler::GetTypeName<T>());
96 }
97
98 auto* component_base = DoFindComponent(name);
99 T* ptr = dynamic_cast<T*>(component_base);
100 if (!ptr) {
101 ThrowComponentTypeMismatch(name, compiler::GetTypeName<T>(),
102 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
144 private:
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(!sizeof(T),
157 "components::ComponentContext should be accepted by "
158 "a constant reference, i.e. "
159 "`MyComponent(const components::ComponentConfig& config, "
160 "const components::ComponentContext& context)`");
161 return 0;
162 }
163
164 friend class Manager;
165 friend class State;
166
167 ComponentContext() noexcept;
168
169 void Emplace(const Manager& manager,
170 std::vector<std::string>&& loading_component_names);
171
172 void Reset() noexcept;
173
174 ~ComponentContext();
175
176 impl::ComponentBase* AddComponent(std::string_view name,
177 const impl::ComponentFactory& factory);
178
179 void OnAllComponentsLoaded();
180
181 void OnAllComponentsAreStopping();
182
183 void ClearComponents();
184
185 void CancelComponentsLoad();
186
187 [[noreturn]] void ThrowNonRegisteredComponent(std::string_view name,
188 std::string_view type) const;
189 [[noreturn]] void ThrowComponentTypeMismatch(
190 std::string_view name, std::string_view type,
191 impl::ComponentBase* component) const;
192
193 impl::ComponentBase* DoFindComponent(std::string_view name) const;
194
195 std::unique_ptr<impl::ComponentContextImpl> impl_;
196};
197
198} // namespace components
199
200USERVER_NAMESPACE_END