]> git.proxmox.com Git - ceph.git/blob - ceph/src/seastar/include/seastar/util/program-options.hh
update ceph source to reef 18.1.2
[ceph.git] / ceph / src / seastar / include / seastar / util / program-options.hh
1 /*
2 * This file is open source software, licensed to you under the terms
3 * of the Apache License, Version 2.0 (the "License"). See the NOTICE file
4 * distributed with this work for additional information regarding copyright
5 * ownership. You may not use this file except in compliance with the License.
6 *
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing,
12 * software distributed under the License is distributed on an
13 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14 * KIND, either express or implied. See the License for the
15 * specific language governing permissions and limitations
16 * under the License.
17 */
18 /*
19 * Copyright (C) 2017 ScyllaDB
20 */
21
22 #pragma once
23
24 #include <seastar/core/sstring.hh>
25 #include <seastar/core/print.hh>
26
27 #include <boost/any.hpp>
28 #include <boost/intrusive/list.hpp>
29
30 #include <string>
31 #include <unordered_map>
32 #include <vector>
33 #include <set>
34
35 /// \defgroup program-options Program Options
36 ///
37 /// \brief Infrastructure for configuring a seastar application
38 ///
39 /// The program-options infrastructure allows configuring seastar both by C++
40 /// code and by command-line and/or config files. This is achieved by
41 /// providing a set of self-describing and self-validating value types as well
42 /// as option groups to allow grouping them into arbitrary tree structures.
43 /// Seastar modules expose statically declared option structs, which derive from
44 /// \ref option_group and contain various concrete \ref basic_value members
45 /// comprising the required configuration. These structs are self-describing, and
46 /// self-validating, the name of the option group as well as the list of its
47 /// \ref basic_value member can be queried run-time.
48
49 namespace seastar {
50
51 namespace program_options {
52
53 ///
54 /// \brief Wrapper for command-line options with arbitrary string associations.
55 ///
56 /// This type, to be used with Boost.Program_options, will result in an option that stores an arbitrary number of
57 /// string associations.
58 ///
59 /// Values are specified in the form "key0=value0:[key1=value1:...]". Options of this type can be specified multiple
60 /// times, and the values will be merged (with the last-provided value for a key taking precedence).
61 ///
62 /// \note We need a distinct type (rather than a simple type alias) for overload resolution in the implementation, but
63 /// advertizing our inheritance of \c std::unordered_map would introduce the possibility of memory leaks since STL
64 /// containers do not declare virtual destructors.
65 ///
66 class string_map final : private std::unordered_map<sstring, sstring> {
67 private:
68 using base = std::unordered_map<sstring, sstring>;
69 public:
70 using base::value_type;
71 using base::key_type;
72 using base::mapped_type;
73
74 using base::base;
75 using base::at;
76 using base::find;
77 using base::count;
78 using base::emplace;
79 using base::clear;
80 using base::operator[];
81 using base::begin;
82 using base::end;
83
84 friend bool operator==(const string_map&, const string_map&);
85 friend bool operator!=(const string_map&, const string_map&);
86 };
87
88 inline bool operator==(const string_map& lhs, const string_map& rhs) {
89 return static_cast<const string_map::base&>(lhs) == static_cast<const string_map::base&>(rhs);
90 }
91
92 inline bool operator!=(const string_map& lhs, const string_map& rhs) {
93 return !(lhs == rhs);
94 }
95
96 ///
97 /// \brief Query the value of a key in a \c string_map, or a default value if the key doesn't exist.
98 ///
99 sstring get_or_default(const string_map&, const sstring& key, const sstring& def = sstring());
100
101 std::istream& operator>>(std::istream& is, string_map&);
102 std::ostream& operator<<(std::ostream& os, const string_map&);
103
104 /// \cond internal
105
106 //
107 // Required implementation hook for Boost.Program_options.
108 //
109 void validate(boost::any& out, const std::vector<std::string>& in, string_map*, int);
110
111 using list_base_hook = boost::intrusive::list_base_hook<boost::intrusive::link_mode<boost::intrusive::auto_unlink>>;
112
113 } // namespace program_options
114
115 enum class log_level;
116 enum class logger_timestamp_style;
117 enum class logger_ostream_type;
118
119 namespace memory {
120 enum class alloc_failure_kind;
121 }
122
123 namespace program_options {
124
125 /// \endcond
126
127 /// \addtogroup program-options
128 /// @{
129
130 class option_group;
131
132 /// Visitor interface for \ref option_group::describe().
133 ///
134 /// See \ref option_group::describe() for more details on the visiting algorithm.
135 class options_descriptor {
136 public:
137 /// Visit the start of the group.
138 ///
139 /// Called when entering a group. Groups can be nested, in which case there
140 /// will be another call to this method, before the current groups is closed.
141 /// \returns whether visitor is interested in the group: \p true - visit,
142 /// \p false - skip.
143 virtual bool visit_group_start(const std::string& name, bool used) = 0;
144 /// Visit the end of the group.
145 ///
146 /// Called after all values and nested groups were visited in the current
147 /// group.
148 virtual void visit_group_end() = 0;
149
150 /// Visit value metadata, common across all value types.
151 ///
152 /// Called at the start of visiting a value. After this, a call to the
153 /// appropriate \ref visit_value() overload (or \ref visit_selection_value())
154 /// follows.
155 /// \returns whether visitor is interested in the value: \p true - visit,
156 /// \p false - skip.
157 virtual bool visit_value_metadata(const std::string& name, const std::string& description, bool used) = 0;
158
159 /// Visit a switch (\ref value<std::monostate>).
160 virtual void visit_value() = 0;
161 /// Visit a value (\ref value), \p default_val is null when value has no default.
162 virtual void visit_value(const bool* default_val) = 0;
163 /// Visit a value (\ref value), \p default_val is null when value has no default.
164 virtual void visit_value(const int* default_val) = 0;
165 /// Visit a value (\ref value), \p default_val is null when value has no default.
166 virtual void visit_value(const unsigned* default_val) = 0;
167 /// Visit a value (\ref value), \p default_val is null when value has no default.
168 virtual void visit_value(const float* default_val) = 0;
169 /// Visit a value (\ref value), \p default_val is null when value has no default.
170 virtual void visit_value(const double* default_val) = 0;
171 /// Visit a value (\ref value), \p default_val is null when value has no default.
172 virtual void visit_value(const std::string* default_val) = 0;
173 /// Visit a value (\ref value), \p default_val is null when value has no default.
174 virtual void visit_value(const std::set<unsigned>* default_val) = 0;
175 /// Visit a value (\ref value), \p default_val is null when value has no default.
176 virtual void visit_value(const log_level* default_val) = 0;
177 /// Visit a value (\ref value), \p default_val is null when value has no default.
178 virtual void visit_value(const logger_timestamp_style* default_val) = 0;
179 /// Visit a value (\ref value), \p default_val is null when value has no default.
180 virtual void visit_value(const logger_ostream_type* default_val) = 0;
181 /// Visit a value (\ref value), \p default_val is null when value has no default.
182 virtual void visit_value(const memory::alloc_failure_kind* default_val) = 0;
183 /// Visit a value (\ref value), \p default_val is null when value has no default.
184 virtual void visit_value(const std::unordered_map<sstring, log_level>* default_val) = 0;
185
186 /// Visit a selection value (\ref selection_value), \p default_candidate is null when there is no default candidate.
187 virtual void visit_selection_value(const std::vector<std::string>& candidate_names, const std::size_t* default_candidate) = 0;
188 };
189
190 /// Visitor interface \ref option_group::mutate().
191 ///
192 /// See \ref option_group::mutate() for more details on the visiting algorithm.
193 class options_mutator {
194 public:
195 /// Visit the start of the group.
196 ///
197 /// Called when entering a group. Groups can be nested, in which case there
198 /// will be another call to this method, before the current groups is closed.
199 /// \returns whether visitor is interested in the group: \p true - visit,
200 /// \p false - skip.
201 virtual bool visit_group_start(const std::string& name, bool used) = 0;
202 /// Visit the end of the group.
203 ///
204 /// Called after all values and nested groups were visited in the current
205 /// group.
206 virtual void visit_group_end() = 0;
207
208 /// Visit value metadata, common across all value types.
209 ///
210 /// Called at the start of visiting a value. After this, a call to the
211 /// appropriate \ref visit_value() overload (or \ref visit_selection_value())
212 /// follows.
213 /// \returns whether visitor is interested in the value: \p true - visit,
214 /// \p false - skip.
215 virtual bool visit_value_metadata(const std::string& name, bool used) = 0;
216
217 /// Visit a switch (\ref value<std::monostate>), switch is set to returned value.
218 virtual bool visit_value() = 0;
219 /// Visit and optionally mutate a value (\ref value), should return true if value was mutated.
220 virtual bool visit_value(bool& val) = 0;
221 /// Visit a value (\ref value), \p default_val is null when value has no default.
222 virtual bool visit_value(int& val) = 0;
223 /// Visit and optionally mutate a value (\ref value), should return true if value was mutated.
224 virtual bool visit_value(unsigned& val) = 0;
225 /// Visit and optionally mutate a value (\ref value), should return true if value was mutated.
226 virtual bool visit_value(float& val) = 0;
227 /// Visit and optionally mutate a value (\ref value), should return true if value was mutated.
228 virtual bool visit_value(double& val) = 0;
229 /// Visit and optionally mutate a value (\ref value), should return true if value was mutated.
230 virtual bool visit_value(std::string& val) = 0;
231 /// Visit and optionally mutate a value (\ref value), should return true if value was mutated.
232 virtual bool visit_value(std::set<unsigned>& val) = 0;
233 /// Visit and optionally mutate a value (\ref value), should return true if value was mutated.
234 virtual bool visit_value(log_level& val) = 0;
235 /// Visit and optionally mutate a value (\ref value), should return true if value was mutated.
236 virtual bool visit_value(logger_timestamp_style& val) = 0;
237 /// Visit and optionally mutate a value (\ref value), should return true if value was mutated.
238 virtual bool visit_value(logger_ostream_type& val) = 0;
239 /// Visit and optionally mutate a value (\ref value), should return true if value was mutated.
240 virtual bool visit_value(memory::alloc_failure_kind& val) = 0;
241 /// Visit and optionally mutate a value (\ref value), should return true if value was mutated.
242 virtual bool visit_value(std::unordered_map<sstring, log_level>& val) = 0;
243
244 /// Visit and optionally mutate a selection value (\ref selection_value), should return true if value was mutated.
245 virtual bool visit_selection_value(const std::vector<std::string>& candidate_names, std::size_t& selected_candidate) = 0;
246 };
247
248 /// A tag type used to construct unused \ref option_group and \ref basic_value objects.
249 struct unused {};
250
251 class basic_value;
252
253 /// A group of options.
254 ///
255 /// \ref option_group is the basis for organizing options. It can hold a number
256 /// of \ref basic_value objects. These are typically also its members:
257 ///
258 /// struct my_option_group : public option_group {
259 /// value<> opt1;
260 /// value<bool> opt2;
261 /// ...
262 ///
263 /// my_option_group()
264 /// : option_group(nullptr, "My option group")
265 /// , opt1(this, "opt1", ...
266 /// , opt2(this, "opt2", ...
267 /// , ...
268 /// { }
269 /// };
270 ///
271 /// Option groups can also be nested and using this property one can build a
272 /// tree of option groups and values. This tree then can be visited using the
273 /// two visitor methods exposed by \ref option_group:
274 /// * \ref describe()
275 /// * \ref mutate()
276 ///
277 /// Using these two visitors one can easily implement glue code to expose an
278 /// entire options tree to the command line. Use \ref describe() to build the
279 /// command-line level description of the objects (using e.g.
280 /// boost::program_options) and after parsing the provided command-line options
281 /// use \ref mutate() to propagate the extracted values back into the options
282 /// tree. How this is done is entirely up to the visitors, the above methods
283 /// only offer an API to visit each group and value in the tree, they don't make
284 /// any assumption about how the visitor works and what its purpose is.
285 class option_group : public list_base_hook {
286 friend class basic_value;
287
288 public:
289 using value_list_type = boost::intrusive::list<
290 basic_value,
291 boost::intrusive::base_hook<list_base_hook>,
292 boost::intrusive::constant_time_size<false>>;
293
294 using option_group_list_type = boost::intrusive::list<
295 option_group,
296 boost::intrusive::base_hook<list_base_hook>,
297 boost::intrusive::constant_time_size<false>>;
298
299 private:
300 option_group* _parent;
301 bool _used = true;
302 std::string _name;
303 value_list_type _values;
304 option_group_list_type _subgroups;
305
306 public:
307 /// Construct an option group.
308 ///
309 /// \param parent - the parent option-group, this option group will become a
310 /// sub option group of the parent group
311 /// \param name - the name of the option group
312 explicit option_group(option_group* parent, std::string name);
313 /// Construct an unused option group.
314 ///
315 /// \param parent - the parent option-group, this option group will become a
316 /// sub option group of the parent group
317 /// \param name - the name of the option group
318 explicit option_group(option_group* parent, std::string name, unused);
319 option_group(option_group&&);
320 option_group(const option_group&) = delete;
321 virtual ~option_group() = default;
322
323 option_group& operator=(option_group&&) = delete;
324 option_group& operator=(const option_group&) = delete;
325
326 /// Does the option group has any values contained in it?
327 operator bool () const { return !_values.empty(); }
328 bool used() const { return _used; }
329 const std::string& name() const { return _name; }
330 const value_list_type& values() const { return _values; }
331 value_list_type& values() { return _values; }
332
333 /// Describe the content of this option group to the visitor.
334 ///
335 /// The content is visited in a depth-first manner:
336 /// * First the option groups itself is visited with
337 /// \ref options_descriptor::visit_group_start(). If this returns \p false
338 /// the entire content of the group, including all its subgroups and values
339 /// are skipped and \ref options_descriptor::visit_group_end() is called
340 /// immediately. Otherwise visiting the content of the group proceeds.
341 /// * All the values contained therein are visited. For each value the
342 /// following happens:
343 /// - First \ref options_descriptor::visit_value_metadata() is called
344 /// with generic metadata that all values have. If this return
345 /// \p false the value is skipped, otherwise visiting the value
346 /// proceeds.
347 /// - Then the appropriate overload of
348 /// \ref options_descriptor::visit_value() is called, with a pointer
349 /// to the default value of the respective value. The pointer is null
350 /// if there is no default value.
351 /// - For \ref selection_value,
352 /// \ref options_descriptor::visit_selection_value() will be called
353 /// instead of \ref options_descriptor::visit_value(). After the value
354 /// is visited, the \ref option_group instance belonging to each
355 /// candidate (if set) will be visited.
356 /// * All the nested \ref option_group instances in the current group are
357 /// visited.
358 /// * Finally \ref options_descriptor::visit_group_end() is called.
359 void describe(options_descriptor& descriptor) const;
360 /// Mutate the content of this option group by the visitor.
361 ///
362 /// The visiting algorithm is identical to that of \ref describe(), with the
363 /// following differences:
364 /// * \ref options_mutator::visit_value() is allowed to mutate the value
365 /// through the passed-in reference. It should return \p true if it did so
366 /// and \p false otherwise.
367 /// * When visiting a selection value, only the nested group belonging to
368 /// the selected value is visited afterwards.
369 void mutate(options_mutator& mutator);
370 };
371
372 /// A basic configuration option value.
373 ///
374 /// This serves as the common base-class of all the concrete value types.
375 class basic_value : public list_base_hook {
376 friend class option_group;
377
378 public:
379 option_group* _group;
380 bool _used = true;
381 std::string _name;
382 std::string _description;
383
384 private:
385 virtual void do_describe(options_descriptor& descriptor) const = 0;
386 virtual void do_mutate(options_mutator& mutator) = 0;
387
388 public:
389 basic_value(option_group& group, bool used, std::string name, std::string description);
390 basic_value(basic_value&&);
391 basic_value(const basic_value&) = delete;
392 virtual ~basic_value() = default;
393
394 basic_value& operator=(basic_value&&) = delete;
395 basic_value& operator=(const basic_value&) = delete;
396
397 bool used() const { return _used; }
398 const std::string& name() const { return _name; }
399 const std::string& description() const { return _description; }
400
401 void describe(options_descriptor& descriptor) const;
402 void mutate(options_mutator& mutator);
403 };
404
405 /// A configuration option value.
406 ///
407 /// \tparam T the type of the contained value.
408 template <typename T = std::monostate>
409 class value : public basic_value {
410 std::optional<T> _value;
411 bool _defaulted = true;
412
413 private:
414 virtual void do_describe(options_descriptor& descriptor) const override {
415 auto* val = _value ? &*_value : nullptr;
416 descriptor.visit_value(val);
417 }
418 virtual void do_mutate(options_mutator& mutator) override {
419 T val;
420 if (mutator.visit_value(val)) {
421 _value = std::move(val);
422 _defaulted = false;
423 }
424 }
425 void do_set_value(T value, bool defaulted) {
426 _value = std::move(value);
427 _defaulted = defaulted;
428 }
429
430 public:
431 /// Construct a value.
432 ///
433 /// \param group - the group containing this value
434 /// \param name - the name of this value
435 /// \param default_value - the default value, can be unset
436 /// \param description - the description of the value
437 value(option_group& group, std::string name, std::optional<T> default_value, std::string description)
438 : basic_value(group, true, std::move(name), std::move(description))
439 , _value(std::move(default_value))
440 { }
441 /// Construct an unused value.
442 value(option_group& group, std::string name, unused)
443 : basic_value(group, false, std::move(name), {})
444 { }
445 value(value&&) = default;
446 /// Is there a contained value?
447 operator bool () const { return bool(_value); }
448 /// Does this value still contain a default-value?
449 bool defaulted() const { return _defaulted; }
450 /// Return the contained value, assumes there is one, see \ref operator bool().
451 const T& get_value() const { return _value.value(); }
452 T& get_value() { return _value.value(); }
453 void set_default_value(T value) { do_set_value(std::move(value), true); }
454 void set_value(T value) { do_set_value(std::move(value), false); }
455 };
456
457 /// A switch-style configuration option value.
458 ///
459 /// Contains no value, can be set or not.
460 template <>
461 class value<std::monostate> : public basic_value {
462 std::optional<bool> _set;
463
464 private:
465 virtual void do_describe(options_descriptor& descriptor) const override {
466 descriptor.visit_value();
467 }
468 virtual void do_mutate(options_mutator& mutator) override {
469 bool is_set = mutator.visit_value();
470 if (_set.has_value()) {
471 // override the value only if it is not preset
472 if (is_set) {
473 _set = true;
474 }
475 } else {
476 _set = is_set;
477 }
478 }
479
480 public:
481 /// Construct a value.
482 ///
483 /// \param group - the group containing this value
484 /// \param name - the name of this value
485 /// \param description - the description of the value
486 value(option_group& group, std::string name, std::string description)
487 : basic_value(group, true, std::move(name), std::move(description))
488 { }
489 /// Construct an unused value.
490 value(option_group& group, std::string name, unused)
491 : basic_value(group, false, std::move(name), {})
492 { }
493 /// Is the option set?
494 operator bool () const { return _set ? _set.value() : false; }
495 void set_value() { _set = true; }
496 void unset_value() { _set = false; }
497 };
498
499 /// A selection value, allows selection from multiple candidates.
500 ///
501 /// The candidates objects are of an opaque type which may not accessible to
502 /// whoever is choosing between the available candidates. This allows the user
503 /// selecting between seastar internal types without exposing them.
504 /// Each candidate has a name, which is what the users choose based on. Each
505 /// candidate can also have an associated \ref option_group containing related
506 /// candidate-specific configuration options, allowing further configuring the
507 /// selected candidate. The code exposing the candidates should document the
508 /// concrete types these can be down-casted to.
509 template <typename T = std::monostate>
510 class selection_value : public basic_value {
511 public:
512 using deleter = std::function<void(T*)>;
513 using value_handle = std::unique_ptr<T, deleter>;
514 struct candidate {
515 std::string name;
516 value_handle value;
517 std::unique_ptr<option_group> opts;
518 };
519 using candidates = std::vector<candidate>;
520
521 private:
522 static constexpr size_t no_selected_candidate = -1;
523
524 private:
525 candidates _candidates;
526 size_t _selected_candidate = no_selected_candidate;
527 bool _defaulted = true;
528
529 private:
530 std::vector<std::string> get_candidate_names() const {
531 std::vector<std::string> candidate_names;
532 candidate_names.reserve(_candidates.size());
533 for (const auto& c : _candidates) {
534 candidate_names.push_back(c.name);
535 }
536 return candidate_names;
537 }
538 virtual void do_describe(options_descriptor& descriptor) const override {
539 descriptor.visit_selection_value(get_candidate_names(), _selected_candidate == no_selected_candidate ? nullptr : &_selected_candidate);
540 for (auto& c : _candidates) {
541 if (c.opts) {
542 c.opts->describe(descriptor);
543 }
544 }
545 }
546 virtual void do_mutate(options_mutator& mutator) override {
547 if (mutator.visit_selection_value(get_candidate_names(), _selected_candidate)) {
548 _defaulted = false;
549 }
550 if (_selected_candidate != no_selected_candidate) {
551 auto& c = _candidates.at(_selected_candidate);
552 if (c.opts) {
553 c.opts->mutate(mutator);
554 }
555 }
556 }
557 size_t find_candidate(const std::string& candidate_name) {
558 auto it = find_if(_candidates.begin(), _candidates.end(), [&] (const auto& candidate) {
559 return candidate.name == candidate_name;
560 });
561 if (it == _candidates.end()) {
562 throw std::invalid_argument(fmt::format("find_candidate(): failed to find candidate {}", candidate_name));
563 }
564 return it - _candidates.begin();
565 }
566
567 option_group* do_select_candidate(std::string candidate_name, bool defaulted) {
568 _selected_candidate = find_candidate(candidate_name);
569 _defaulted = defaulted;
570 return _candidates.at(_selected_candidate).opts.get();
571 }
572
573 public:
574 /// Construct a value.
575 ///
576 /// \param group - the group containing this value
577 /// \param name - the name of this value
578 /// \param candidates - the available candidates
579 /// \param default_candidates - the name of the default candidate
580 /// \param description - the description of the value
581 selection_value(option_group& group, std::string name, candidates candidates, std::string default_candidate, std::string description)
582 : basic_value(group, true, std::move(name), std::move(description))
583 , _candidates(std::move(candidates))
584 , _selected_candidate(find_candidate(default_candidate))
585 { }
586 selection_value(option_group& group, std::string name, candidates candidates, std::string description)
587 : basic_value(group, true, std::move(name), std::move(description))
588 , _candidates(std::move(candidates))
589 { }
590 /// Construct an unused value.
591 selection_value(option_group& group, std::string name, unused)
592 : basic_value(group, false, std::move(name), {})
593 { }
594 /// Was there a candidate selected (default also counts)?
595 operator bool () const { return _selected_candidate != no_selected_candidate; }
596 /// Is the currently selected candidate the default one?
597 bool defaulted() const { return _defaulted; }
598 /// Get the name of the currently selected candidate (assumes there is one selected, see \operator bool()).
599 const std::string& get_selected_candidate_name() const { return _candidates.at(_selected_candidate).name; }
600 /// Get the options of the currently selected candidate (assumes there is one selected, see \operator bool()).
601 const option_group* get_selected_candidate_opts() const { return _candidates.at(_selected_candidate).opts.get(); }
602 /// Get the options of the currently selected candidate (assumes there is one selected, see \operator bool()).
603 option_group* get_selected_candidate_opts() { return _candidates.at(_selected_candidate).opts.get(); }
604 T& get_selected_candidate() const { return *_candidates.at(_selected_candidate).value; }
605 /// Select a candidate.
606 ///
607 /// \param candidate_name - the name of the to-be-selected candidate.
608 option_group* select_candidate(std::string candidate_name) { return do_select_candidate(candidate_name, false); }
609 /// Select a candidate and make it the default.
610 ///
611 /// \param candidate_name - the name of the to-be-selected candidate.
612 option_group* select_default_candidate(std::string candidate_name) { return do_select_candidate(candidate_name, true); }
613 };
614
615 /// @}
616
617 }
618
619 }