LArSoft  v09_90_00
Liquid Argon Software toolkit - https://larsoft.org/
ParameterSet.cc
Go to the documentation of this file.
1 // ======================================================================
2 //
3 // ParameterSet
4 //
5 // ======================================================================
6 
8 #include "cetlib/container_algorithms.h"
17 #include "fhiclcpp/parse.h"
18 
19 #include <cstddef>
20 #include <stack>
21 
22 using namespace fhicl;
23 using namespace fhicl::detail;
24 using namespace std;
25 using namespace std::string_literals;
26 
27 using std::any;
28 using std::any_cast;
29 
33 using ldbl = long double;
34 
35 // ======================================================================
36 
37 namespace {
38 
39  // See notes for 'put' specialization below
40  void
41  fill_src_info(extended_value const& value,
42  std::string const& key,
43  ParameterSet::annot_t& src_map)
44  {
45  src_map[key] = value.src_info;
46  if (!value.is_a(SEQUENCE))
47  return;
48  std::size_t i{};
49  for (auto const& xval : extended_value::sequence_t(value)) {
50  std::ostringstream oss;
51  oss << key << "[" << i++ << "]";
52  src_map[oss.str()] = xval.src_info;
53  fill_src_info(xval, oss.str(), src_map);
54  }
55  }
56 
57  ParameterSet const&
58  get_pset_via_any(std::any const& a)
59  {
60  ParameterSetID const& psid = std::any_cast<ParameterSetID>(a);
61  return ParameterSetRegistry::get(psid);
62  }
63 }
64 
65 // ----------------------------------------------------------------------
66 
69 {
70  ParameterSet result;
71  for (auto const& [key, value] : tbl) {
72  if (!value.in_prolog)
73  result.put(key, value);
74  }
75  return result;
76 }
77 
78 // ----------------------------------------------------------------------
79 
82 {
83  if (!xval.is_a(TABLE))
84  throw fhicl::exception(type_mismatch, "extended value not a table");
85 
86  ParameterSet result;
87  auto const& tbl = table_t(xval);
88  for (auto const& [key, value] : tbl) {
89  if (!value.in_prolog)
90  result.put(key, value);
91  }
92  return result;
93 }
94 
95 // ----------------------------------------------------------------------
96 
98 fhicl::ParameterSet::make(std::string const& str)
99 {
100  auto const tbl = parse_document(str);
101  return ParameterSet::make(tbl);
102 }
103 
104 // ----------------------------------------------------------------------
105 
107 fhicl::ParameterSet::make(std::string const& filename,
108  cet::filepath_maker& maker)
109 {
110  auto const tbl = parse_document(filename, maker);
111  return ParameterSet::make(tbl);
112 }
113 
114 // ======================================================================
115 
116 string
117 ParameterSet::stringify_(any const& a, bool const compact) const
118 {
119  string result;
120  if (is_table(a)) {
121  auto const& psid = any_cast<ParameterSetID>(a);
122  result = '{' + ParameterSetRegistry::get(psid).to_string() + '}';
123  if (compact && result.size() > (5 + ParameterSetID::max_str_size())) {
124  // Replace with a reference to the ParameterSetID;
125  result = std::string("@id::") + psid.to_string();
126  }
127  } else if (is_sequence(a)) {
128  auto const& seq = any_cast<ps_sequence_t>(a);
129  result = '[';
130  if (!seq.empty()) {
131  result.append(stringify_(*seq.begin(), compact));
132  for (auto it = seq.cbegin(), e = seq.cend(); ++it != e;) {
133  result.append(1, ',').append(stringify_(*it, compact));
134  }
135  }
136  result.append(1, ']');
137  } else { // is_atom(a)
138  ps_atom_t const str = any_cast<ps_atom_t>(a);
139  result = str == string(9, '\0') ? "@nil" : str;
140  }
141  return result;
142 } // stringify_()
143 
144 // ----------------------------------------------------------------------
145 
146 bool
148 {
149  return mapping_.empty();
150 }
151 
154 {
155  if (!id_.is_valid()) {
156  id_.reset(*this);
157  }
158  return id_;
159 }
160 
161 string
162 ParameterSet::to_string_(bool const compact) const
163 {
164  string result;
165  if (mapping_.empty()) {
166  return result;
167  }
168  auto it = mapping_.begin();
169  result.append(it->first).append(1, ':').append(
170  stringify_(it->second, compact));
171  for (auto const e = mapping_.end(); ++it != e;) {
172  result.append(1, ' ').append(it->first).append(1, ':').append(
173  stringify_(it->second, compact));
174  }
175  return result;
176 }
177 
178 vector<string>
180 {
181  vector<string> keys;
182  cet::transform_all(mapping_, std::back_inserter(keys), [](auto const& pr) {
183  return pr.first;
184  });
185  return keys;
186 }
187 
188 vector<string>
190 {
191  vector<string> keys;
192  for (auto const& [key, value] : mapping_) {
193  if (is_table(value)) {
194  keys.push_back(key);
195  }
196  }
197  return keys;
198 }
199 
200 vector<string>
202 {
203  KeyAssembler ka;
204  walk(ka);
205  return ka.result();
206 }
207 
208 bool
209 ParameterSet::find_one_(std::string const& simple_key) const
210 {
211  auto skey = detail::get_sequence_indices(simple_key);
212 
213  auto it = mapping_.find(skey.name());
214  if (it == mapping_.end()) {
215  return false;
216  }
217 
218  auto a = it->second;
219  return detail::find_an_any(skey.indices().cbegin(), skey.indices().cend(), a);
220 }
221 
222 std::optional<ParameterSet>
223 ParameterSet::descend_(std::vector<std::string> const& names) const
224 {
225  if (empty(names)) {
226  return std::make_optional(*this);
227  }
228  ParameterSet const* p{this};
229  std::optional<ParameterSet> result;
230  for (auto const& name : names) {
231  if (!p->find_one_(name)) {
232  return std::nullopt;
233  }
234  if (!p->is_key_to_table(name)) {
235  return std::nullopt;
236  }
237 
238  if (result = p->get_one_<ParameterSet>(name); not result) {
239  return std::nullopt;
240  }
241  p = &result.value();
242  }
243  return result;
244 }
245 
246 bool
247 ParameterSet::has_key(std::string const& key) const
248 {
249  auto keys = detail::get_names(key);
250  auto ps = descend_(keys.tables());
251  return ps ? ps->find_one_(keys.last()) : false;
252 }
253 
254 // ----------------------------------------------------------------------
255 
256 std::string
257 ParameterSet::get_src_info(std::string const& key) const
258 {
259  auto result = srcMapping_.find(key);
260  return result != srcMapping_.cend() ? result->second : "";
261 }
262 
263 // ----------------------------------------------------------------------
264 
265 void
266 ParameterSet::put(std::string const& key)
267 {
268  put(key, nullptr);
269 }
270 
271 void
272 ParameterSet::put_or_replace(std::string const& key)
273 {
274  put_or_replace(key, nullptr); // Replace with nil is always OK.
275 }
276 
277 // ----------------------------------------------------------------------
278 
279 namespace {
280  inline void
281  check_put_local_key(std::string const& key)
282  {
283  if (key.find('.') != std::string::npos) {
284  throw fhicl::exception(unimplemented, "putXXX() for nested key.");
285  }
286  }
287 }
288 
289 void
290 ParameterSet::insert_(string const& key, any const& value)
291 {
292  check_put_local_key(key);
293  if (!mapping_.emplace(key, value).second) {
294  throw exception(cant_insert) << "key " << key << " already exists.";
295  }
296  id_.invalidate();
297 }
298 
299 void
300 ParameterSet::insert_or_replace_(string const& key, any const& value)
301 {
302  check_put_local_key(key);
303  mapping_[key] = value;
304  id_.invalidate();
305 }
306 
307 void
309 {
310  check_put_local_key(key);
311  auto item = mapping_.find(key);
312  if (item == mapping_.end()) {
313  insert_(key, value);
314  return;
315  } else {
316  if (!detail::is_nil(value)) {
317  auto is_non_nil_atom = [](any const& v) {
318  return !(detail::is_sequence(v) || detail::is_table(v) ||
319  detail::is_nil(v));
320  };
321  if (detail::is_sequence(item->second) && !detail::is_sequence(value)) {
322  throw exception(cant_insert)
323  << "can't use non-sequence to replace sequence.";
324  } else if (detail::is_table(item->second) && !detail::is_table(value)) {
325  throw exception(cant_insert) << "can't use non-table to replace table.";
326  } else if (is_non_nil_atom(item->second) &&
327  (detail::is_sequence(value) || detail::is_table(value))) {
328  throw exception(cant_insert)
329  << "can't use non-atom to replace non-nil atom.";
330  }
331  }
332  item->second = value;
333  }
334  id_.invalidate();
335 }
336 
337 bool
338 ParameterSet::erase(string const& key)
339 {
340  bool const did_erase{1u == mapping_.erase(key)};
341  id_.invalidate();
342  return did_erase;
343 }
344 
345 bool
346 ParameterSet::key_is_type_(std::string const& key,
347  std::function<bool(std::any const&)> func) const
348 {
349  auto split_keys = detail::get_names(key);
350  auto ps = descend_(split_keys.tables());
351  if (not ps) {
352  throw exception(error::cant_find, key);
353  }
354 
355  auto skey = detail::get_sequence_indices(split_keys.last());
356 
357  auto it = ps->mapping_.find(skey.name());
358  if (it == ps->mapping_.end()) {
359  throw exception(error::cant_find, key);
360  }
361 
362  auto a = it->second;
363  return detail::find_an_any(
364  skey.indices().cbegin(), skey.indices().cend(), a) ?
365  func(a) :
366  throw exception(error::cant_find, key);
367 }
368 
369 // ======================================================================
370 // 'put' specialization for extended_value
371 //
372 // With this specialization, the free function 'fill_src_info' is
373 // called, which fills an std::unordered_map whose key-value pairs
374 // correspond to the ParameterSet key and the location
375 // (filename:line#) where the key was last overridden.
376 //
377 // The main benefit of 'fill_src_info' is that it appropriately tracks
378 // the source information for individual sequence entries. This is
379 // possible because each entry from a 'sequence_t' in the intermediate
380 // table is an extended_value that has a data member 'src_info'. Note
381 // that whenever a printout is provided, the extended_value instances
382 // are no longer used, but only the mapping_ key-value pairs, which
383 // are the ParameterSet names and associated std::any objects.
384 //
385 // ParameterSet instances therefore do not have a natural way of
386 // storing source information for sequence entries because the
387 // extended-value information is lost. In other words, simply doing
388 //
389 // srcMapping_[key] = value.src_info;
390 //
391 // whenever 'put' is called will insert the source information for the
392 // sequence, but not for each sequence entry. To get around this, the
393 // 'fill_src_info' function appends an index number to the end of the
394 // sequence key (e.g. "sequence_key.1"). It is called recursively to
395 // allow for nested sequences.
396 //
397 // In order to access the correct source information for individual
398 // sequence entries in 'Prettifier::stringify()', the sequence
399 // index(es) must be appended to the sequence key. Care must be taken
400 // to ensure that the correct ParameterSet is used to lookup the
401 // source information. This is accomplished by using a stack of the
402 // form:
403 //
404 // std::vector<ParameterSet const*>
405 
406 namespace fhicl {
407  template <>
408  void
409  ParameterSet::put(std::string const& key, fhicl::extended_value const& value)
410  {
411  auto insert = [this, &value](auto const& key) {
412  using detail::encode;
413  this->insert_(key, std::any(encode(value)));
414  fill_src_info(value, key, srcMapping_);
415  };
416  detail::try_insert(insert, key);
417  }
418 }
419 
420 // ======================================================================
421 
422 void
424 {
425  std::stack<ParameterSet const*> ps_stack;
426  ps_stack.push(this);
427 
428  std::function<void(std::string const&, std::any const&)> act_on_element =
429  [&psw, &ps_stack, &act_on_element](std::string const& key,
430  std::any const& a) {
431  auto const* ps = ps_stack.top();
432  psw.do_before_action(key, a, ps);
433 
434  if (is_table(a)) {
435  ParameterSet const* ps = &get_pset_via_any(a);
436  ps_stack.push(ps);
437  psw.do_enter_table(key, a);
438  for (auto const& [nested_key, nested_a] : ps->mapping_) {
439  act_on_element(nested_key, nested_a);
440  }
441  psw.do_exit_table(key, a);
442  ps_stack.pop();
443  } else if (is_sequence(a)) {
444  psw.do_enter_sequence(key, a);
445  std::size_t i{};
446  for (auto const& elem : any_cast<ps_sequence_t>(a)) {
447  std::string const new_key = key + "["s + std::to_string(i++) + "]";
448  act_on_element(new_key, elem);
449  }
450  psw.do_exit_sequence(key, a);
451  } else {
452  psw.do_atom(key, a);
453  }
454 
455  psw.do_after_action(key);
456  };
457 
458  for (auto const& [key, value] : mapping_) {
459  act_on_element(key, value);
460  }
461 }
462 
463 //========================================================================
464 
465 string
467 {
468  return to_indented_string(0u);
469 }
470 
471 string
472 ParameterSet::to_indented_string(unsigned const initial_indent_level) const
473 {
474  return to_indented_string(initial_indent_level, false);
475 }
476 
477 string
478 ParameterSet::to_indented_string(unsigned const initial_indent_level,
479  bool const annotate) const
480 {
481  if (annotate) {
482  return to_indented_string(initial_indent_level, print_mode::annotated);
483  }
484  return to_indented_string(initial_indent_level, print_mode::raw);
485 }
486 
487 string
488 ParameterSet::to_indented_string(unsigned const initial_indent_level,
489  print_mode const pm) const
490 {
491  std::string result;
492  switch (pm) {
493  case print_mode::raw: {
494  Prettifier p{initial_indent_level};
495  walk(p);
496  result = p.result();
497  break;
498  }
499  case print_mode::annotated: {
500  PrettifierAnnotated p{initial_indent_level};
501  walk(p);
502  result = p.result();
503  break;
504  }
507  walk(p);
508  result = p.result();
509  break;
510  }
511  }
512  return result;
513 }
514 
515 // ======================================================================
516 // Explicit instantiations for commonly used types
517 
518 #define _INSTANTIATE_GET(FHICL_TYPE, T) \
519  template _DECODE_##FHICL_TYPE##_(T); \
520  template _GET_ONE_(T); \
521  template _GET(T); \
522  template _GET_WITH_DEFAULT(T); \
523  template _GET_IF_PRESENT(T)
524 
525 _INSTANTIATE_GET(ATOM, bool);
526 _INSTANTIATE_GET(ATOM, int);
527 _INSTANTIATE_GET(ATOM, unsigned);
528 _INSTANTIATE_GET(ATOM, float);
529 _INSTANTIATE_GET(ATOM, double);
530 _INSTANTIATE_GET(ATOM, std::string);
532 
533 // Instantiate std::vector of same types except 'bool'.
534 _INSTANTIATE_GET(SEQUENCE, std::vector<int>);
535 _INSTANTIATE_GET(SEQUENCE, std::vector<unsigned>);
536 _INSTANTIATE_GET(SEQUENCE, std::vector<float>);
537 _INSTANTIATE_GET(SEQUENCE, std::vector<double>);
538 _INSTANTIATE_GET(SEQUENCE, std::vector<std::string>);
539 _INSTANTIATE_GET(SEQUENCE, std::vector<fhicl::ParameterSet>);
void do_before_action(key_t const &k, any_t const &a, ParameterSet const *ps)
bool is_a(value_tag const t) const noexcept
ps_atom_t encode(std::string const &)
Definition: coding.cc:83
static ParameterSet make(intermediate_table const &tbl)
Definition: ParameterSet.cc:68
long double ldbl
Definition: coding.h:46
static collection_type const & get() noexcept
Keys get_names(std::string const &key)
std::unordered_map< std::string, std::string > annot_t
Definition: ParameterSet.h:40
intermediate_table::table_t table_t
Definition: ParameterSet.cc:32
STL namespace.
void do_enter_sequence(key_t const &k, any_t const &a)
void do_exit_table(key_t const &k, any_t const &a)
std::vector< std::any > ps_sequence_t
Definition: coding.h:45
std::vector< std::string > get_pset_names() const
void try_insert(L l, std::string const &key)
Definition: try_blocks.h:11
decltype(auto) constexpr to_string(T &&obj)
ADL-aware version of std::to_string.
#define _INSTANTIATE_GET(FHICL_TYPE, T)
parameter set interface
std::vector< extended_value > sequence_t
static constexpr std::size_t max_str_size() noexcept
std::string to_indented_string() const
fhicl::detail::ps_atom_t ps_atom_t
Definition: ParameterSet.h:38
void insert_(std::string const &key, std::any const &value)
std::optional< ParameterSet > descend_(std::vector< std::string > const &names) const
void insert_or_replace_compatible_(std::string const &key, std::any const &value)
void walk(ParameterSetWalker &psw) const
bool has_key(std::string const &key) const
bool is_nil(std::any const &val)
Definition: coding.cc:71
bool find_an_any(std::vector< std::size_t >::const_iterator it, std::vector< std::size_t >::const_iterator const cend, std::any &a)
void do_after_action(key_t const &k)
SequenceKey get_sequence_indices(std::string const &key)
std::vector< std::string > get_all_keys() const
fhicl::detail::ps_sequence_t ps_sequence_t
Definition: ParameterSet.h:39
double value
Definition: spectrum.C:18
ParameterSetID id() const
extended_value::table_t table_t
void do_exit_sequence(key_t const &k, any_t const &a)
std::vector< key_t > const & result()
Definition: KeyAssembler.h:75
bool is_empty() const
bool key_is_type_(std::string const &key, std::function< bool(std::any const &)> func) const
std::string get_src_info(std::string const &key) const
intermediate_table parse_document(std::string const &filename, cet::filepath_maker &maker)
Definition: parse.cc:709
std::string ps_atom_t
Definition: coding.h:44
void reset(ParameterSet const &)
std::vector< std::string > get_names() const
std::string stringify_(std::any const &a, bool compact=false) const
bool is_table(std::any const &val)
Definition: coding.h:55
bool erase(std::string const &key)
void do_atom(key_t const &k, any_t const &a)
bool find_one_(std::string const &key) const
Float_t e
Definition: plot.C:35
void put_or_replace(std::string const &key)
void put(std::string const &key)
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:109
bool is_sequence(std::any const &val)
Definition: coding.h:49
void do_enter_table(key_t const &k, any_t const &a)
std::string to_string_(bool compact=false) const
void insert_or_replace_(std::string const &key, std::any const &value)