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