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