userver: userver/components/container.hpp Source File
Loading...
Searching...
No Matches
container.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file
4/// @brief @copybrief components::Container
5
6#include <userver/components/component_base.hpp>
7#include <userver/components/component_config.hpp>
8#include <userver/components/component_context.hpp>
9#include <userver/formats/common/meta.hpp>
10
11USERVER_NAMESPACE_BEGIN
12
13namespace components {
14
15template <typename T>
16struct Of {};
17
18/// @cond
19constexpr std::string_view ContainerName(Of<void>) { return {}; }
20/// @endcond
21
22template <typename T>
23class Container;
24
25namespace impl {
26
27template <typename T>
28concept ContainerHasName = requires { ContainerName(Of<T>{}); };
29
30template <typename T>
31constexpr std::string_view GetContainerName()
32{
33 if constexpr (ContainerHasName<T>) {
34 return ContainerName(Of<T>());
35 } else {
36 static_assert(!sizeof(T), "Container name is not registered. Forgot to define ContainerName(Of<T>)?");
37 return {};
38 }
39}
40
41template <typename U>
42struct DependencyLocator {
43 const ComponentConfig& config;
44 const ComponentContext& context;
45
46 template <typename T>
47 using LocateDependencyResult = decltype(LocateDependency(
48 WithType<std::decay_t<T>>{},
49 std::declval<const ComponentConfig&>(),
50 std::declval<const ComponentContext&>()
51 ));
52
53 template <typename T>
54 static constexpr bool kIsReference = std::is_reference_v<LocateDependencyResult<T>>;
55
56 template <typename T>
57 static constexpr bool kIsLocatable =
58 !std::is_same_v<std::decay_t<T>, std::decay_t<U>> // 1) skip U::U(const U&)
59 && !!sizeof(LocateDependencyResult<T>) // 2) U is locate'able
60 ;
61
62 template <typename T>
63 requires(!kIsReference<T> && kIsLocatable<T>)
64 operator T() const {
65 return LocateDependency(WithType<std::decay_t<T>>{}, config, context);
66 }
67
68 template <typename T>
69 requires(kIsReference<T> && kIsLocatable<T>)
70 operator T&() const {
71 return LocateDependency(WithType<std::decay_t<T>>{}, config, context);
72 }
73};
74
75} // namespace impl
76
77/// @brief A function that extracts an in-component-stored data
78/// (probably, reference) of type `T`, `T&`, or `const T&` from
79/// @ref components::ComponentContext.
80///
81/// You may define your own version of `LocateDependency` for a custom type `T`
82/// either in T's namespace or (if T's namespace is not extendable for some
83/// reason) in `USERVER_NAMESPACE::components` namespace. Usually it is defined
84/// for a component `C` that returns `T` from `C::GetSomeT()` as following:
85///
86/// @snippet core/src/dynamic_config/storage/component.cpp LocateDependency example
87template <typename T>
88requires(formats::common::impl::HasParse<yaml_config::YamlConfig, T> && std::is_class_v<T>)
89T LocateDependency(WithType<T>, const ComponentConfig& config, const ComponentContext&)
90{
91 return config.As<T>();
92}
93
94template <typename T>
95requires impl::ContainerHasName<T>
96T& LocateDependency(WithType<T>, const ComponentConfig&, const ComponentContext& context)
97{
98 return context.FindComponent<Container<T>>().Get();
99}
100
101template <typename T>
102requires std::is_base_of_v<RawComponentBase, T>
103T& LocateDependency(WithType<T>, const ComponentConfig&, const ComponentContext& context)
104{
105 return context.FindComponent<T>();
106}
107
108/// @brief A simple Component that creates, hold, and distributes
109/// an object of user type `T`. The component has a name equal to the constexpr
110/// std::string_view result of user-defined `ContainerName(Of<T>{})`.
111///
112/// Every dependency of type `X` is resolved by the component using
113/// @ref components::LocateDependency. By default, it is able to resolve
114/// the following types of dependencies:
115/// - `X` that was previously registered via defining `ContainerName(Of<X>)`. That is
116/// a containerized dependency.
117/// - `X` that declares `Parse(const yaml_config::YamlConfig& value, formats::parse::To<X>)`.
118/// This is a static config dependency.
119///
120/// Besides that, you may define our own specialization for
121/// @ref components::LocateDependency to allow fetching dependencies
122/// from non-container components. E.g. userver already defines
123/// @ref dynamic_config::Source from @ref components::DynamicConfig.
124///
125/// The core limitation of a type `T` registered via `ContainerName(Of<T>)` is
126/// that it is not able to explicitly use
127/// @ref components::ComponentContext in the constructor.
128/// But if you want to only "fetch" something from the context,
129/// you're always able to define your own @ref components::LocateDependency
130/// that fetches everything you need from @ref components::ComponentContext.
131///
132/// Example:
133///
134/// @snippet core/src/components/container_test.cpp definition
135/// @snippet core/src/components/container_test.cpp registration
136template <typename T>
137class Container final : public ComponentBase {
138public:
139 Container(const ComponentConfig& config, const ComponentContext& context)
140 : ComponentBase(config, context),
141 content_(Build(config, context))
142 {}
143
144 T& Get() { return content_; }
145
146 static constexpr auto kConfigFileMode = ConfigFileMode::kNotRequired;
147
148 static constexpr std::string_view kName = impl::GetContainerName<T>();
149
150private:
151 static T Build(const ComponentConfig& config, const ComponentContext& context)
152 {
153 using Arg = impl::DependencyLocator<T>;
154 Arg arg{config, context};
155
156 // A kind of copy-paste, but a more generic solution would be too template-ish
157 // and absolutely non-readable :(
158 if constexpr (std::is_constructible<T>::value) {
159 return T();
160 } else if constexpr (std::is_constructible<T, Arg>::value) {
161 return T(arg);
162 } else if constexpr (std::is_constructible<T, Arg, Arg>::value) {
163 return T(arg, arg);
164 } else if constexpr (std::is_constructible<T, Arg, Arg, Arg>::value) {
165 return T(arg, arg, arg);
166 } else if constexpr (std::is_constructible<T, Arg, Arg, Arg, Arg>::value) {
167 return T(arg, arg, arg, arg);
168 } else if constexpr (std::is_constructible<T, Arg, Arg, Arg, Arg, Arg>::value) {
169 return T(arg, arg, arg, arg, arg);
170 } else if constexpr (std::is_constructible<T, Arg, Arg, Arg, Arg, Arg, Arg>::value) {
171 return T(arg, arg, arg, arg, arg, arg);
172 } else if constexpr (std::is_constructible<T, Arg, Arg, Arg, Arg, Arg, Arg, Arg>::value) {
173 return T(arg, arg, arg, arg, arg, arg, arg);
174 } else if constexpr (std::is_constructible<T, Arg, Arg, Arg, Arg, Arg, Arg, Arg, Arg>::value) {
175 return T(arg, arg, arg, arg, arg, arg, arg, arg);
176 } else if constexpr (std::is_constructible<T, Arg, Arg, Arg, Arg, Arg, Arg, Arg, Arg, Arg>::value) {
177 return T(arg, arg, arg, arg, arg, arg, arg, arg, arg);
178 } else if constexpr (std::is_constructible<T, Arg, Arg, Arg, Arg, Arg, Arg, Arg, Arg, Arg, Arg>::value) {
179 return T(arg, arg, arg, arg, arg, arg, arg, arg, arg, arg);
180 } else {
181 static_assert(
182 !sizeof(T),
183 "Failed to find an appropriate version of T::T(...). Please check that T has a constructor with "
184 "arguments of containerized types or locatable via LocateDependency()."
185 );
186 }
187 }
188
189 T content_;
190};
191
192} // namespace components
193
194USERVER_NAMESPACE_END