Reactions  0.1.2
Handling reaction trees and decays
database.hpp
Go to the documentation of this file.
1 
4 #pragma once
5 #include <fstream>
6 #include <ios>
7 #include <limits>
8 #include <tuple>
9 #include <vector>
10 
11 #include "reactions/exceptions.hpp"
12 #include "reactions/fields.hpp"
13 
16 
20  template <class Element, class NameField, class IdField> class database {
21 
22  public:
23  using element_type = Element;
24 
25  virtual ~database() {}
26 
37 
39 
40  switch (m_cache.status()) {
41 
42  case (cache::full):
43 
44  out.insert(out.end(), m_cache.begin(), m_cache.end());
45  return out;
46 
47  default:
48 
49  // open the database to count the number of lines
50  auto file = open_database();
51 
52  auto start = skip_commented_lines(file);
53 
54  std::size_t count = 0;
55  while (file.ignore(element_type::line_size + 1)) // include end-of-line
56  ++count;
57 
58  file.clear(); // we reached the end of the file
59 
60  // go back to the start of the table and read the elements
61  file.seekg(start);
62 
63  out.reserve(count + m_cache.size());
64 
65  for (auto i = 0u; i < count; ++i) {
66  std::string line;
67  std::getline(file, line);
68  out.emplace_back(read_element(line));
69  }
70 
71  if (m_cache.size())
72  out.insert(out.end(), m_cache.begin(), m_cache.end());
73 
74  file.close();
75  }
76 
77  return out;
78  }
79 
81  void clear_cache() { m_cache.clear(); }
82 
85 
86  /* \brief Enable the internal cache.
87  *
88  * All the values in the database will be read an stored in a internal
89  * cache. This will speed-up the code, with the caveat of consuming some
90  * memory.
91  */
92  void enable_cache() {
93 
94  if (m_cache.status() == cache::full)
95  return;
96 
97  // open the database to count the number of lines
98  auto file = open_database();
99 
100  auto start = skip_commented_lines(file);
101 
102  std::size_t count = 0;
103  while (file.ignore(element_type::line_size + 1)) // include end-of-line
104  ++count;
105 
106  file.clear(); // we reached the end of the file
107 
108  // go back to the start of the table and read the elements
109  file.seekg(start);
110 
111  std::string line;
113  [this, &file, &line]() -> element_type {
114  std::getline(file, line);
115  return read_element(line);
116  });
117 
118  file.close();
119  }
120 
122  std::string const &get_database_path() const { return m_db; }
123 
130  template <class... Args> void register_element(Args &&...args) {
131 
132  element_type new_element{std::forward<Args>(args)...};
133 
134  // If the cache is enabled, the checks are done within the cache object,
135  // otherwise we must check the database
136  if (m_cache.status() != cache::full) {
137 
138  auto file = open_database();
139 
140  skip_commented_lines(file);
141 
142  std::string line;
143 
144  while (std::getline(file, line)) {
145 
146  typename NameField::value_type name;
147  if (reactions::fields::read_field<typename NameField::range_type>(
148  name, line) == reactions::fields::failed)
150  "Error reading the database; data format not understood");
151 
152  if (new_element.template get<NameField>() == name)
154  "Attempt to register an element with similar name to an "
155  "element in the database");
156 
157  typename IdField::value_type id;
158  if (reactions::fields::read_field<typename IdField::range_type>(
159  id, line) == reactions::fields::failed)
161  "Error reading the database; data format not understood");
162 
163  if (new_element.template get<IdField>() == id)
165  "Attempt to register an element with similar ID to an "
166  "element in the database");
167  }
168  }
169 
170  // this must be done after the checks are done to prevent leaving
171  // the cache in an invalid state
172  m_cache.add_user_element(std::move(new_element));
173  }
174 
175  /* \brief Set the path to the database file.
176  *
177  * If the cache is enabled, reloads the content using the new path.
178  */
180  m_db = s;
181  if (m_cache.status() == cache::full) {
182  disable_cache();
183  enable_cache();
184  }
185  }
186 
188  virtual element_type operator()(int id) const = 0;
189 
191  virtual element_type operator()(std::string const &str) const = 0;
192 
193  protected:
196 
198  class cache {
199 
200  public:
203 
205  using const_iterator_type = typename cache_type::const_iterator;
206  using size_type = typename cache_type::size_type;
207 
208  cache() = default;
209 
211  void clear() {
212  m_vector.clear();
214  m_separator = 0;
215  }
216 
221  m_separator = 0;
222  }
223 
226  if (m_vector.size()) {
227  if (database_size() == 0)
228  return cache_status::user;
229  else
230  return cache_status::full;
231  } else
232  return cache_status::empty;
233  }
234 
236  cache_type const &elements() const { return m_vector; }
237 
239  cache_type &elements() { return m_vector; }
240 
242  const_iterator_type begin() const { return m_vector.cbegin(); }
243 
245  const_iterator_type end() const { return m_vector.cend(); }
246 
249 
252  return m_vector.cbegin() + m_separator;
253  }
254 
257  return database_cend();
258  }
259 
262  return m_vector.cend();
263  }
264 
266  size_type database_size() const { return m_separator; }
267 
270  return m_vector.size() - m_separator;
271  }
272 
274  size_type size() const { return m_vector.size(); }
275 
278  template <class ElementReader>
279  void add_database_elements(size_type n, ElementReader func) {
280 
281  cache_type new_cache;
282  new_cache.reserve(n + user_registered_size());
283 
284  for (auto i = 0u; i < n; ++i) {
285 
286  auto new_element = func();
287 
288  // check that we do not repeat any entry
289  if (user_registered_size() != 0) {
290  auto cend = user_registered_cend();
292  [&new_element](element_type const &el) {
293  return (
294  el.template get<NameField>() ==
295  new_element.template get<NameField>() ||
296  el.template get<IdField>() ==
297  new_element.template get<IdField>());
298  }) != cend)
300  (std::string{"User-defined element clashes with database "
301  "element: \""} +
302  new_cache.back().name() + "\"")
303  .c_str());
304  }
305 
306  new_cache.emplace_back(std::move(new_element));
307  }
308 
309  // insert the elements and assign the separator to the number of
310  // database elements
311  new_cache.insert(new_cache.end(),
314  m_separator = n;
315  m_vector = std::move(new_cache);
316  }
317 
319  template <class... Args>
320  element_type const &add_user_element(Args &&...args) {
321  element_type new_element{std::forward<Args>(args)...};
322  auto e = end();
323  if (std::find_if(begin(), e,
324  [this, &new_element](element_type const &el) {
325  return (el.template get<NameField>() ==
326  new_element.template get<NameField>() ||
327  el.template get<IdField>() ==
328  new_element.template get<IdField>());
329  }) != e) {
331  (std::string{"User-registered element clashes: \""} +
332  new_element.name() + "\"")
333  .c_str());
334  }
335  m_vector.emplace_back(std::move(new_element));
336  return m_vector.back();
337  }
338 
339  protected:
342 
346 
347  } m_cache;
348 
352 
353  if (m_db.empty())
354  throw reactions::database_error("The database has not been specified");
355 
356  std::ifstream file;
357 
358  try {
359  file.open(m_db);
360  } catch (...) {
361  throw reactions::database_error("Unable to access the database");
362  }
363 
364  if (!file.is_open())
365  throw reactions::database_error("Unable to access the database");
366 
367  return file;
368  }
369 
372 
373  char c;
374  while (true) {
375  file.get(c);
376  if (c == '*')
378  else
379  break;
380  }
381  file.unget();
382 
383  return file.tellg();
384  }
385 
387  template <std::size_t I>
388  bool read_field(typename element_type::base_type &tuple,
389  std::string const &line) const {
390 
391  using field = std::tuple_element_t<I, typename element_type::fields_type>;
392 
393  if constexpr (fields::is_optional_field_v<field>) {
394 
395  typename field::value_type::value_type value;
396 
397  auto sc = reactions::fields::read_field<typename field::range_type>(
398  value, line);
399 
401  std::get<I>(tuple).emplace(value);
402 
404  } else
405  return reactions::fields::read_field<typename field::range_type>(
406  std::get<I>(tuple), line) !=
408  }
409 
411  template <std::size_t... I>
412  bool read_line(typename element_type::base_type &tuple,
413  std::string const &line, std::index_sequence<I...>) const {
414  return (read_field<I>(tuple, line) && ...);
415  }
416 
418  bool read_line(typename element_type::base_type &tuple,
419  std::string const &line) const {
420  return read_line(
421  tuple, line,
423  }
424 
426  element_type read_element(std::string const &line) const {
427 
428  typename element_type::base_type tuple;
429 
430  if (!read_line(tuple, line))
432  "Error reading the database; data format not understood");
433 
434  return tuple;
435  }
436 
438  template <class Field, class T> element_type access(T const &v) const {
439 
440  switch (m_cache.status()) {
441  case (cache::full): // the full database is loaded
442 
443  for (auto const &el : m_cache)
444  if (el.template get<Field>() == v)
445  return el;
446 
447  break; // throws an exception
448 
449  case (cache::user): // only user-registered entries exist
450 
451  for (auto const &el : m_cache)
452  if (el.template get<Field>() == v)
453  return el;
454 
455  [[fallthrough]]; // continue as if we had no cache
456 
457  case (cache::empty): // the cache is empty
458 
459  auto file = open_database();
460 
461  skip_commented_lines(file);
462 
463  std::string line;
464 
465  while (std::getline(file, line)) {
466 
467  typename Field::value_type ref;
468  auto sc = reactions::fields::read_field<typename Field::range_type>(
469  ref, line);
470 
471  if (sc == reactions::fields::failed)
473  "Error reading the database; data format not understood");
474 
475  if (ref == v) {
476  element_type el = read_element(line);
477  file.close();
478  return el;
479  }
480  }
481 
482  file.close();
483  }
484 
486  (std::string{"Unable to find element with "} + Field::title + " \"" +
487  fields::to_string(v) + '"')
488  .c_str());
489  }
490 
491  database(std::string &&db) : m_db{std::move(db)} {}
492  database(database &&) = delete;
493  database(database const &) = delete;
494  void operator=(database &&) = delete;
495  void operator=(database const &) = delete;
496  };
497 } // namespace reactions::database
T empty(T... args)
bool read_line(typename element_type::base_type &tuple, std::string const &line) const
Read all the fields from a line.
Definition: database.hpp:418
the conversion succeeded
Definition: fields.hpp:366
T open(T... args)
const_iterator_type begin() const
Begining of the cache.
Definition: database.hpp:242
std::string to_string(T const &v)
Convert the given object to a string.
Definition: fields.hpp:554
Base database class.
Definition: database.hpp:20
virtual element_type operator()(int id) const =0
Create an element accessing by ID.
cache_type & elements()
Underlying vector of elements.
Definition: database.hpp:239
T unget(T... args)
Definition: database.hpp:202
cache_status status() const
Status of the cache.
Definition: database.hpp:225
A PDG particle, based on the fields of a particle in the PDG database.
Definition: pdg.hpp.in:138
Exceptions that can be thrown when running the functions of the package.
T getline(T... args)
T make_move_iterator(T... args)
the conversion failed
Definition: fields.hpp:368
T end(T... args)
Raised whenever a problem is detected in the database.
Definition: exceptions.hpp:101
typename cache_type::const_iterator const_iterator_type
Definition: database.hpp:205
cache_status
Code to define the status of the cache.
Definition: database.hpp:202
const_iterator_type user_registered_cend() const
End of the user-registered elements.
Definition: database.hpp:261
T shrink_to_fit(T... args)
std::string m_db
Path to the database file.
Definition: database.hpp:195
void operator=(database &&)=delete
STL class.
class reactions::database::database::cache m_cache
bool read_line(typename element_type::base_type &tuple, std::string const &line, std::index_sequence< I... >) const
Read all the fields from a line.
Definition: database.hpp:412
void clear()
Clear the cache.
Definition: database.hpp:211
pdg_element_base base_type
Base class.
Definition: pdg.hpp.in:142
T erase(T... args)
Raised when an element is not found within a database.
Definition: exceptions.hpp:88
Common tools and base objects to handle databases.
Definition: database.hpp:15
const_iterator_type database_cbegin() const
Begining of the database elements.
Definition: database.hpp:248
const_iterator_type end() const
End of the cache.
Definition: database.hpp:245
void enable_cache()
Definition: database.hpp:92
T clear(T... args)
T tellg(T... args)
void register_element(Args &&...args)
Register a new element.
Definition: database.hpp:130
virtual ~database()
Definition: database.hpp:25
size_type size() const
Number of cached elements.
Definition: database.hpp:274
T get(T... args)
T insert(T... args)
T find_if(T... args)
T size(T... args)
void set_database_path(std::string const &s)
Definition: database.hpp:179
the object is missing
Definition: fields.hpp:367
Common operations on fields.
T cbegin(T... args)
database(std::string &&db)
Definition: database.hpp:491
std::string const & get_database_path() const
Get the path to the database file.
Definition: database.hpp:122
Definition: database.hpp:202
element_type const & add_user_element(Args &&...args)
Add a new element (by the user)
Definition: database.hpp:320
T back(T... args)
void clear_database_elements()
Clear the cache.
Definition: database.hpp:218
const_iterator_type database_cend() const
End of the database elements.
Definition: database.hpp:251
cache_type const & elements() const
Underlying vector of elements.
Definition: database.hpp:236
Element element_type
Definition: database.hpp:23
typename cache_type::size_type size_type
Definition: database.hpp:206
std::vector< element_type > all_elements() const
All the elements in the database file.
Definition: database.hpp:36
element_type access(T const &v) const
Access an element using the field accessor.
Definition: database.hpp:438
void disable_cache()
Disable the cache.
Definition: database.hpp:84
cache_type m_vector
Collection of elements.
Definition: database.hpp:341
element_type read_element(std::string const &line) const
Advance to the next element in the file and read it.
Definition: database.hpp:426
bool read_field(typename element_type::base_type &tuple, std::string const &line) const
Read a field with the given index from a line.
Definition: database.hpp:388
T is_open(T... args)
std::streampos skip_commented_lines(std::ifstream &file) const
Skip lines with comments (preceeded by *)
Definition: database.hpp:371
void add_database_elements(size_type n, ElementReader func)
Definition: database.hpp:279
const_iterator_type user_registered_cbegin() const
Begining of the user-registered elements.
Definition: database.hpp:256
T ignore(T... args)
size_type database_size() const
Number of elements associated to the database.
Definition: database.hpp:266
std::ifstream open_database() const
Open the database.
Definition: database.hpp:351
STL class.
size_type m_separator
Definition: database.hpp:345
size_type user_registered_size() const
Number of user-registered elements.
Definition: database.hpp:269
T reserve(T... args)
Cache of elements.
Definition: database.hpp:198
T emplace_back(T... args)
void clear_cache()
Clear the cache, removing also user-registered elements.
Definition: database.hpp:81