Reactions  0.0.0
Handling reaction trees and decays
processes.hpp
Go to the documentation of this file.
1 
5 #pragma once
6 
9 #include "reactions/pow_enum.hpp"
10 #include "reactions/tokens.hpp"
11 
12 #include <functional>
13 #include <memory>
14 #include <optional>
15 #include <string>
16 #include <utility>
17 #include <vector>
18 
19 namespace reactions {
20 
21  // Forward declarations
22  template <class Element> class element_wrapper;
23  template <class Element> class reaction;
24  template <class Element> class decay;
25 
28  namespace processes {
30  REACTIONS_POW_ENUM_WITH_UNKNOWN(node_kind, element, reaction, decay);
31 
44  template <class FillElement, class FillExpression, class ArrowSwitch>
45  void process_expression(std::string::const_iterator &sit,
46  std::string::const_iterator const &end,
47  FillElement fill_element,
48  FillExpression fill_expression,
49  ArrowSwitch arrow_switch) {
50 
51  while (tokens::match_token<tokens::space>(sit))
52  ++sit; // remove leading spaces
53 
54  if (tokens::match_token<tokens::left_bra>(sit))
56  "Expression starts with another expression", end - sit);
57 
58  auto start = sit; // keep track of the beginning of an expression
59  while (sit != end) {
60 
61  if (tokens::match_token<tokens::space>(sit)) {
62 
63  if (sit == start) {
64  // remove consecutive spaces
65  start = ++sit;
66  continue;
67  } else {
68  // add the new element
69  fill_element(start);
70  start = (sit += tokens::space::size);
71  continue;
72  }
73  } else if (tokens::match_token<tokens::left_bra>(sit)) {
74 
75  if (sit == start) {
76 
78 
79  // begin a new expression
80  fill_expression();
81 
82  if (!tokens::match_token<tokens::right_bra>(sit))
84  "Expected closing braces", end - sit);
85 
86  start = (sit += tokens::right_bra::size);
87 
88  continue;
89 
90  } else {
91  // add the new element
92  fill_element(start);
93  start = sit; // end up in the previous conditional
94  continue;
95  }
96  } else if (tokens::match_token<tokens::right_bra>(sit)) {
97 
98  if (sit != start) {
99  fill_element(start);
100  start = sit;
101  }
102 
103  break;
104 
105  } else if (tokens::match_token<tokens::arrow>(sit)) {
106 
107  if (sit != start)
108  fill_element(start);
109 
110  start = (sit += tokens::arrow::size);
111 
112  // switch to the products
113  arrow_switch();
114 
115  continue;
116 
117  } else
118  ++sit;
119  }
120 
121  if (sit != start)
122  fill_element(start);
123  }
124 
131  template <class Process>
132  Process make_process(std::string const &str,
133  typename Process::builder_type builder) {
134 
135  try {
136 
137  auto sit = str.cbegin();
138  auto const send = str.cend();
139 
140  Process p{sit, send, builder};
141 
142  if (sit != send) {
143  if (tokens::match_token<tokens::right_bra>(sit))
144  throw reactions::exceptions::__syntax_error("Mismatching braces",
145  send - sit);
146  else
147  throw reactions::exceptions::__syntax_error("Invalid syntax",
148  send - sit);
149  }
150 
151  return p;
152 
154  throw e.update(str);
155  }
156  }
157  } // namespace processes
158 
163  struct node_object {
164  node_object() = default;
165  node_object(node_object const &) = default;
166  node_object(node_object &&) = default;
167  node_object &operator=(node_object const &) = default;
168  virtual ~node_object() = default;
169  };
170 
176  template <class Element> class node final {
177 
178  public:
182 
185  : m_type{std::move(processes::node_kind::element)},
186  m_ptr{ptr.release()} {}
187 
190  : m_type{std::move(processes::node_kind::reaction)},
191  m_ptr{ptr.release()} {}
192 
195  : m_type{std::move(processes::node_kind::decay)}, m_ptr{ptr.release()} {
196  }
197 
199  node(node &&other) : m_type{other.m_type}, m_ptr{other.m_ptr} {
200  other.m_ptr = nullptr;
201  }
202 
204  ~node() noexcept(false) {
205 
206  if (m_ptr) {
207  switch (m_type) {
208  case (processes::node_kind::element):
209  delete ptr_as_element_wrapper();
210  return;
211  case (processes::node_kind::reaction):
212  delete ptr_as_reaction();
213  return;
214  case (processes::node_kind::decay):
215  delete ptr_as_decay();
216  return;
217  case (processes::node_kind::unknown):
219  "A node type should always be set (internal error); please "
220  "report the bug");
221  }
222  }
223  }
224 
225  node() = delete;
226  node(node const &) = delete;
227  node &operator=(node const &) = delete;
228 
230  bool is_element() const { return m_type == processes::node_kind::element; }
231 
233  bool is_reaction() const {
234  return m_type == processes::node_kind::reaction;
235  }
236 
238  bool is_decay() const { return m_type == processes::node_kind::decay; }
239 
241  processes::node_kind type() const { return m_type; }
242 
244  node_object const *object() const { return m_ptr; }
245 
247  Element const *ptr_as_element() const {
248  return ptr_as_element_wrapper()->operator->();
249  }
250 
253  return static_cast<reaction_type *>(m_ptr);
254  }
255 
257  decay_type const *ptr_as_decay() const {
258  return static_cast<decay_type *>(m_ptr);
259  }
260 
261  protected:
265  return static_cast<element_type *>(m_ptr);
266  }
267 
268  private:
270  processes::node_kind m_type = processes::node_kind::unknown;
271 
273  node_object *m_ptr = nullptr;
274  };
275 
277  namespace processes::detail {
285  template <class Element>
286  inline bool check_nodes(std::vector<node<Element>> const &first,
287  std::vector<node<Element>> const &second) {
288 
289  auto size = first.size();
290 
291  if (size != second.size())
292  return false;
293 
294  // two masks to count the elements that are gone
295  std::vector<bool> mask(size, false);
296 
297  for (auto i = 0u; i < size; ++i) {
298 
299  for (auto j = 0u; j < size; ++j) {
300 
301  if (mask[j])
302  // already used
303  continue;
304 
305  if (second[j].type() != first[i].type())
306  // different types
307  continue;
308 
309  switch (first[i].type()) {
310  case (processes::node_kind::element):
311  if (*(first[i].ptr_as_element()) == *(second[i].ptr_as_element()))
312  mask[j] = true;
313  break;
314  case (processes::node_kind::reaction):
315  if (*(first[i].ptr_as_reaction()) == *(second[i].ptr_as_reaction()))
316  mask[j] = true;
317  break;
318  case (processes::node_kind::decay):
319  if (*(first[i].ptr_as_decay()) == *(second[i].ptr_as_decay()))
320  mask[j] = true;
321  break;
322  case (processes::node_kind::unknown):
324  "A node can not be of unknown type (internal error); please "
325  "report the bug");
326  break;
327  }
328  }
329  // if we reach this point, no match has been found for "first[i]"
330  return false;
331  }
332 
333  // all of them have been matched
334  return true;
335  }
336  } // namespace processes::detail
337 } // namespace reactions
338 
339 namespace reactions {
340 
346  template <class Element> class element_wrapper final : public node_object {
347 
348  public:
350  element_wrapper(Element &&e) : node_object{}, m_element{std::move(e)} {}
352  ~element_wrapper() override = default;
354  element_wrapper(const element_wrapper &) = default;
356  element_wrapper(element_wrapper &&) = default;
358  element_wrapper &operator=(const element_wrapper &) = default;
359  // No empty constructor
360  element_wrapper() = delete;
362  Element const &operator*() const { return m_element; }
364  Element const *operator->() const { return &m_element; }
365 
366  protected:
368  Element m_element;
369  };
370 
375  template <class Element> class reaction final : public node_object {
376 
377  public:
379  using builder_type =
383 
385  reaction(reaction &&) = default;
387  reaction(const reaction &) = default;
389  reaction &operator=(const reaction &) = default;
391  ~reaction() = default;
392  // No empty constructor
393  reaction() = delete;
394 
396  nodes_type const &reactants() const { return m_reactants; }
397 
399  nodes_type const &products() const { return m_products; }
400 
408  bool operator==(reaction<Element> const &other) const {
409 
410  if (m_reactants.size() != other.m_reactants.size() ||
411  m_products.size() != other.m_products.size())
412  return false;
413 
414  return processes::detail::check_nodes(m_reactants, other.m_reactants) &&
415  processes::detail::check_nodes(m_products, other.m_products);
416  }
417 
419  bool operator!=(reaction<Element> const &other) const {
420  return !(*this == other);
421  }
422 
423  protected:
431  reaction(std::string::const_iterator &&begin,
432  std::string::const_iterator const &end, builder_type builder)
433  : reaction{begin, end, builder} {}
434 
435  // Create a new instance using the protected constructor
437  std::make_unique<reaction>(std::string::const_iterator &,
438  std::string::const_iterator const &,
439  builder_type);
440 
441  template <class Process>
442  friend Process
444  typename Process::builder_type);
445 
456  reaction(std::string::const_iterator &sit,
457  std::string::const_iterator const &end, builder_type builder)
458  : node_object{} {
459 
460  nodes_type *current_set = &m_reactants;
461 
462  auto fill_element =
463  [&](std::string::const_iterator const &start) -> void {
464  current_set->push_back(
465  std::make_unique<element_type>(builder(std::string{start, sit})));
466  };
467  auto fill_reaction = [&]() -> void {
468  current_set->push_back(std::make_unique<reaction>(sit, end, builder));
469  };
470  auto arrow_switch = [&]() -> void {
471  if (!m_reactants.size())
472  throw reactions::exceptions::__syntax_error("Missing reactants",
473  end - sit);
474 
475  if (current_set == &m_products)
476  throw reactions::exceptions::__syntax_error("Duplicated arrow",
477  end - sit);
478 
479  current_set = &m_products;
480  };
481 
482  processes::process_expression(sit, end, fill_element, fill_reaction,
483  arrow_switch);
484 
485  if (!m_reactants.size())
486  throw reactions::exceptions::__syntax_error("Missing reactants",
487  end - sit);
488 
489  if (!m_products.size())
490  throw reactions::exceptions::__syntax_error("Missing products",
491  end - sit);
492  }
493 
498  };
499 
506  template <class Element> class decay final : public node_object {
507 
508  public:
512 
514  decay(decay &&) = default;
516  decay(const decay &) = default;
518  decay &operator=(const decay &) = default;
520  ~decay() = default;
521 
522  decay() = delete;
523 
525  Element const &head() const { return *(*m_head); }
526 
528  nodes_type const &products() const { return m_products; }
529 
531  bool operator==(decay<Element> const &other) const {
532 
533  if (m_products.size() != other.m_products.size())
534  return false;
535 
536  return (*m_head == *other.m_head) &&
537  processes::detail::check_nodes(m_products, other.m_products);
538  }
539 
541  bool operator!=(decay<Element> const &other) const {
542  return !(*this == other);
543  }
544 
545  protected:
556  decay(std::string::const_iterator &&begin,
557  std::string::const_iterator const &end, builder_type builder)
558  : decay{begin, end, builder} {}
559 
562  std::make_unique<decay>(std::string::const_iterator &,
563  std::string::const_iterator const &, builder_type);
564 
565  template <class Process>
566  friend Process
568  typename Process::builder_type);
569 
580  decay(std::string::const_iterator &sit,
581  std::string::const_iterator const &end, builder_type builder)
582  : node_object{} {
583 
584  bool fill_products = false; // keep track of the elements we are adding
585 
586  auto fill_element =
587  [&](std::string::const_iterator const &start) -> void {
588  if (!m_head) {
589  this->m_head.emplace(builder(std::string{start, sit}));
590  } else if (fill_products) {
591  this->m_products.emplace_back(
592  std::make_unique<element_type>(builder(std::string{start, sit})));
593  } else
594  throw reactions::exceptions::__syntax_error("Missing arrow",
595  end - start);
596  };
597  auto fill_decay = [&] {
598  if (!m_head) {
599  throw reactions::exceptions::__syntax_error("Missing head",
600  end - sit);
601  } else if (fill_products) {
602  this->m_products.push_back(
603  std::make_unique<decay>(sit, end, builder));
604  } else
605  throw reactions::exceptions::__syntax_error("Missing arrow",
606  end - sit);
607  };
608  auto arrow_switch = [&] {
609  if (fill_products)
610  throw reactions::exceptions::__syntax_error("Duplicated arrow",
611  end - sit);
612 
613  else if (!m_head)
614  throw reactions::exceptions::__syntax_error("Missing head particle",
615  end - sit);
616 
617  fill_products = true;
618  };
619 
620  processes::process_expression(sit, end, fill_element, fill_decay,
621  arrow_switch);
622 
623  if (!m_head)
625  "No elements have been parsed", end - sit);
626 
627  if (!m_products.size())
628  throw reactions::exceptions::__syntax_error("Expected products",
629  end - sit);
630  }
631 
636  };
637 
644  template <class Element>
648  return processes::make_process<reaction<Element>>(str, builder);
649  }
650 
656  template <class Element>
658  return make_reaction_for<Element>(str, element_traits::builder<Element>);
659  }
660 
667  template <class Element>
670  return processes::make_process<decay<Element>>(str, builder);
671  }
672 
678  template <class Element> decay<Element> make_decay(std::string const &str) {
679  return make_decay_for<Element>(str, element_traits::builder<Element>);
680  }
681 } // namespace reactions
Description of a process where reactants generate a set of products.
Definition: processes.hpp:23
Element m_element
Underlying element.
Definition: processes.hpp:368
Syntax error with an unformatted message.
Definition: exceptions.hpp:69
Base class for types referred to a node.
Definition: processes.hpp:163
syntax_error update(std::string const &str)
Definition: exceptions.hpp:77
bool is_element() const
Check if the underlying class is an element.
Definition: processes.hpp:230
bool is_reaction() const
Check if the underlying class is a reaction.
Definition: processes.hpp:233
Element const * ptr_as_element() const
Return the pointer to the underlying object casted to an element.
Definition: processes.hpp:247
reaction< Element > make_reaction(std::string const &str)
Create a new reaction.
Definition: processes.hpp:657
element_traits::builder_tpl_t< Element > builder_type
Definition: processes.hpp:510
decay_type const * ptr_as_decay() const
Return the pointer to the underlying object casted to a decay.
Definition: processes.hpp:257
nodes_type m_products
Products.
Definition: processes.hpp:497
void process_expression(std::string::const_iterator &sit, std::string::const_iterator const &end, FillElement fill_element, FillExpression fill_expression, ArrowSwitch arrow_switch)
Internal function to process an expression.
Definition: processes.hpp:45
Exceptions that can be thrown when running the functions of the package.
reaction_type const * ptr_as_reaction() const
Return the pointer to the underlying object casted to a reaction.
Definition: processes.hpp:252
node(node &&other)
Move constructor.
Definition: processes.hpp:199
processes::node_kind type() const
Get the node type.
Definition: processes.hpp:241
node(std::unique_ptr< reaction_type > &&ptr)
Constructor from a reaction.
Definition: processes.hpp:189
T cend(T... args)
Raised when unexpected problems appear, which should be reported as bugs.
Definition: exceptions.hpp:38
Element const & operator*() const
Access the underlying element by reference.
Definition: processes.hpp:362
bool check_nodes(std::vector< node< Element >> const &first, std::vector< node< Element >> const &second)
Compare two nodes.
Definition: processes.hpp:286
node(std::unique_ptr< decay_type > &&ptr)
Construction from a decay.
Definition: processes.hpp:194
Description of a process where head particle generate a set of products.
Definition: processes.hpp:24
STL class.
T push_back(T... args)
static constexpr auto builder
Default builder for a given kind of element.
Definition: element_traits.hpp:42
nodes_type m_products
Products.
Definition: processes.hpp:635
Contains macros to define smart enumeration types.
nodes_type const & reactants() const
Get the reactants of the reaction.
Definition: processes.hpp:396
bool operator==(decay< Element > const &other) const
Comparison operator.
Definition: processes.hpp:531
Tokens allowed in a reaction or decay.
std::function< T(std::string const &)> const & builder_tpl_t
General builder type for a given kind of element.
Definition: element_traits.hpp:49
Main namespace of the Reactions package.
Definition: all.hpp:21
node_object const * object() const
Get the pointer to the underlying object.
Definition: processes.hpp:244
static const size_t size
Definition: tokens.hpp:16
bool operator!=(decay< Element > const &other) const
Comparison operator.
Definition: processes.hpp:541
bool operator==(reaction< Element > const &other) const
Compare two reactions.
Definition: processes.hpp:408
Base class for a process node.
Definition: processes.hpp:176
Template for elements of a reaction or decay.
Definition: processes.hpp:22
REACTIONS_POW_ENUM_WITH_UNKNOWN(node_kind, element, reaction, decay)
Node types.
Process make_process(std::string const &str, typename Process::builder_type builder)
Make a new process (a reaction or a decay)
Definition: processes.hpp:132
T size(T... args)
reaction< Element > make_reaction_for(std::string const &str, typename reaction< Element >::builder_type builder)
Create a new reaction with a custom builder.
Definition: processes.hpp:646
decay< Element > make_decay_for(std::string const &str, typename decay< Element >::builder_type builder)
Create a new decay with a custom builder.
Definition: processes.hpp:668
STL class.
Element const * operator->() const
Access the underlying element by pointer.
Definition: processes.hpp:364
bool is_decay() const
Check if the underlying class is a decay.
Definition: processes.hpp:238
T cbegin(T... args)
reaction(std::string::const_iterator &&begin, std::string::const_iterator const &end, builder_type builder)
Constructor from the string iterators.
Definition: processes.hpp:431
nodes_type const & products() const
Get the products of the reaction.
Definition: processes.hpp:399
Element const & head() const
Get the head particle of the decay.
Definition: processes.hpp:525
Utilities to work with element types.
decay(std::string::const_iterator &sit, std::string::const_iterator const &end, builder_type builder)
Constructor from the string iterators.
Definition: processes.hpp:580
std::optional< element_type > m_head
Head particle.
Definition: processes.hpp:633
element_type const * ptr_as_element_wrapper() const
Definition: processes.hpp:264
bool operator!=(reaction< Element > const &other) const
Compare two reactions.
Definition: processes.hpp:419
reaction(std::string::const_iterator &sit, std::string::const_iterator const &end, builder_type builder)
Constructor from the string iterators.
Definition: processes.hpp:456
nodes_type const & products() const
Get the products of the decay.
Definition: processes.hpp:528
node(std::unique_ptr< element_type > &&ptr)
Constructor from an element.
Definition: processes.hpp:184
decay(std::string::const_iterator &&begin, std::string::const_iterator const &end, builder_type builder)
Constructor from the string iterators.
Definition: processes.hpp:556
element_traits::builder_tpl_t< Element > builder_type
Underlying element type.
Definition: processes.hpp:380
decay< Element > make_decay(std::string const &str)
Create a new decay.
Definition: processes.hpp:678
nodes_type m_reactants
Reactants.
Definition: processes.hpp:495
element_wrapper(Element &&e)
Constructor given the underlying element.
Definition: processes.hpp:350
~node() noexcept(false)
Destructor.
Definition: processes.hpp:204