userver: userver/dynamic_config/snapshot.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
snapshot.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file userver/dynamic_config/snapshot.hpp
4/// @brief @copybrief dynamic_config::Snapshot
5
6#include <cstdint>
7#include <string>
8#include <type_traits>
9
10#include <userver/dynamic_config/impl/snapshot.hpp>
11#include <userver/dynamic_config/impl/to_json.hpp>
12#include <userver/formats/json_fwd.hpp>
13#include <userver/utils/fast_pimpl.hpp>
14
15USERVER_NAMESPACE_BEGIN
16
17namespace dynamic_config {
18
19/// A strong typedef for usage in dynamic_config::Key constructors.
20struct DefaultAsJsonString final {
21 constexpr explicit DefaultAsJsonString(std::string_view json_string);
22
23 std::string_view json_string;
24};
25
26/// A config name-value pair for usage in dynamic_config::Key constructors.
27struct ConfigDefault final {
28 template <typename T>
29 ConfigDefault(std::string_view name, const T& value);
30
31 ConfigDefault(std::string_view name, DefaultAsJsonString default_json);
32
33 std::string_view name;
34 std::string default_json;
35};
36
37/// A tag type for usage in dynamic_config::Key constructors.
38struct ConstantConfig final {
39 constexpr explicit ConstantConfig() = default;
40};
41
42/// @brief A config key is a unique identifier for a config variable
43/// @snippet core/src/components/logging_configurator.cpp key
44template <typename Variable>
45class Key final {
46 public:
47 /// The type of the parsed config variable.
48 using VariableType = Variable;
49
50 using JsonParser = Variable (*)(const formats::json::Value&);
51 using DocsMapParser = Variable (*)(const DocsMap&);
52
53 /// @brief The constructor for a trivial `VariableType`, e.g. `bool`, integer,
54 /// `double`, `string`. The default is passed by value.
55 ///
56 /// Usage example:
57 /// @snippet server/handlers/http_server_settings.cpp bool config sample
58 Key(std::string_view name, const VariableType& default_value);
59
60 /// @brief The constructor for a non-trivial `VariableType`. The default is
61 /// passed as a JSON string.
62 ///
63 /// Uses formats::json::Value `Parse` customization point function to parse
64 /// `VariableType`.
65 ///
66 /// Usage example:
67 /// @snippet components/logging_configurator.cpp key
68 Key(std::string_view name, DefaultAsJsonString default_json);
69
70 /// @brief The constructor that provides a special parser from JSON.
71 /// @warning Prefer the constructors above whenever possible.
72 /// @details Can be used when generic `Parse` is not applicable. Sometimes
73 /// used to add validation, e.g. minimum, maximum, string pattern, etc.
74 Key(std::string_view name, JsonParser parser,
75 DefaultAsJsonString default_json);
76
77 /// @brief The constructor that parses multiple JSON config items
78 /// into a single C++ object.
79 /// @warning Prefer to use a separate `Key` per JSON config item and use the
80 /// constructors above whenever possible.
81 ///
82 /// Usage example:
83 /// @snippet clients/http/component.cpp docs map config sample
84 template <std::size_t N>
85 Key(DocsMapParser parser, const ConfigDefault (&default_json_map)[N]);
86
87 /// Creates a config that always has the same value.
88 Key(ConstantConfig, VariableType value);
89
90 /// @cond
91 Key(impl::InternalTag, std::string_view name);
92
93 Key(impl::InternalTag, DocsMapParser parser);
94 /// @endcond
95
96 Key(const Key&) noexcept = delete;
97 Key& operator=(const Key&) noexcept = delete;
98
99 /// @returns the name of the config variable, as passed at the construction.
100 std::string_view GetName() const noexcept;
101
102 /// Parses the config. Useful only in some very niche scenarios. The config
103 /// value should be typically be retrieved from dynamic_config::Snapshot,
104 /// which is obtained from components::DynamicConfig in production or from
105 /// dynamic_config::StorageMock in unit tests.
106 VariableType Parse(const DocsMap& docs_map) const;
107
108 private:
109 friend struct impl::ConfigIdGetter;
110
111 const impl::ConfigId id_;
112};
113
114// clang-format off
115
116/// @brief The shared snapshot of
117/// @ref scripts/docs/en/userver/dynamic_config.md "dynamic configs". Cheap to
118/// copy, even cheaper to move. Thread safe, not updated with new dynamic
119/// config values in background (it's a snapshot!).
120///
121/// When a config update comes in via new `DocsMap`, configs of all
122/// the registered types are constructed and stored in `Config`. After that
123/// the `DocsMap` is dropped.
124///
125/// Config types are automatically registered if they are used
126/// somewhere in the program.
127///
128/// ## Usage example:
129/// @snippet components/component_sample_test.cpp Sample user component runtime config source
130
131// clang-format on
132class Snapshot final {
133 public:
134 Snapshot(const Snapshot&);
135 Snapshot& operator=(const Snapshot&);
136
137 Snapshot(Snapshot&&) noexcept;
138 Snapshot& operator=(Snapshot&&) noexcept;
139
140 ~Snapshot();
141
142 /// Used to access individual configs in the type-safe config map
143 template <typename VariableType>
144 const VariableType& operator[](const Key<VariableType>& key) const&;
145
146 /// Used to access individual configs in the type-safe config map
147 template <typename VariableType>
148 const VariableType& operator[](const Key<VariableType>&) &&;
149
150 /// @cond
151 // No longer supported, use `config[key]` instead
152 template <typename T>
153 const T& Get() const&;
154
155 // No longer supported, use `config[key]` instead
156 template <typename T>
157 const T& Get() &&;
158 /// @endcond
159
160 private:
161 // for the constructor
162 friend class Source;
163 friend class impl::StorageData;
164
165 explicit Snapshot(const impl::StorageData& storage);
166
167 const impl::SnapshotData& GetData() const;
168
169 struct Impl;
170 utils::FastPimpl<Impl, 16, 8> impl_;
171};
172
173// ========================== Implementation follows ==========================
174
175constexpr DefaultAsJsonString::DefaultAsJsonString(std::string_view json_string)
176 : json_string(json_string) {}
177
178template <typename T>
179ConfigDefault::ConfigDefault(std::string_view name, const T& value)
180 : name(name), default_json(impl::ToJsonString(value)) {}
181
182template <typename Variable>
183Key<Variable>::Key(std::string_view name, const VariableType& default_value)
184 : id_(impl::Register(
185 std::string{name},
186 [name = std::string{name}](const auto& docs_map) -> std::any {
187 return impl::DocsMapGet(docs_map, name).template As<VariableType>();
188 },
190
191template <typename Variable>
192Key<Variable>::Key(std::string_view name, DefaultAsJsonString default_json)
193 : id_(impl::Register(
194 std::string{name},
195 [name = std::string{name}](const auto& docs_map) -> std::any {
196 return impl::DocsMapGet(docs_map, name).template As<VariableType>();
197 },
199
200template <typename Variable>
201Key<Variable>::Key(std::string_view name, JsonParser parser,
202 DefaultAsJsonString default_json)
203 : id_(impl::Register(
204 std::string{name},
205 [name = std::string{name}, parser](const auto& docs_map) -> std::any {
207 },
209
210template <typename Variable>
211template <std::size_t N>
212Key<Variable>::Key(DocsMapParser parser,
213 const ConfigDefault (&default_json_map)[N])
214 : id_(impl::Register(
215 std::string{},
216 [parser](const DocsMap& docs_map) -> std::any {
217 return parser(docs_map);
218 },
219 impl::MultipleToDocsMapString(default_json_map, N))) {}
220
221template <typename Variable>
222Key<Variable>::Key(ConstantConfig /*tag*/, VariableType value)
223 : id_(impl::Register(
224 std::string{},
225 [value = std::move(value)](const DocsMap& /*unused*/) {
226 return value;
227 },
228 "{}")) {}
229
230template <typename Variable>
231Key<Variable>::Key(impl::InternalTag, std::string_view name)
232 : id_(impl::Register(
233 std::string{name},
234 [name = std::string{name}](const auto& docs_map) -> std::any {
235 return impl::DocsMapGet(docs_map, name).template As<VariableType>();
236 },
237 "{}")) {}
238
239template <typename Variable>
240Key<Variable>::Key(impl::InternalTag, DocsMapParser parser)
241 : id_(impl::Register(
242 std::string{},
243 [parser](const DocsMap& docs_map) -> std::any {
244 return parser(docs_map);
245 },
246 "{}")) {}
247
248template <typename VariableType>
249std::string_view Key<VariableType>::GetName() const noexcept {
250 return impl::GetName(id_);
251}
252
253template <typename VariableType>
254VariableType Key<VariableType>::Parse(const DocsMap& docs_map) const {
255 return std::any_cast<VariableType>(impl::MakeConfig(id_, docs_map));
256}
257
258template <typename VariableType>
259const VariableType& Snapshot::operator[](const Key<VariableType>& key) const& {
260 return GetData().Get<VariableType>(impl::ConfigIdGetter::Get(key));
261}
262
263template <typename VariableType>
264const VariableType& Snapshot::operator[](const Key<VariableType>&) && {
265 static_assert(!sizeof(VariableType),
266 "keep the Snapshot before using, please");
267}
268
269template <typename T>
270const T& Snapshot::Get() const& {
271 return (*this)[T::kDeprecatedKey];
272}
273
274template <typename T>
275const T& Snapshot::Get() && {
276 static_assert(!sizeof(T), "keep the Snapshot before using, please");
277}
278
279} // namespace dynamic_config
280
281USERVER_NAMESPACE_END