Seastar
High performance C++ framework for concurrent servers
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 #include <seastar/util/modules.hh>
27 
28 #ifndef SEASTAR_MODULE
29 #include <fmt/format.h>
30 
31 #include <boost/any.hpp>
32 #include <boost/intrusive/list.hpp>
33 
34 #include <string>
35 #include <unordered_map>
36 #include <vector>
37 #include <set>
38 #endif
39 
53 
54 namespace seastar {
55 
56 namespace program_options {
57 
71 class string_map final : private std::unordered_map<sstring, sstring> {
72 private:
73  using base = std::unordered_map<sstring, sstring>;
74 public:
75  using base::value_type;
76  using base::key_type;
77  using base::mapped_type;
78 
79  using base::base;
80  using base::at;
81  using base::find;
82  using base::count;
83  using base::emplace;
84  using base::clear;
85  using base::operator[];
86  using base::begin;
87  using base::end;
88 
89  friend bool operator==(const string_map&, const string_map&);
90  friend bool operator!=(const string_map&, const string_map&);
91 };
92 
93 inline bool operator==(const string_map& lhs, const string_map& rhs) {
94  return static_cast<const string_map::base&>(lhs) == static_cast<const string_map::base&>(rhs);
95 }
96 
97 inline bool operator!=(const string_map& lhs, const string_map& rhs) {
98  return !(lhs == rhs);
99 }
100 
104 sstring get_or_default(const string_map&, const sstring& key, const sstring& def = sstring());
105 
106 std::istream& operator>>(std::istream& is, string_map&);
107 std::ostream& operator<<(std::ostream& os, const string_map&);
108 
110 
111 //
112 // Required implementation hook for Boost.Program_options.
113 //
114 void validate(boost::any& out, const std::vector<std::string>& in, string_map*, int);
115 
116 using list_base_hook = boost::intrusive::list_base_hook<boost::intrusive::link_mode<boost::intrusive::auto_unlink>>;
117 
118 } // namespace program_options
119 
120 SEASTAR_MODULE_EXPORT_BEGIN
121 enum class log_level;
122 enum class logger_timestamp_style;
123 enum class logger_ostream_type;
124 namespace memory {
125  enum class alloc_failure_kind;
126 }
127 
128 namespace program_options {
129 
131 
134 
135 class option_group;
136 
141 public:
148  virtual bool visit_group_start(const std::string& name, bool used) = 0;
153  virtual void visit_group_end() = 0;
154 
162  virtual bool visit_value_metadata(const std::string& name, const std::string& description, bool used) = 0;
163 
165  virtual void visit_value() = 0;
167  virtual void visit_value(const bool* default_val) = 0;
169  virtual void visit_value(const int* default_val) = 0;
171  virtual void visit_value(const unsigned* default_val) = 0;
173  virtual void visit_value(const float* default_val) = 0;
175  virtual void visit_value(const double* default_val) = 0;
177  virtual void visit_value(const std::string* default_val) = 0;
179  virtual void visit_value(const std::set<unsigned>* default_val) = 0;
181  virtual void visit_value(const log_level* default_val) = 0;
183  virtual void visit_value(const logger_timestamp_style* default_val) = 0;
185  virtual void visit_value(const logger_ostream_type* default_val) = 0;
187  virtual void visit_value(const memory::alloc_failure_kind* default_val) = 0;
189  virtual void visit_value(const std::unordered_map<sstring, log_level>* default_val) = 0;
190 
192  virtual void visit_selection_value(const std::vector<std::string>& candidate_names, const std::size_t* default_candidate) = 0;
193 };
194 
199 public:
206  virtual bool visit_group_start(const std::string& name, bool used) = 0;
211  virtual void visit_group_end() = 0;
212 
220  virtual bool visit_value_metadata(const std::string& name, bool used) = 0;
221 
223  virtual bool visit_value() = 0;
225  virtual bool visit_value(bool& val) = 0;
227  virtual bool visit_value(int& val) = 0;
229  virtual bool visit_value(unsigned& val) = 0;
231  virtual bool visit_value(float& val) = 0;
233  virtual bool visit_value(double& val) = 0;
235  virtual bool visit_value(std::string& val) = 0;
237  virtual bool visit_value(std::set<unsigned>& val) = 0;
239  virtual bool visit_value(log_level& val) = 0;
241  virtual bool visit_value(logger_timestamp_style& val) = 0;
243  virtual bool visit_value(logger_ostream_type& val) = 0;
245  virtual bool visit_value(memory::alloc_failure_kind& val) = 0;
247  virtual bool visit_value(std::unordered_map<sstring, log_level>& val) = 0;
248 
250  virtual bool visit_selection_value(const std::vector<std::string>& candidate_names, std::size_t& selected_candidate) = 0;
251 };
252 
254 struct unused {};
255 
256 class basic_value;
257 
290 class option_group : public list_base_hook {
291  friend class basic_value;
292 
293 public:
294  using value_list_type = boost::intrusive::list<
295  basic_value,
296  boost::intrusive::base_hook<list_base_hook>,
297  boost::intrusive::constant_time_size<false>>;
298 
299  using option_group_list_type = boost::intrusive::list<
300  option_group,
301  boost::intrusive::base_hook<list_base_hook>,
302  boost::intrusive::constant_time_size<false>>;
303 
304 private:
305  option_group* _parent;
306  bool _used = true;
307  std::string _name;
308  value_list_type _values;
309  option_group_list_type _subgroups;
310 
311 public:
317  explicit option_group(option_group* parent, std::string name);
323  explicit option_group(option_group* parent, std::string name, unused);
325  option_group(const option_group&) = delete;
326  virtual ~option_group() = default;
327 
328  option_group& operator=(option_group&&) = delete;
329  option_group& operator=(const option_group&) = delete;
330 
332  operator bool () const { return !_values.empty(); }
333  bool used() const { return _used; }
334  const std::string& name() const { return _name; }
335  const value_list_type& values() const { return _values; }
336  value_list_type& values() { return _values; }
337 
364  void describe(options_descriptor& descriptor) const;
374  void mutate(options_mutator& mutator);
375 };
376 
380 class basic_value : public list_base_hook {
381  friend class option_group;
382 
383 public:
384  option_group* _group;
385  bool _used = true;
386  std::string _name;
387  std::string _description;
388 
389 private:
390  virtual void do_describe(options_descriptor& descriptor) const = 0;
391  virtual void do_mutate(options_mutator& mutator) = 0;
392 
393 public:
394  basic_value(option_group& group, bool used, std::string name, std::string description);
396  basic_value(const basic_value&) = delete;
397  virtual ~basic_value() = default;
398 
399  basic_value& operator=(basic_value&&) = delete;
400  basic_value& operator=(const basic_value&) = delete;
401 
402  bool used() const { return _used; }
403  const std::string& name() const { return _name; }
404  const std::string& description() const { return _description; }
405 
406  void describe(options_descriptor& descriptor) const;
407  void mutate(options_mutator& mutator);
408 };
409 
413 template <typename T = std::monostate>
414 class value : public basic_value {
415  std::optional<T> _value;
416  bool _defaulted = true;
417 
418 private:
419  virtual void do_describe(options_descriptor& descriptor) const override {
420  auto* val = _value ? &*_value : nullptr;
421  descriptor.visit_value(val);
422  }
423  virtual void do_mutate(options_mutator& mutator) override {
424  T val;
425  if (mutator.visit_value(val)) {
426  _value = std::move(val);
427  _defaulted = false;
428  }
429  }
430  void do_set_value(T value, bool defaulted) {
431  _value = std::move(value);
432  _defaulted = defaulted;
433  }
434 
435 public:
442  value(option_group& group, std::string name, std::optional<T> default_value, std::string description)
443  : basic_value(group, true, std::move(name), std::move(description))
444  , _value(std::move(default_value))
445  { }
447  value(option_group& group, std::string name, unused)
448  : basic_value(group, false, std::move(name), {})
449  { }
450  value(value&&) = default;
452  operator bool () const { return bool(_value); }
454  bool defaulted() const { return _defaulted; }
456  const T& get_value() const { return _value.value(); }
457  T& get_value() { return _value.value(); }
458  void set_default_value(T value) { do_set_value(std::move(value), true); }
459  void set_value(T value) { do_set_value(std::move(value), false); }
460 };
461 
465 template <>
466 class value<std::monostate> : public basic_value {
467  std::optional<bool> _set;
468 
469 private:
470  virtual void do_describe(options_descriptor& descriptor) const override {
471  descriptor.visit_value();
472  }
473  virtual void do_mutate(options_mutator& mutator) override {
474  bool is_set = mutator.visit_value();
475  if (_set.has_value()) {
476  // override the value only if it is not preset
477  if (is_set) {
478  _set = true;
479  }
480  } else {
481  _set = is_set;
482  }
483  }
484 
485 public:
491  value(option_group& group, std::string name, std::string description)
492  : basic_value(group, true, std::move(name), std::move(description))
493  { }
495  value(option_group& group, std::string name, unused)
496  : basic_value(group, false, std::move(name), {})
497  { }
499  operator bool () const { return _set ? _set.value() : false; }
500  void set_value() { _set = true; }
501  void unset_value() { _set = false; }
502 };
503 
514 template <typename T = std::monostate>
515 class selection_value : public basic_value {
516 public:
517  using deleter = std::function<void(T*)>;
518  using value_handle = std::unique_ptr<T, deleter>;
519  struct candidate {
520  std::string name;
521  value_handle value;
522  std::unique_ptr<option_group> opts;
523  };
524  using candidates = std::vector<candidate>;
525 
526 private:
527  static constexpr size_t no_selected_candidate = -1;
528 
529 private:
530  candidates _candidates;
531  size_t _selected_candidate = no_selected_candidate;
532  bool _defaulted = true;
533 
534 private:
535  std::vector<std::string> get_candidate_names() const {
536  std::vector<std::string> candidate_names;
537  candidate_names.reserve(_candidates.size());
538  for (const auto& c : _candidates) {
539  candidate_names.push_back(c.name);
540  }
541  return candidate_names;
542  }
543  virtual void do_describe(options_descriptor& descriptor) const override {
544  descriptor.visit_selection_value(get_candidate_names(), _selected_candidate == no_selected_candidate ? nullptr : &_selected_candidate);
545  for (auto& c : _candidates) {
546  if (c.opts) {
547  c.opts->describe(descriptor);
548  }
549  }
550  }
551  virtual void do_mutate(options_mutator& mutator) override {
552  if (mutator.visit_selection_value(get_candidate_names(), _selected_candidate)) {
553  _defaulted = false;
554  }
555  if (_selected_candidate != no_selected_candidate) {
556  auto& c = _candidates.at(_selected_candidate);
557  if (c.opts) {
558  c.opts->mutate(mutator);
559  }
560  }
561  }
562  size_t find_candidate(const std::string& candidate_name) {
563  auto it = find_if(_candidates.begin(), _candidates.end(), [&] (const auto& candidate) {
564  return candidate.name == candidate_name;
565  });
566  if (it == _candidates.end()) {
567  throw std::invalid_argument(fmt::format("find_candidate(): failed to find candidate {}", candidate_name));
568  }
569  return it - _candidates.begin();
570  }
571 
572  option_group* do_select_candidate(std::string candidate_name, bool defaulted) {
573  _selected_candidate = find_candidate(candidate_name);
574  _defaulted = defaulted;
575  return _candidates.at(_selected_candidate).opts.get();
576  }
577 
578 public:
586  selection_value(option_group& group, std::string name, candidates candidates, std::string default_candidate, std::string description)
587  : basic_value(group, true, std::move(name), std::move(description))
588  , _candidates(std::move(candidates))
589  , _selected_candidate(find_candidate(default_candidate))
590  { }
591  selection_value(option_group& group, std::string name, candidates candidates, std::string description)
592  : basic_value(group, true, std::move(name), std::move(description))
593  , _candidates(std::move(candidates))
594  { }
596  selection_value(option_group& group, std::string name, unused)
597  : basic_value(group, false, std::move(name), {})
598  { }
600  operator bool () const { return _selected_candidate != no_selected_candidate; }
602  bool defaulted() const { return _defaulted; }
604  const std::string& get_selected_candidate_name() const { return _candidates.at(_selected_candidate).name; }
606  const option_group* get_selected_candidate_opts() const { return _candidates.at(_selected_candidate).opts.get(); }
608  option_group* get_selected_candidate_opts() { return _candidates.at(_selected_candidate).opts.get(); }
609  T& get_selected_candidate() const { return *_candidates.at(_selected_candidate).value; }
613  option_group* select_candidate(std::string candidate_name) { return do_select_candidate(candidate_name, false); }
617  option_group* select_default_candidate(std::string candidate_name) { return do_select_candidate(candidate_name, true); }
618 };
619 
621 
622 }
623 SEASTAR_MODULE_EXPORT_END
624 
625 }
Definition: program-options.hh:380
Definition: program-options.hh:290
void mutate(options_mutator &mutator)
option_group(option_group *parent, std::string name, unused)
void describe(options_descriptor &descriptor) const
option_group(option_group *parent, std::string name)
Definition: program-options.hh:140
virtual void visit_value(const std::set< unsigned > *default_val)=0
Visit a value (value), default_val is null when value has no default.
virtual void visit_value(const logger_ostream_type *default_val)=0
Visit a value (value), default_val is null when value has no default.
virtual void visit_value(const double *default_val)=0
Visit a value (value), default_val is null when value has no default.
virtual void visit_value(const std::unordered_map< sstring, log_level > *default_val)=0
Visit a value (value), default_val is null when value has no default.
virtual void visit_selection_value(const std::vector< std::string > &candidate_names, const std::size_t *default_candidate)=0
Visit a selection value (selection_value), default_candidate is null when there is no default candida...
virtual bool visit_value_metadata(const std::string &name, const std::string &description, bool used)=0
virtual void visit_value(const log_level *default_val)=0
Visit a value (value), default_val is null when value has no default.
virtual void visit_value(const float *default_val)=0
Visit a value (value), default_val is null when value has no default.
virtual void visit_value(const memory::alloc_failure_kind *default_val)=0
Visit a value (value), default_val is null when value has no default.
virtual void visit_value(const std::string *default_val)=0
Visit a value (value), default_val is null when value has no default.
virtual void visit_value(const unsigned *default_val)=0
Visit a value (value), default_val is null when value has no default.
virtual void visit_value(const int *default_val)=0
Visit a value (value), default_val is null when value has no default.
virtual bool visit_group_start(const std::string &name, bool used)=0
virtual void visit_value()=0
Visit a switch (value<std::monostate>).
virtual void visit_value(const logger_timestamp_style *default_val)=0
Visit a value (value), default_val is null when value has no default.
virtual void visit_value(const bool *default_val)=0
Visit a value (value), default_val is null when value has no default.
Definition: program-options.hh:198
virtual bool visit_value(std::set< unsigned > &val)=0
Visit and optionally mutate a value (value), should return true if value was mutated.
virtual bool visit_value(std::unordered_map< sstring, log_level > &val)=0
Visit and optionally mutate a value (value), should return true if value was mutated.
virtual bool visit_value_metadata(const std::string &name, bool used)=0
virtual bool visit_value(logger_ostream_type &val)=0
Visit and optionally mutate a value (value), should return true if value was mutated.
virtual bool visit_value()=0
Visit a switch (value<std::monostate>), switch is set to returned value.
virtual bool visit_value(double &val)=0
Visit and optionally mutate a value (value), should return true if value was mutated.
virtual bool visit_value(float &val)=0
Visit and optionally mutate a value (value), should return true if value was mutated.
virtual bool visit_group_start(const std::string &name, bool used)=0
virtual bool visit_value(unsigned &val)=0
Visit and optionally mutate a value (value), should return true if value was mutated.
virtual bool visit_selection_value(const std::vector< std::string > &candidate_names, std::size_t &selected_candidate)=0
Visit and optionally mutate a selection value (selection_value), should return true if value was muta...
virtual bool visit_value(memory::alloc_failure_kind &val)=0
Visit and optionally mutate a value (value), should return true if value was mutated.
virtual bool visit_value(logger_timestamp_style &val)=0
Visit and optionally mutate a value (value), should return true if value was mutated.
virtual bool visit_value(int &val)=0
Visit a value (value), default_val is null when value has no default.
virtual bool visit_value(std::string &val)=0
Visit and optionally mutate a value (value), should return true if value was mutated.
virtual bool visit_value(log_level &val)=0
Visit and optionally mutate a value (value), should return true if value was mutated.
virtual bool visit_value(bool &val)=0
Visit and optionally mutate a value (value), should return true if value was mutated.
Definition: program-options.hh:515
const option_group * get_selected_candidate_opts() const
Get the options of the currently selected candidate (assumes there is one selected,...
Definition: program-options.hh:606
selection_value(option_group &group, std::string name, unused)
Construct an unused value.
Definition: program-options.hh:596
option_group * select_candidate(std::string candidate_name)
Definition: program-options.hh:613
option_group * select_default_candidate(std::string candidate_name)
Definition: program-options.hh:617
bool defaulted() const
Is the currently selected candidate the default one?
Definition: program-options.hh:602
selection_value(option_group &group, std::string name, candidates candidates, std::string default_candidate, std::string description)
Definition: program-options.hh:586
option_group * get_selected_candidate_opts()
Get the options of the currently selected candidate (assumes there is one selected,...
Definition: program-options.hh:608
const std::string & get_selected_candidate_name() const
Get the name of the currently selected candidate (assumes there is one selected, see \operator bool()...
Definition: program-options.hh:604
Wrapper for command-line options with arbitrary string associations.
Definition: program-options.hh:71
value(option_group &group, std::string name, unused)
Construct an unused value.
Definition: program-options.hh:495
value(option_group &group, std::string name, std::string description)
Definition: program-options.hh:491
Definition: program-options.hh:414
value(option_group &group, std::string name, std::optional< T > default_value, std::string description)
Definition: program-options.hh:442
bool defaulted() const
Does this value still contain a default-value?
Definition: program-options.hh:454
const T & get_value() const
Return the contained value, assumes there is one, see operator bool().
Definition: program-options.hh:456
value(option_group &group, std::string name, unused)
Construct an unused value.
Definition: program-options.hh:447
A tag type used to construct unused option_group and basic_value objects.
Definition: program-options.hh:254
alloc_failure_kind
The kind of allocation failures to dump diagnostics report for.
Definition: memory_diagnostics.hh:41
Seastar API namespace.
Definition: abort_on_ebadf.hh:26
logger_timestamp_style
Timestamp style.
Definition: log.hh:498
logger_ostream_type
Output stream to use for logging.
Definition: log.hh:505
log_level
log level used with
Definition: log.hh:52