ʻOhana
Population structure, admixture history, and selection using learning methods.
jade.assert.hpp
1 /* -------------------------------------------------------------------------
2  Ohana
3  Copyright (c) 2015-2020 Jade Cheng (\___/)
4  Jade Cheng <info@jade-cheng.com> (='.'=)
5  ------------------------------------------------------------------------- */
6 
7 #ifndef JADE_ASSERT_HPP__
8 #define JADE_ASSERT_HPP__
9 
10 #include "jade.system.hpp"
11 
12 #ifndef NDEBUG
13 
14 #include <cxxabi.h>
15 #include <execinfo.h>
16 
17 namespace jade
18 {
19  ///
20  /// A template for a class that asserts valid conditions.
21  ///
22  template <typename TChar>
24  {
25  public:
26  /// The character type.
27  typedef TChar char_type;
28 
29  /// The string type.
30  typedef std::basic_string<char_type> string_type;
31 
32  ///
33  /// Validates an expression is true; if not, this method throws an
34  /// exception.
35  ///
36  static void validate(
37  const bool is_true, ///< The validated expression.
38  char_type const * const expression, ///< The expression text.
39  char_type const * const file, ///< The name of the file.
40  const int line) ///< The line number.
41  {
42  if (is_true)
43  return;
44 
45  auto & err = _err();
46  err << file << char_type('(') << line << char_type(')')
47  << char_type(':') << char_type(' ') << expression
48  << std::endl;
49 
50  void * addresses[256];
51  const auto n = ::backtrace(
52  addresses,
53  std::extent<decltype(addresses)>::value);
54 
55  typedef std::unique_ptr<
56  char_type *,
57  decltype(&std::free)
58  > symbols_ptr_type;
59 
60  const symbols_ptr_type symbols_ptr (
61  _backtrace_symbols(addresses, n),
62  &std::free);
63  const auto symbols = symbols_ptr.get();
64 
65  for (auto i = 1; i < n; i++)
66  {
67  const auto symbol = symbols[i];
68 
69  //
70  // Look for the last '+' symbol; if found subract white-space
71  // off the end of the mangled function name.
72  //
73  auto end = symbol;
74  while (*end != char_type('\0'))
75  ++end;
76  while (end != symbol && *end != char_type('+'))
77  --end;
78  while (end > symbol && end[-1] == char_type(' '))
79  --end;
80 
81  //
82  // Loop back until the first white-space symbol or parenthesis
83  // symbol is found.
84  //
85  auto begin = std::max(symbol, end - 1);
86  while (
87  begin > symbol &&
88  begin[-1] != char_type(' ') &&
89  begin[-1] != char_type('('))
90  --begin;
91 
92  //
93  // If the mangled name wasn't found, then print the mangled
94  // version; otherwise, replace the manged name with the
95  // demangled version and print the reformatted line.
96  //
97  if (begin <= symbol || end <= symbol)
98  {
99  err << symbol << std::endl;
100  }
101  else
102  {
103  for (auto p = symbol; p != begin; p++)
104  err << *p;
105  const auto replaced = *end;
106  *end++ = char_type('\0');
107 
108  err << _demangle(begin) << replaced << end << std::endl;
109  }
110  }
111 
112  throw std::runtime_error("assertion failure");
113  }
114 
115  private:
116  // --------------------------------------------------------------------
117  static char_type ** _backtrace_symbols(
118  void * const * buffer,
119  int size);
120 
121  // --------------------------------------------------------------------
122  static char_type * _cxa_demangle(
123  const char_type * mangled_name,
124  char_type * output_buffer,
125  size_t * length,
126  int * status);
127 
128  // --------------------------------------------------------------------
129  static string_type _demangle(char_type const * const symbol)
130  {
131  typedef std::unique_ptr<
132  char_type,
133  decltype(&std::free)
134  > ptr_type;
135 
136  const ptr_type demangled (
137  _cxa_demangle(symbol, nullptr, nullptr, nullptr),
138  &std::free);
139 
140  return demangled ? demangled.get() : symbol;
141  }
142 
143  // --------------------------------------------------------------------
144  static std::basic_ostream<char_type> & _err();
145  };
146 
147  // ------------------------------------------------------------------------
148  template <>
149  inline char ** basic_assertion<char>::_backtrace_symbols(
150  void * const * buffer,
151  int size)
152  {
153  return ::backtrace_symbols(buffer, size);
154  }
155 
156  // ------------------------------------------------------------------------
157  template <>
158  inline char * basic_assertion<char>::_cxa_demangle(
159  const char_type * mangled_name,
160  char_type * output_buffer,
161  size_t * length,
162  int * status)
163  {
164  return abi::__cxa_demangle(
165  mangled_name,
166  output_buffer,
167  length,
168  status);
169  }
170  // ------------------------------------------------------------------------
171  template <>
172  inline std::ostream & basic_assertion<char>::_err()
173  {
174  return std::cerr;
175  }
176 
177  ///
178  /// A class that asserts valid conditions.
179  ///
180  typedef basic_assertion<char> assertion;
181 }
182 
183 #define assert(E) jade::assertion::validate(E, #E, __FILE__, __LINE__)
184 
185 #else // NDEBUG
186 #define assert(E)
187 #endif
188 
189 #endif // JADE_ASSERT_HPP__
jade::basic_assertion
A template for a class that asserts valid conditions.
Definition: jade.assert.hpp:24
jade::basic_assertion::char_type
TChar char_type
The character type.
Definition: jade.assert.hpp:27
jade::basic_assertion::validate
static void validate(const bool is_true, char_type const *const expression, char_type const *const file, const int line)
Validates an expression is true; if not, this method throws an exception.
Definition: jade.assert.hpp:36
jade::basic_assertion::string_type
std::basic_string< char_type > string_type
The string type.
Definition: jade.assert.hpp:30