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