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>
89  std::enable_if_t<AllConvertibleToStrings_v<Aliases...>, Option_t&>
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 
173  Exception(std::string const& s): s(s) {}
174  virtual const char* what() const noexcept override { return s.c_str(); }
175 
177  }; // Exception
178 
179  /// Request for unknown option.
183 
184  std::string const& label() const { return s; }
185  }; // UnknownOptionError
186 
187  /// Adding an option that already exists.
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>
288  std::enable_if_t
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>
304  std::enable_if_t
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.
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>
419  std::enable_if_t<details::AllConvertibleToStrings_v<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)
649  -> std::enable_if_t<AllConvertibleToStrings_v<Aliases...>, Option_t&>
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>
674  (std::string const& defValue) const
675  { return value_as_string(value(), defValue); }
676 
677 
678 // -----------------------------------------------------------------------------
679 template <typename Choices>
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>
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==
740  (MultipleChoiceSelectionOption_t<Choices> const& option, Choices const value)
741  { return option.value() == value; }
742 
743 template <typename Choices>
744 bool util::details::operator==
745  (Choices const value, MultipleChoiceSelectionOption_t<Choices> const& option)
746  { return option == value; }
747 
748 
749 // -----------------------------------------------------------------------------
750 template <typename Choices>
751 bool util::details::operator!=
752  (MultipleChoiceSelectionOption_t<Choices> const& option, Choices const value)
753  { return option.value() != value; }
754 
755 template <typename Choices>
756 bool util::details::operator!=
757  (Choices const value, MultipleChoiceSelectionOption_t<Choices> const& option)
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<<
796  (std::ostream& out, MultipleChoiceSelectionOption_t<Choices> const& option)
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)
839  -> std::enable_if_t
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)
855  -> std::enable_if_t
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>
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)
1029  -> std::enable_if_t<details::AllConvertibleToStrings_v<Aliases...>>
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
static QCString name
Definition: declinfo.cpp:673
intermediate_table::iterator iterator
std::enable_if_t< details::AllConvertibleToStrings_v< Aliases... >, Option_t const & > addAlias(Choices_t value, Aliases...aliases)
Adds aliases to an existing option.
bool my_lexicographical_compare(B1 b1, E1 e1, B2 b2, E2 e2, Comp less)
Namespace for general, non-LArSoft-specific utilities.
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.
std::string string
Definition: nybbler.cc:12
static std::optional< std::string > convert(T const &)
Option_t const & parse(std::string const &label) const
Returns the option matching the specified label.
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)
STL namespace.
Base class of util::MultipleChoiceSelection with basics independent of the option type...
Choices Choices_t
Type of the underlying choice.
intermediate_table::const_iterator const_iterator
std::vector< Option_t > OptionList_t
Type of collection of options.
Class representing one of the available options to be selected.
Choices_t fValue
The value associated to the option.
std::vector< std::string > fLabels
All the labels.
decltype(auto) constexpr size(T &&obj)
ADL-aware version of std::size.
Definition: StdUtils.h:92
static bool eq_lower(unsigned char a, unsigned char b)
MultipleChoiceSelectionOption_t(Choices_t value, std::string name, Aliases...aliases)
Constructor: assigns value, name and aliases.
Option_t const & get(Choices_t value) const
Returns the specified option.
std::string optionListDump(std::string const &indent="") const
Returns a string with all the options, one per line.
const double a
def move(depos, offset)
Definition: depos.py:107
def convert(inputfile, outputfile="wire-cell-garfield-fine-response.json.bz2", average=False, shaped=False)
Definition: garfield.py:262
OptionLabelMap_t fLabelToOptionIndex
Map from labels to option index in fOptions.
def dump(input_file, output_file)
Definition: dumpTree.py:102
Definition: 018_def.c:13
double distance(double x1, double y1, double z1, double x2, double y2, double z2)
static int max(int a, int b)
void unregisterLabel(std::string const &label)
Removes the specified label from the register.
Option_t const & addOption(Choices_t value, std::string label, Aliases...aliases)
Adds a new option to the selector.
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)
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.
OptionList_t fOptions
The list of registered objects.
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.
Definition: 018_def.c:13
std::map< std::string, std::size_t, details::SorterFrom< details::CaseInsensitiveComparer > > OptionLabelMap_t
Type of label index (associative container: label to option index).
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).
static bool * b
Definition: config.cpp:1043
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.
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.
std::vector< std::string > Strings
std::enable_if_t< details::AllConvertibleToStrings_v< Aliases... > > recordLabels(std::size_t index, std::string alias, Aliases...moreAliases)
virtual const char * what() const noexcept override
bool operator==(infinite_endcount_iterator< T > const &, count_iterator< T > const &)
Definition: counter.h:269
std::string to_string(ModuleType const mt)
Definition: ModuleType.h:34
static QCString * s
Definition: config.cpp:1042
cet::coded_exception< error, detail::translate > exception
Definition: exception.h:33
decltype(auto) constexpr empty(T &&obj)
ADL-aware version of std::empty.
Definition: StdUtils.h:97
Choices_t value() const
Returns a copy of the value of the option.