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/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