All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
MultipleChoiceSelection.h
Go to the documentation of this file.
1 /**
2  * @file lardataalg/Utilities/MultipleChoiceSelection.h
3  * @brief Helper to select an string option among a set of allowed choices.
4  * @author Gianluca Petrillo (petrillo@slac.stanford.edu)
5  * @date December 13, 2019
6  *
7  */
8 
9 #ifndef LARDATAALG_UTILITIES_MULTIPLECHOICESELECTION_H
10 #define LARDATAALG_UTILITIES_MULTIPLECHOICESELECTION_H
11 
12 // C/C++ standard library
13 #include <algorithm> // std::equal(), std::lexicographical_compare()
14 #include <exception> // std::exception
15 #include <string>
16 #include <vector>
17 #include <ostream>
18 #include <optional>
19 #include <type_traits> // std::conjunction_v, std::is_convertible...
20 #include <cctype> // std::tolower()
21 #include <cstddef> // std::size_t
22 #include <cassert>
23 
24 
25 
26 // -----------------------------------------------------------------------------
27 namespace util::details {
28 
29  // ---------------------------------------------------------------------------
30  template <typename... Strings>
31  constexpr auto AllConvertibleToStrings_v
32  = std::conjunction_v<std::is_convertible<Strings, std::string>...>;
33 
34  // ---------------------------------------------------------------------------
36 
37  /// Returns whether strings `a` and `b` are equal.
38  static bool equal(std::string const& a, std::string const& b);
39 
40  /// Returns whether `a` is lexicographically smaller than `b`.
41  static bool less(std::string const& a, std::string const& b);
42 
43  private:
44  static bool cmp_lower(unsigned char a, unsigned char b);
45  static bool eq_lower(unsigned char a, unsigned char b);
46 
47  }; // struct CaseInsensitiveComparer
48 
49 
50  // ---------------------------------------------------------------------------
51  template <typename Comparer>
52  struct SorterFrom {
53  bool operator() (std::string const& a, std::string const& b) const;
54  }; // struct SorterFrom<>
55 
56 
57  // ---------------------------------------------------------------------------
58  template <typename Value, typename = void> struct ValueToString;
59 
60  // ---------------------------------------------------------------------------
61  /**
62  * @brief Class representing one of the available options to be selected.
63  *
64  * An option has a value (of type `Choices_t`) and a name as a string.
65  * It may also have aliases. The identity of the option is defined by the
66  * value: two option objects with the same value represent the same option.
67  *
68  * Matching a label is encoded in this class: the option matches a label if
69  * its name or any of its aliases matches the proposed label in a
70  * case-insensitive comparison.
71  */
72  template <typename Choices>
74 
76 
77  public:
78  using Choices_t = Choices;
80 
81 
82  /// Constructor: assigns value, name and aliases.
83  template <typename... Aliases>
85  (Choices_t value, std::string name, Aliases... aliases);
86 
87  /// Adds aliases.
88  template <typename... Aliases>
90  addAlias(std::string alias, Aliases... moreAliases);
91 
92  /// Returns whether this option matches the specified label (name or alias).
93  bool match(std::string const& label) const;
94 
95  /// Returns a copy of the value of the option.
96  Choices_t value() const { return fValue; }
97 
98  /// Returns the name of the option (i.e. the main label).
99  std::string name() const { return labels().front(); }
100 
101  /// Returns an iterable object with all the labels of the option.
102  std::vector<std::string> const& labels() const { return fLabels; }
103 
104 
105  /// Returns a string representing this option.
106  operator std::string() const { return name(); }
107 
108  /// Returns the value of this option.
109  operator Choices_t() const { return value(); }
110 
111  /// Returns a string representing the value of the option.
112  ///
113  /// That will be the `name()` of the option if the value is not convertible
114  /// to a string.
115  std::string value_as_string() const;
116 
117  /// Returns a string represent the value of the option, or `defValue`.
118  std::string value_as_string(std::string const& defValue) const;
119 
120  /// Returns in a string the name and all the aliases.
121  std::string dump() const;
122 
123  /// Returns whether the two options are the same (same value and name).
124  bool operator== (Option_t const& option) const
125  { return (value() == option.value()) && equal(name(), option.name()); }
126 
127  /// Returns whether the two options are not the same.
128  bool operator!= (Option_t const& option) const
129  { return (value() != option.value()) || !equal(name(), option.name()); }
130 
131 
132  /// Converts a value of type `Choices_t` into a string, if possible.
133  static std::optional<std::string> value_as_string(Choices_t value);
134 
135  /// Converts a value of type `Choices_t` into a string, if possible.
136  static std::string value_as_string
137  (Choices_t value, std::string const& defValue);
138 
139 
140  private:
141 
142  Choices_t fValue; ///< The value associated to the option.
143  std::vector<std::string> fLabels; ///< All the labels.
144 
145  static bool equal(std::string const& a, std::string const& b)
146  { return Comparer_t::equal(a, b); }
147 
148  }; // MultipleChoiceSelectionOption_t
149 
150 
151  // ---------------------------------------------------------------------------
152 
153 
154 } // namespace util::details
155 
156 
157 // -----------------------------------------------------------------------------
158 namespace util { class MultipleChoiceSelectionBase; }
159 
160 /**
161  * @brief Base class of `util::MultipleChoiceSelection` with basics independent
162  * of the option type.
163  */
165 
166  public:
167 
168  // --- BEGIN -- Exceptions ---------------------------------------------------
169  /// @name Exceptions
170  /// @{
171 
172  struct Exception: std::exception {
173  Exception(std::string const& s): s(s) {}
174  virtual const char* what() const noexcept override { return s.c_str(); }
175 
176  std::string s;
177  }; // Exception
178 
179  /// Request for unknown option.
182  UnknownOptionError(std::string const& label): Exception(label) {}
183 
184  std::string const& label() const { return s; }
185  }; // UnknownOptionError
186 
187  /// Adding an option that already exists.
190  OptionAlreadyExistsError(std::string const& label): Exception(label) {}
191 
192  std::string const& label() const { return s; }
193  }; // OptionAlreadyExistsError
194 
195 
196  /// @}
197  // --- END -- Exceptions -----------------------------------------------------
198 
199 
200 }; // class MultipleChoiceSelectionBase
201 
202 
203 // -----------------------------------------------------------------------------
204 namespace util { template <typename> class MultipleChoiceSelection; }
205 /**
206  * @brief Helper to select one among multiple choices via strings.
207  * @tparam Choices type describing the choices
208  *
209  *
210  * @note If the type to describe the choice is a string, its value still need
211  * to be explicitly added as an option label.
212  *
213  */
214 template <typename Choices>
216 
218 
219 
220  public:
221 
222  using Choices_t = Choices; ///< Type of the underlying choice.
223 
225 
226 
227  // --- BEGIN -- Constructors -------------------------------------------------
228 
229  /// Default constructor: flags are set to `DefaultFlags`, options may be added
230  /// later.
231  MultipleChoiceSelection() = default;
232 
233  /**
234  * @brief Constructor: adds the specified options.
235  * @tparam Options a number of `Option_t` objects
236  * @param options a list of options to add to the selector
237  *
238  * All specified options are added as with `addOption()`.
239  */
240  template <typename... Options>
241  MultipleChoiceSelection(std::initializer_list<Option_t> options);
242 
243  // --- END -- Constructors ---------------------------------------------------
244 
245 
246  // --- BEGIN -- Option management --------------------------------------------
247  /// @name Option management
248  /// @{
249 
250  /// Returns the number of available options.
251  std::size_t size() const;
252 
253  /// Returns whether there is no available option.
254  bool empty() const;
255 
256 
257  /**
258  * @brief Adds a new option to the selector.
259  * @tparam Aliases any number of `std::string`
260  * @param value the value of the new option
261  * @param label the first label to associate to the option
262  * @param aliases additional aliases for this option
263  * @return the newly created option
264  * @throw OptionAlreadyExistsError if there is already an option with `value`
265  * @see `addAlias()`
266  *
267  * An option must always have a label; aliases are instead optional.
268  * There must be no existing option with the specified `value`, otherwise
269  * an exception will be thrown with the label of the existing option.
270  * To add aliases to an existing option, use `addAlias()` instead.
271  * Currently, it is not possible to change a label after having added it.
272  */
273  template <typename... Aliases>
274  Option_t const& addOption
275  (Choices_t value, std::string label, Aliases... aliases);
276 
277 
278  /**
279  * @brief Adds aliases to an existing option.
280  * @tparam Args any number of `std::string`
281  * @param value the value of the option to assign labels to
282  * @param aliases the additional alias(es) to assign to this option
283  * @return the option being changed
284  * @throw UnknownOptionError if no option with `value` is registered yet
285  *
286  */
287  template <typename... Aliases>
289  <details::AllConvertibleToStrings_v<Aliases...>, Option_t const&>
290  addAlias(Choices_t value, Aliases... aliases);
291 
292  /**
293  * @brief Adds labels to an existing option.
294  * @tparam Aliases any number of `std::string`
295  * @param option the option to assign labels to
296  * @param aliases the additional alias(es) to assign to this option
297  * @return the option being changed
298  * @throw UnknownOptionError if no option with `value` is registered yet
299  *
300  * The option with the same value as the `option` argument is assigned the
301  * specified aliases.
302  */
303  template <typename... Aliases>
305  <details::AllConvertibleToStrings_v<Aliases...>, Option_t const&>
306  addAlias(Option_t const& option, Aliases... aliases);
307 
308 
309  /// Returns whether the selector has an option with the specified `value`.
310  bool hasOption(Choices_t value) const;
311 
312  /// Returns whether the selector has an option with the specified `label`.
313  bool hasOption(std::string const& label) const;
314 
315  /// Returns if the specified option is present in the selector (by value).
316  bool hasOption(Option_t const& option) const;
317 
318  /// @}
319  // --- END -- Option management ----------------------------------------------
320 
321 
322 
323  // --- BEGIN -- Option access ------------------------------------------------
324  /// @name Option access
325  /// @{
326 
327  /**
328  * @brief Returns the specified option.
329  * @param value value of the requested option
330  * @return the requested option
331  * @throw UnknownOptionError if there is no available option with `value`
332  * (the string of the exception is empty)
333  */
334  Option_t const& get(Choices_t value) const;
335 
336  /**
337  * @brief Returns the option with the specified label.
338  * @param label label of the requested option
339  * @return the requested option
340  * @throw UnknownOptionError if there is no available option with `label`
341  * (the string of the exception shows `label` value)
342  */
343  Option_t const& get(std::string const& label) const;
344 
345 
346  /**
347  * @brief Returns the option matching the specified label.
348  * @param label label of the requested option
349  * @return the requested option
350  * @throw UnknownOptionError if there is no available option with `label`
351  * (the string of the exception shows `label` value)
352  */
353  Option_t const& parse(std::string const& label) const;
354 
355 
356  /// @}
357  // --- END -- Option access --------------------------------------------------
358 
359 
360  /// --- BEGIN ----------------------------------------------------------------
361  /// @name Presentation and dumping
362  /// @{
363 
364  /// Returns a string with the (main) name of all options.
365  std::string optionListString(std::string const& sep = ", ") const;
366 
367  /// Returns a string with all the options, one per line.
368  std::string optionListDump
369  (std::string const& indent, std::string const& firstIndent) const;
370 
371  /// Returns a string with all the options, one per line.
372  std::string optionListDump(std::string const& indent = "") const
373  { return optionListDump(indent, indent); }
374 
375  /// @}
376  /// --- END ------------------------------------------------------------------
377 
378 
379  private:
380 
381  /// Type of collection of options.
382  using OptionList_t = std::vector<Option_t>;
383 
384  /// Type of label index (associative container: label to option index).
385  using OptionLabelMap_t = std::map<
386  std::string, std::size_t,
388  >;
389 
390 
391  OptionList_t fOptions; ///< The list of registered objects.
392 
393  /// Map from labels to option index in `fOptions`.
395 
396 
397  /**
398  * @brief Moves the specified option into the list of registered options.
399  * @param option the option to move
400  * @return the newly added option
401  * @throw OptionAlreadyExistsError if there is already an option with the same
402  * `value` or any of the labels; in that case,
403  * `label()` method of the exception will
404  * report the offending label of `option` or
405  * its name if it is the value to be
406  * duplicated
407  */
408  Option_t const& addOption(Option_t&& option);
409 
410 
411  /// Associates `label` to the option at `index`.
412  /// @throw OptionAlreadyExistsError if there is already an option with `label`
413  void recordLabel(std::string&& label, std::size_t index);
414 
415  /// Associates all `labels` to the option at `index`.
416  /// @throw OptionAlreadyExistsError if there is already an option with any of
417  /// the aliases
418  template <typename... Aliases>
420  recordLabels(std::size_t index, std::string alias, Aliases... moreAliases);
421 
422  /// Removes the specified label from the register.
423  void unregisterLabel(std::string const& label);
424 
425  /// Retrieves the option with the specified `value`.
426  /// @throw UnknownOptionError if there is no available option with `value`
427  Option_t& get(Choices_t value);
428 
429  /// Returns an iterator to the option with `label`, or `npos` if none.
430  typename OptionList_t::const_iterator findOption(Choices_t value) const;
431 
432  /// Returns an iterator to the option with `label`, or `npos` if none.
433  typename OptionList_t::iterator findOption(Choices_t value);
434 
435  /// Returns the index of the option with `label`, or `npos` if none.
436  std::size_t findOptionIndex(Choices_t value) const;
437 
438  /// Returns the index of the option with `label`, or `npos` if none.
439  std::size_t findOptionIndex(std::string const& label) const;
440 
441  /// Special value.
442  static constexpr auto npos = std::numeric_limits<std::size_t>::max();
443 
444 }; // class util::MultipleChoiceSelection
445 
446 
447 // -----------------------------------------------------------------------------
448 namespace util::details {
449 
450  /// --- BEGIN -- Comparison operators ----------------------------------------
451  /// @name Option comparison operators
452  /// @{
453 
454  //@{
455  /// Returns whether `option` has the specified `value`.
456  template <typename Choices>
457  bool operator== (
458  MultipleChoiceSelectionOption_t<Choices> const& option,
459  Choices const value
460  );
461  template <typename Choices>
462  bool operator== (
463  Choices const value,
464  MultipleChoiceSelectionOption_t<Choices> const& option
465  );
466  //@}
467 
468  //@{
469  /// Returns whether `option` does not have the specified `value`.
470  template <typename Choices>
471  bool operator!= (
472  MultipleChoiceSelectionOption_t<Choices> const& option,
473  Choices const value
474  );
475  template <typename Choices>
476  bool operator!= (
477  Choices const value,
478  MultipleChoiceSelectionOption_t<Choices> const& option
479  );
480  //@}
481 
482  //@{
483  /// Returns whether `option` has the specified name or alias.
484  template <typename Choices>
485  bool operator== (
486  MultipleChoiceSelectionOption_t<Choices> const& option,
487  std::string const& label
488  );
489  template <typename Choices>
490  bool operator== (
491  std::string const& label,
492  MultipleChoiceSelectionOption_t<Choices> const& option
493  );
494  //@}
495 
496  //@{
497  /// Returns whether `option` does not have the specified name or alias.
498  template <typename Choices>
499  bool operator!= (
500  MultipleChoiceSelectionOption_t<Choices> const& option,
501  std::string const& label
502  );
503  template <typename Choices>
504  bool operator!= (
505  std::string const& label,
506  MultipleChoiceSelectionOption_t<Choices> const& option
507  );
508  //@}
509 
510  /// @}
511  /// --- END -- Comparison operators ------------------------------------------
512 
513 
514  /// Prints an option into a stream.
515  template <typename Choices>
516  std::ostream& operator<<
517  (std::ostream& out, MultipleChoiceSelectionOption_t<Choices> const& option);
518 
519 
520 } // namespace util
521 
522 
523 
524 // =============================================================================
525 // --- template implementation
526 // =============================================================================
527 // -----------------------------------------------------------------------------
528 // --- (details)
529 // -----------------------------------------------------------------------------
530 template <typename B1, typename E1, typename B2, typename E2, typename Comp>
531 bool my_lexicographical_compare(B1 b1, E1 e1, B2 b2, E2 e2, Comp less) {
532 
533  while (b1 != e1) {
534  if (b2 == e2) return false; // shorter is less
535 
536  if (less(*b1, *b2)) return true;
537  if (less(*b2, *b1)) return false;
538  // equal so far...
539  ++b1;
540  ++b2;
541  } // while
542  return true; // 1 is shorter
543 } // my_lexicographical_compare()
544 
546  (std::string const& a, std::string const& b)
547  { return std::equal(a.begin(), a.end(), b.begin(), b.end(), eq_lower); }
548 
549 
550 // -----------------------------------------------------------------------------
552  (std::string const& a, std::string const& b)
553 {
554  return std::lexicographical_compare
555  (a.begin(), a.end(), b.begin(), b.end(), cmp_lower);
556 } // util::details::CaseInsensitiveComparer::less()
557 
558 
559 // -----------------------------------------------------------------------------
561  (unsigned char a, unsigned char b)
562  { return std::tolower(a) < std::tolower(b); }
563 
564 
565 // -----------------------------------------------------------------------------
567  (unsigned char a, unsigned char b)
568  { return std::tolower(a) == std::tolower(b); }
569 
570 
571 // -----------------------------------------------------------------------------
572 template <typename Comparer>
574  (std::string const& a, std::string const& b) const
575  { return Comparer::less(a, b); }
576 
577 
578 // -----------------------------------------------------------------------------
579 namespace util::details {
580 
581  template <typename Value, typename /* = void */>
582  struct ValueToString {
583  static constexpr bool can_convert = false;
584 
585  template <typename T>
586  static std::optional<std::string> convert(T const&) { return {}; }
587  }; // struct ValueToString
588 
589  // enumerators
590  template <typename Value>
591  struct ValueToString<Value, std::enable_if_t<std::is_enum_v<Value>>> {
592  static constexpr bool can_convert = true;
593 
594  template <typename T>
595  static std::optional<std::string> convert(T const& value)
596  {
597  return
598  { std::to_string(static_cast<std::underlying_type_t<T>>(value)) };
599  }
600  }; // ValueToString(enum)
601 
602  // whatever converts to `std::to_string`
603  template <typename Value>
604  struct ValueToString<Value,
605  std::enable_if_t<
606  std::is_convertible_v<Value, std::string>
607  || std::is_constructible_v<std::string, Value>
608  >>
609  {
610  static constexpr bool can_convert = true;
611  template <typename T>
612  static std::optional<std::string> convert(T const& value)
613  { return { std::string{ value } }; }
614  }; // ValueToString(string)
615 
616  // whatever supports `std::to_string`
617  template <typename Value>
619  <Value, std::void_t<decltype(std::to_string(std::declval<Value>()))>>
620  {
621  static constexpr bool can_convert = true;
622  template <typename T>
623  static std::optional<std::string> convert(T const& value)
624  { return { std::to_string(value) }; }
625  }; // ValueToString(to_string)
626 
627 } // namespace util::details
628 
629 
630 // -----------------------------------------------------------------------------
631 // --- util::details::MultipleChoiceSelectionOption_t
632 // -----------------------------------------------------------------------------
633 template <typename Choices>
634 template <typename... Aliases>
636  (Choices_t value, std::string name, Aliases... aliases)
637  : fValue(value)
638 {
639  fLabels.reserve(1U + sizeof...(aliases));
640  addAlias(std::move(name), std::move(aliases)...);
641 } // util::details::MultipleChoiceSelectionOption_t<>::MultipleChoiceSelectionOption_t
642 
643 
644 // -----------------------------------------------------------------------------
645 template <typename Choices>
646 template <typename... Aliases>
648  (std::string alias, Aliases... moreAliases)
650 {
651  fLabels.push_back(std::move(alias));
652  if constexpr(sizeof...(moreAliases) > 0)
653  return addAlias(std::move(moreAliases)...);
654  else return *this;
655 } // util::details::MultipleChoiceSelectionOption_t<>::addAlias()
656 
657 
658 // -----------------------------------------------------------------------------
659 template <typename Choices>
661  (std::string const& label) const
662 {
663  return std::find_if(
664  fLabels.begin(), fLabels.end(),
665  [&label](std::string const& alias){ return equal(label, alias); }
666  ) != fLabels.end();
667 } // util::details::MultipleChoiceSelectionOption_t<>::match()
668 
669 
670 // -----------------------------------------------------------------------------
671 template <typename Choices>
672 std::string
674  (std::string const& defValue) const
675  { return value_as_string(value(), defValue); }
676 
677 
678 // -----------------------------------------------------------------------------
679 template <typename Choices>
680 std::string
682  { return value_as_string(name()); }
683 
684 
685 // -----------------------------------------------------------------------------
686 template <typename Choices>
687 std::optional<std::string>
689  (Choices_t value)
690 {
692 } // util::details::MultipleChoiceSelectionOption_t<>::value_as_string()
693 
694 
695 // -----------------------------------------------------------------------------
696 template <typename Choices>
697 std::string
699  (Choices_t value, std::string const& defValue)
700 {
701  return value_as_string(value).value_or(defValue);
702 } // util::details::MultipleChoiceSelectionOption_t<>::value_as_string()
703 
704 
705 // -----------------------------------------------------------------------------
706 template <typename Choices>
708  const
709 {
710  auto iLabel = fLabels.begin();
711  auto const lend = fLabels.end();
712  std::string s { '"' };
713  s += *iLabel;
714  s += '"';
715  auto const valueStr = value_as_string();
716  if (valueStr != *iLabel) {
717  s += " [=";
718  s += valueStr;
719  s += "]";
720  }
721  if (++iLabel != lend) {
722  s += " (aliases: \"";
723  s += *iLabel;
724  s += '"';
725  while (++iLabel != lend) {
726  s += " \"";
727  s += *iLabel;
728  s += '"';
729  } // while
730  s += ')';
731  } // if aliases
732 
733  return s;
734 } // util::details::MultipleChoiceSelectionOption_t<>::dump()
735 
736 
737 // -----------------------------------------------------------------------------
738 template <typename Choices>
739 bool util::details::operator==
741  { return option.value() == value; }
742 
743 template <typename Choices>
744 bool util::details::operator==
746  { return option == value; }
747 
748 
749 // -----------------------------------------------------------------------------
750 template <typename Choices>
751 bool util::details::operator!=
753  { return option.value() != value; }
754 
755 template <typename Choices>
756 bool util::details::operator!=
758  { return option != value; }
759 
760 
761 // -----------------------------------------------------------------------------
762 template <typename Choices>
765  std::string const& label
766  )
767  { return option.match(label); }
768 
769 template <typename Choices>
771  std::string const& label,
773  )
774  { return option == label; }
775 
776 
777 // -----------------------------------------------------------------------------
778 template <typename Choices>
781  std::string const& label
782  )
783  { return !option.match(label); }
784 
785 template <typename Choices>
787  std::string const& label,
789  )
790  { return option != label; }
791 
792 
793 // -----------------------------------------------------------------------------
794 template <typename Choices>
795 std::ostream& util::details::operator<<
797  { out << option.name(); return out; }
798 
799 
800 // -----------------------------------------------------------------------------
801 // --- util::MultipleChoiceSelection
802 // -----------------------------------------------------------------------------
803 template <typename Choices>
804 template <typename... Options>
806  (std::initializer_list<Option_t> options)
807 {
808  for (Option_t option: options) addOption(std::move(option));
809 } // util::MultipleChoiceSelection<>::MultipleChoiceSelection()
810 
811 
812 // -----------------------------------------------------------------------------
813 template <typename Choices>
815  { return fOptions.size(); }
816 
817 
818 // -----------------------------------------------------------------------------
819 template <typename Choices>
821  { return fOptions.empty(); }
822 
823 
824 // -----------------------------------------------------------------------------
825 template <typename Choices>
826 template <typename... Aliases>
828  (Choices_t value, std::string label, Aliases... aliases) -> Option_t const&
829 {
830  return addOption({ value, std::move(label), std::move(aliases)... });
831 } // util::MultipleChoiceSelection<>::addOption()
832 
833 
834 // -----------------------------------------------------------------------------
835 template <typename Choices>
836 template <typename... Aliases>
838  (Choices_t value, Aliases... aliases)
840  <details::AllConvertibleToStrings_v<Aliases...>, Option_t const&>
841 {
842  std::size_t const index = findOptionIndex(value);
843  if (index >= fOptions.size())
844  throw UnknownOptionError(Option_t::value_as_string(value, ""));
845  recordLabels(index, aliases...);
846  return fOptions[index].addAlias(std::move(aliases)...);
847 } // util::MultipleChoiceSelection<>::addAlias()
848 
849 
850 // -----------------------------------------------------------------------------
851 template <typename Choices>
852 template <typename... Aliases>
854  (Option_t const& option, Aliases... aliases)
856  <details::AllConvertibleToStrings_v<Aliases...>, Option_t const&>
857 {
858  return addAlias(option.value(), std::move(aliases)...);
859 } // util::MultipleChoiceSelection<>::addAlias()
860 
861 
862 // -----------------------------------------------------------------------------
863 template <typename Choices>
865  { return findOption(value) != fOptions.end(); }
866 
867 
868 // -----------------------------------------------------------------------------
869 template <typename Choices>
871  (std::string const& label) const
872 {
873  return fLabelToOptionIndex.find(label) != fLabelToOptionIndex.end();
874 } // util::MultipleChoiceSelection<>::hasOption(string)
875 
876 
877 // -----------------------------------------------------------------------------
878 template <typename Choices>
880  (Option_t const& option) const
881  { return hasOption(option.value()); }
882 
883 
884 // -----------------------------------------------------------------------------
885 template <typename Choices>
887  -> Option_t const&
888 {
889  auto const iOption = findOption(value);
890  if (iOption == fOptions.end())
891  throw UnknownOptionError(Option_t::value_as_string(value).value_or(""));
892  return *iOption;
893 } // util::MultipleChoiceSelection<>::get(value)
894 
895 
896 // -----------------------------------------------------------------------------
897 template <typename Choices>
898 auto util::MultipleChoiceSelection<Choices>::get(std::string const& label) const
899  -> Option_t const&
900 {
901  auto const iLabelIndexPair = fLabelToOptionIndex.find(label);
902  if (iLabelIndexPair == fLabelToOptionIndex.end()) {
903  throw UnknownOptionError(label);
904  }
905  assert(iLabelIndexPair->second < fOptions.size());
906  return fOptions[iLabelIndexPair->second];
907 } // util::MultipleChoiceSelection<>::get(string)
908 
909 
910 // -----------------------------------------------------------------------------
911 template <typename Choices>
913  (std::string const& label) const -> Option_t const&
914 {
915  return get(label);
916 } // util::MultipleChoiceSelection<>::parse()
917 
918 
919 // -----------------------------------------------------------------------------
920 template <typename Choices>
922  (std::string const& sep /* = ", " */) const
923 {
924  using namespace std::string_literals;
925 
926  auto iOption = fOptions.begin();
927  auto const oend = fOptions.end();
928 
929  if (iOption == oend) return "<no options>"s;
930 
931  std::string s { *iOption };
932  while (++iOption != oend) {
933  s += sep;
934  s += *iOption;
935  } // while
936  return s;
937 } // util::MultipleChoiceSelection<>::optionListString()
938 
939 
940 // -----------------------------------------------------------------------------
941 template <typename Choices>
943  (std::string const& indent, std::string const& firstIndent) const
944 {
945  using namespace std::string_literals;
946 
947  auto iOption = fOptions.begin();
948  auto const oend = fOptions.end();
949 
950  if (iOption == oend) return firstIndent + "<no options>\n"s;
951 
952  std::string s { firstIndent };
953  s += iOption->dump();
954  s += '\n';
955  while (++iOption != oend) {
956  s += indent;
957  s += iOption->dump();
958  s += '\n';
959  } // while
960  return s;
961 } // util::MultipleChoiceSelection<>::optionListDump()
962 
963 
964 // -----------------------------------------------------------------------------
965 template <typename Choices>
967  -> Option_t const&
968 {
969  std::size_t const newOptionIndex = size();
970 
971  fOptions.push_back(std::move(option));
972  Option_t const& newOption = fOptions.back();
973  auto const& labels = newOption.labels();
974  for (auto iLabel = labels.begin(); iLabel != labels.end(); ++iLabel) {
975  try {
976  recordLabel(std::string{ *iLabel }, newOptionIndex);
977  }
978  catch (OptionAlreadyExistsError const&) {
979  // we attempt to offer a strong guarantee here,
980  // that the object is restored in the state it was before the call
981 
982  // remove the new entries from the index
983  // (*iLabel was not inserted, and all labels before it were new)
984  for (auto iNewLabel = labels.begin(); iNewLabel != iLabel; ++iNewLabel)
985  unregisterLabel(*iNewLabel);
986 
987  // remove the new option from the list
988  fOptions.pop_back();
989 
990  // let the caller handle the rest
991  throw;
992  } // try ... catch
993  } // for labels
994 
995  return newOption;
996 } // util::MultipleChoiceSelection<>::addOption()
997 
998 
999 // -----------------------------------------------------------------------------
1000 template <typename Choices>
1002  (std::string&& label, std::size_t index)
1003 {
1004 
1005  auto const iOption = fLabelToOptionIndex.lower_bound(label);
1006 
1007  // check for duplicate entries: if iOption is not `end()`, it points to an
1008  // element whose key is either greater or equal (equivalent) to `label`;
1009  // if the key is greater than `label`, we are good:
1010  // we check it by requiring that `label` is smaller than the key;
1011  // `key_compare(a, b)` comparison is equivalent to `a < b`,
1012  if ((iOption != fLabelToOptionIndex.end())
1013  && !OptionLabelMap_t::key_compare()(label, iOption->first))
1014  {
1015  throw OptionAlreadyExistsError(label); // maybe too terse?
1016  }
1017 
1018  // the actual recording, at last:
1019  fLabelToOptionIndex.emplace_hint(iOption, std::move(label), index);
1020 
1021 } // util::MultipleChoiceSelection<>::recordLabel()
1022 
1023 
1024 // -----------------------------------------------------------------------------
1025 template <typename Choices>
1026 template <typename... Aliases>
1028  (std::size_t index, std::string alias, Aliases... moreAliases)
1030 {
1031  try {
1032  recordLabel(std::move(alias), index);
1033  if constexpr(sizeof...(moreAliases) > 0U)
1034  recordLabels(index, std::move(moreAliases)...);
1035  }
1036  catch (OptionAlreadyExistsError const&) {
1037  unregisterLabel(alias); // if recordLabel() call threw alias is still intact
1038  throw;
1039  }
1040 } // util::MultipleChoiceSelection<>::recordLabels()
1041 
1042 
1043 // -----------------------------------------------------------------------------
1044 template <typename Choices>
1046  (std::string const& label)
1047  { fLabelToOptionIndex.erase(label); }
1048 
1049 
1050 // -----------------------------------------------------------------------------
1051 template <typename Choices>
1053  -> typename OptionList_t::const_iterator
1054 {
1055  auto const matchValue
1056  = [value](Option_t const& option){ return option.value() == value; };
1057  return std::find_if(fOptions.begin(), fOptions.end(), matchValue);
1058 } // util::MultipleChoiceSelection<>::findOption(value) const
1059 
1060 
1061 // -----------------------------------------------------------------------------
1062 template <typename Choices>
1064  -> typename OptionList_t::iterator
1065 {
1066  auto const matchValue
1067  = [value](Option_t const& option){ return option.value() == value; };
1068  return std::find_if(fOptions.begin(), fOptions.end(), matchValue);
1069 } // util::MultipleChoiceSelection<>::findOption(value)
1070 
1071 
1072 // -----------------------------------------------------------------------------
1073 template <typename Choices>
1075  (Choices_t value) const
1076 {
1077  auto const d = static_cast<std::size_t>
1078  (std::distance(fOptions.begin(), findOption(value)));
1079  return (d >= size())? npos: d;
1080 } // util::MultipleChoiceSelection<>::findOptionIndex(value) const
1081 
1082 
1083 // -----------------------------------------------------------------------------
1084 template <typename Choices>
1086  (std::string const& label) const
1087 {
1088  auto const iOption = fLabelToOptionIndex.find(label);
1089  return (iOption == fLabelToOptionIndex.end())? npos: iOption->second;
1090 } // util::MultipleChoiceSelection<>::findOptionIndex(string) const
1091 
1092 
1093 // -----------------------------------------------------------------------------
1094 template <typename Choices>
1096  auto const iOption = findOption(value);
1097  if (iOption == fOptions.end()) {
1098  throw UnknownOptionError(Option_t::value_as_string(value).value_or(""));
1099  }
1100  return *iOption;
1101 } // util::MultipleChoiceSelection<>::get()
1102 
1103 
1104 // -----------------------------------------------------------------------------
1105 
1106 // =============================================================================
1107 
1108 
1109 #endif // LARDATAALG_UTILITIES_MULTIPLECHOICESELECTION_H
Choices Choices_t
Type of the underlying choice.
bool my_lexicographical_compare(B1 b1, E1 e1, B2 b2, E2 e2, Comp less)
double std(const std::vector< short > &wf, const double ped_mean, size_t start, size_t nsample)
Definition: UtilFunc.cxx:42
* labels
bool hasOption(Choices_t value) const
Returns whether the selector has an option with the specified value.
std::vector< std::string > const & labels() const
Returns an iterable object with all the labels of the option.
std::enable_if_t< AllConvertibleToStrings_v< Aliases...>, Option_t & > addAlias(std::string alias, Aliases...moreAliases)
Adds aliases.
OptionLabelMap_t fLabelToOptionIndex
Map from labels to option index in fOptions.
static std::optional< std::string > convert(T const &)
Option_t const & parse(std::string const &label) const
Returns the option matching the specified label.
bool operator==(Option_t const &option) const
Returns whether the two options are the same (same value and name).
static bool cmp_lower(unsigned char a, unsigned char b)
std::string optionListString(std::string const &sep=", ") const
Returns a string with the (main) name of all options.
void recordLabel(std::string &&label, std::size_t index)
std::size_t size(FixedBins< T, C > const &) noexcept
Definition: FixedBins.h:561
Base class of util::MultipleChoiceSelection with basics independent of the option type...
static constexpr auto npos
Special value.
Option_t const & addOption(Choices_t value, std::string label, Aliases...aliases)
Adds a new option to the selector.
std::enable_if_t< details::AllConvertibleToStrings_v< Aliases...> > recordLabels(std::size_t index, std::string alias, Aliases...moreAliases)
bool operator!=(Option_t const &option) const
Returns whether the two options are not the same.
Class representing one of the available options to be selected.
process_name gaushit a
Choices_t fValue
The value associated to the option.
std::vector< std::string > fLabels
All the labels.
std::vector< Option_t > OptionList_t
Type of collection of options.
static bool eq_lower(unsigned char a, unsigned char b)
std::string optionListDump(std::string const &indent="") const
Returns a string with all the options, one per line.
MultipleChoiceSelectionOption_t(Choices_t value, std::string name, Aliases...aliases)
Constructor: assigns value, name and aliases.
double distance(geo::Point_t const &point, CathodeDesc_t const &cathode)
Returns the distance of a point from the cathode.
Option_t const & get(Choices_t value) const
Returns the specified option.
typename std::enable_if< B, T >::type enable_if_t
Definition: json.hpp:2191
std::map< std::string, std::size_t, details::SorterFrom< details::CaseInsensitiveComparer > > OptionLabelMap_t
Type of label index (associative container: label to option index).
void unregisterLabel(std::string const &label)
Removes the specified label from the register.
std::string name() const
Returns the name of the option (i.e. the main label).
static bool equal(std::string const &a, std::string const &b)
bool operator()(std::string const &a, std::string const &b) const
bool equal(double a, double b)
Comparison tolerance, in centimeters.
constexpr auto AllConvertibleToStrings_v
OptionList_t::const_iterator findOption(Choices_t value) const
Returns an iterator to the option with label, or npos if none.
std::string dump() const
Returns in a string the name and all the aliases.
bool empty() const
Returns whether there is no available option.
std::string optionListDump(std::string const &indent, std::string const &firstIndent) const
Returns a string with all the options, one per line.
std::string to_string(WindowPattern const &pattern)
then echo File list $list not found else cat $list while read file do echo $file sed s
Definition: file_to_url.sh:60
typename make_void< Ts...>::type void_t
Definition: json.hpp:2267
static bool less(std::string const &a, std::string const &b)
Returns whether a is lexicographically smaller than b.
bool match(std::string const &label) const
Returns whether this option matches the specified label (name or alias).
then echo echo For and will not be changed by echo further linking echo echo B echo The symbol is in the uninitialized data multiple common symbols may appear with the echo same name If the symbol is defined the common echo symbols are treated as undefined references For more echo details on common see the discussion of warn common echo in *Note Linker options
bool operator!=(infinite_endcount_iterator< T > const &, count_iterator< T > const &)
Never admit a infinite_endcount_iterator to be equal to anything else.
Definition: counter.h:259
std::size_t findOptionIndex(Choices_t value) const
Returns the index of the option with label, or npos if none.
Helper to select one among multiple choices via strings.
then echo fcl name
echo Invalid option
Definition: TrainMVA.sh:17
std::size_t size() const
Returns the number of available options.
static bool equal(std::string const &a, std::string const &b)
Returns whether strings a and b are equal.
temporary value
std::enable_if_t< details::AllConvertibleToStrings_v< Aliases...>, Option_t const & > addAlias(Choices_t value, Aliases...aliases)
Adds aliases to an existing option.
bool operator==(infinite_endcount_iterator< T > const &, count_iterator< T > const &)
Definition: counter.h:269
physics pm2 e1
TimeTrackTreeStorage::TriggerInputSpec_t convert(TimeTrackTreeStorage::Config::TriggerSpecConfig const &config)
Choices_t value() const
Returns a copy of the value of the option.
virtual const char * what() const noexceptoverride
OptionList_t fOptions
The list of registered objects.