Github   Telegram
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/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 ThrowComponentTypeMissmatch(name, compiler::GetTypeName<T>(),
93 component_base);
94 }
95
96 return *ptr;
97 }
98
99 /// @brief If there's no component with specified type and name return
100 /// nullptr; otherwise behaves as FindComponent().
101 template <typename T>
103 return FindComponentOptional<T>(impl::NameFromComponentType<T>());
104 }
105
106 /// @overload T* FindComponentOptional()
107 template <typename T>
108 T* FindComponentOptional(std::string_view name) const {
109 if (!Contains(name)) {
110 return nullptr;
111 }
112 return dynamic_cast<T*>(DoFindComponent(name));
113 }
114
115 /// @brief Returns an engine::TaskProcessor with the specified name.
116 engine::TaskProcessor& GetTaskProcessor(const std::string& name) const;
117
118 const Manager& GetManager() const;
119
120 /// @returns true if one of the components is in fatal state and can not
121 /// work. A component is in fatal state if the
122 /// components::ComponentHealth::kFatal value is returned from the overriden
123 /// components::LoggableComponentBase::GetComponentHealth().
125
126 /// @returns true if there is a component with the specified name and it
127 /// could be found via FindComponent()
128 bool Contains(std::string_view name) const noexcept;
129
130 private:
131 friend class Manager;
132
133 ComponentContext() noexcept;
134
135 void Emplace(const Manager& manager,
136 std::vector<std::string>&& loading_component_names);
137
138 void Reset() noexcept;
139
140 ~ComponentContext();
141
142 using ComponentFactory =
143 std::function<std::unique_ptr<components::impl::ComponentBase>(
144 const components::ComponentContext&)>;
145
146 impl::ComponentBase* AddComponent(std::string_view name,
147 const ComponentFactory& factory);
148
149 void OnAllComponentsLoaded();
150
151 void OnAllComponentsAreStopping();
152
153 void ClearComponents();
154
155 void CancelComponentsLoad();
156
157 [[noreturn]] void ThrowNonRegisteredComponent(std::string_view name,
158 std::string_view type) const;
159 [[noreturn]] void ThrowComponentTypeMissmatch(
160 std::string_view name, std::string_view type,
161 impl::ComponentBase* component) const;
162
163 impl::ComponentBase* DoFindComponent(std::string_view name) const;
164
165 struct Impl;
166 std::unique_ptr<Impl> impl_;
167};
168
169} // namespace components
170
171USERVER_NAMESPACE_END