userver: userver/components/component_context.hpp Source File
⚠️ This is the documentation for an old userver version. Click here to switch to the latest version.
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages Concepts
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/impl/component_base.hpp>
15#include <userver/engine/task/task_processor_fwd.hpp>
16
17USERVER_NAMESPACE_BEGIN
18
19namespace engine::impl {
20class TaskContext;
21} // namespace engine::impl
22
23namespace components {
24
25class Manager;
26
27namespace impl {
28
29enum class ComponentLifetimeStage;
30class ComponentInfo;
31
32template <class T>
33constexpr auto NameFromComponentType() -> decltype(std::string_view{T::kName}) {
34 return T::kName;
35}
36
37template <class T, class... Args>
38constexpr auto NameFromComponentType(Args...) {
39 static_assert(!sizeof(T),
40 "Component does not have a 'kName' member convertible to "
41 "std::string_view. You have to explicitly specify the name: "
42 "context.FindComponent<T>(name) or "
43 "context.FindComponentOptional<T>(name).");
44 return std::string_view{};
45}
46
47} // namespace impl
48
49/// @brief Exception that is thrown from
50/// components::ComponentContext::FindComponent() if a component load failed.
51class ComponentsLoadCancelledException : public std::runtime_error {
52 public:
53 ComponentsLoadCancelledException();
54 explicit ComponentsLoadCancelledException(const std::string& message);
55};
56
57/// @brief Class to retrieve other components.
58///
59/// Only the const member functions of this class are meant for usage in
60/// component constructor (because of that this class is always passed as a
61/// const reference to the constructors).
62///
63/// @see @ref userver_components
64class ComponentContext final {
65 public:
66 /// @brief Finds a component of type T with specified name (if any) and
67 /// returns the component after it was initialized.
68 ///
69 /// Can only be called from other component's constructor in a task where
70 /// that constructor was called.
71 /// May block and asynchronously wait for the creation of the requested
72 /// component.
73 /// @throw ComponentsLoadCancelledException if components loading was
74 /// cancelled due to errors in the creation of other component.
75 /// @throw std::runtime_error if component missing in `component_list` was
76 /// requested.
77 template <typename T>
78 T& FindComponent() const {
79 return FindComponent<T>(impl::NameFromComponentType<T>());
80 }
81
82 /// @overload T& FindComponent()
83 template <typename T>
84 T& FindComponent(std::string_view name) const {
85 if (!Contains(name)) {
86 ThrowNonRegisteredComponent(name, compiler::GetTypeName<T>());
87 }
88
89 auto* component_base = DoFindComponent(name);
90 T* ptr = dynamic_cast<T*>(component_base);
91 if (!ptr) {
92 ThrowComponentTypeMismatch(name, compiler::GetTypeName<T>(),
93 component_base);
94 }
95
96 return *ptr;
97 }
98
99 template <typename T>
100 T& FindComponent(std::string_view /*name*/ = {}) {
101 return ReportMisuse<T>();
102 }
103
104 /// @brief If there's no component with specified type and name return
105 /// nullptr; otherwise behaves as FindComponent().
106 template <typename T>
108 return FindComponentOptional<T>(impl::NameFromComponentType<T>());
109 }
110
111 /// @overload T* FindComponentOptional()
112 template <typename T>
113 T* FindComponentOptional(std::string_view name) const {
114 if (!Contains(name)) {
115 return nullptr;
116 }
117 return dynamic_cast<T*>(DoFindComponent(name));
118 }
119
120 template <typename T>
121 T& FindComponentOptional(std::string_view /*name*/ = {}) {
122 return ReportMisuse<T>();
123 }
124
125 /// @brief Returns an engine::TaskProcessor with the specified name.
126 engine::TaskProcessor& GetTaskProcessor(const std::string& name) const;
127
128 template <typename T>
129 engine::TaskProcessor& GetTaskProcessor(const T&) {
130 return ReportMisuse<T>();
131 }
132
133 const Manager& GetManager() const;
134
135 /// @returns true if one of the components is in fatal state and can not
136 /// work. A component is in fatal state if the
137 /// components::ComponentHealth::kFatal value is returned from the overridden
138 /// components::LoggableComponentBase::GetComponentHealth().
140
141 private:
142 /// @returns true if there is a component with the specified name and it
143 /// could be found via FindComponent()
144 bool Contains(std::string_view name) const noexcept;
145
146 template <typename T>
147 bool Contains(const T&) {
148 return ReportMisuse<T>();
149 }
150
151 template <class T>
152 decltype(auto) ReportMisuse() {
153 static_assert(!sizeof(T),
154 "components::ComponentContext should be accepted by "
155 "a constant reference, i.e. "
156 "`MyComponent(const components::ComponentConfig& config, "
157 "const components::ComponentContext& context)`");
158 return 0;
159 }
160
161 friend class Manager;
162
163 ComponentContext() noexcept;
164
165 void Emplace(const Manager& manager,
166 std::vector<std::string>&& loading_component_names);
167
168 void Reset() noexcept;
169
170 ~ComponentContext();
171
172 using ComponentFactory =
173 std::function<std::unique_ptr<components::impl::ComponentBase>(
174 const components::ComponentContext&)>;
175
176 impl::ComponentBase* AddComponent(std::string_view name,
177 const 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 struct Impl;
196 std::unique_ptr<Impl> impl_;
197};
198
199} // namespace components
200
201USERVER_NAMESPACE_END