rapidyaml  0.12.0
parse and emit YAML, and do it fast
error.hpp
Go to the documentation of this file.
1 #ifndef _C4_YML_ERROR_HPP_
2 #define _C4_YML_ERROR_HPP_
3 
4 /** @file error.hpp Error utilities used by ryml. */
5 
6 #ifndef _C4_YML_COMMON_HPP_
7 #include <c4/yml/common.hpp>
8 #endif
9 
10 /// @cond dev
11 #if (defined(C4_EXCEPTIONS) && (!defined(RYML_NO_DEFAULT_CALLBACKS) && defined(RYML_DEFAULT_CALLBACK_USES_EXCEPTIONS))) || defined(__DOXYGEN__)
12 #define _RYML_WITH_EXCEPTIONS
13 #endif
14 #if defined(RYML_DBG) && !defined(NDEBUG) && !defined(C4_NO_DEBUG_BREAK)
15 # define RYML_DEBUG_BREAK() \
16  do { \
17  if(c4::get_error_flags() & c4::ON_ERROR_DEBUGBREAK) \
18  { \
19  C4_DEBUG_BREAK(); \
20  } \
21  } while(false)
22 #else
23 # define RYML_DEBUG_BREAK()
24 #endif
25 /// @endcond
26 
27 #ifdef _RYML_WITH_EXCEPTIONS
28 #include <exception>
29 #endif
30 
31 
32 namespace c4 {
33 namespace yml {
34 
35 
36 /// @cond dev
37 
38 namespace detail {
39 struct _SubstrWriter
40 {
41  substr buf;
42  size_t pos;
43  _SubstrWriter(substr buf_, size_t pos_=0) : buf(buf_), pos(pos_) { C4_ASSERT(buf.str); }
44  void append(csubstr s)
45  {
46  C4_ASSERT(!s.overlaps(buf));
47  C4_ASSERT(s.str || !s.len);
48  if(s.len && pos + s.len <= buf.len)
49  {
50  C4_ASSERT(s.str);
51  memcpy(buf.str + pos, s.str, s.len);
52  }
53  pos += s.len;
54  }
55  void append(char c)
56  {
57  C4_ASSERT(buf.str);
58  if(pos < buf.len)
59  buf.str[pos] = c;
60  ++pos;
61  }
62  void append_n(char c, size_t numtimes)
63  {
64  C4_ASSERT(buf.str);
65  if(numtimes && pos + numtimes < buf.len)
66  memset(buf.str + pos, c, numtimes);
67  pos += numtimes;
68  }
69  size_t slack() const { return pos <= buf.len ? buf.len - pos : 0; }
70  size_t excess() const { return pos > buf.len ? pos - buf.len : 0; }
71  //! get the part written so far
72  csubstr curr() const { return pos <= buf.len ? buf.first(pos) : buf; }
73  //! get the part that is still free to write to (the remainder)
74  substr rem() const { return pos < buf.len ? buf.sub(pos) : buf.last(0); }
75 
76  size_t advance(size_t more) { pos += more; return pos; }
77 };
78 
79 
80 // truncate the result to the buffer, adding ellipsis if it
81 // doesn't fit
82 inline C4_NO_INLINE csubstr _maybe_add_ellipsis(substr buf, size_t len)
83 {
84  if(C4_UNLIKELY(len > buf.len))
85  {
86  const size_t numdots = (buf.len > 3) ? 3 : buf.len;
87  buf.last(numdots).fill('.');
88  len = buf.len;
89  }
90  return buf.first(len);
91 }
92 
93 
94 // std::remove_cvref appeared in c++20
95 template<class T>
96 struct _remove_cvref
97 {
98  using type = typename std::remove_cv<typename std::remove_reference<T>::type>::type;
99 };
100 template<class T>
101 struct _dump_directly : public c4::is_string<typename _remove_cvref<T>::type>
102 {
103 };
104 
105 
106 #if C4_CPP >= 17
107 template<class T> using _remove_cvref_t = typename _remove_cvref<T>::type;
108 template<class T> using _dump_directly_v = typename _dump_directly<T>::value;
109 template<class T>
110 C4_NO_INLINE csubstr _to_chars_limited(substr buf, T &&var)
111 {
112  if constexpr (_dump_directly<T>::value)
113  {
114  (void)buf;
115  return to_csubstr(std::forward<T>(var)); // no need to convert to buf
116  }
117  else
118  {
119  size_t len = to_chars(buf, std::forward<T>(var));
120  return _maybe_add_ellipsis(buf, len);
121  }
122 }
123 #else
124 template<class T>
125 C4_NO_INLINE auto _to_chars_limited(substr, T &&var)
126  -> typename std::enable_if<_dump_directly<T>::value, csubstr>::type
127 {
128  return to_csubstr(std::forward<T>(var)); // no need to convert to buf
129 }
130 template<class T>
131 C4_NO_INLINE auto _to_chars_limited(substr buf, T &&var)
132  -> typename std::enable_if< ! _dump_directly<T>::value, csubstr>::type
133 {
134  size_t len = to_chars(buf, std::forward<T>(var));
135  return _maybe_add_ellipsis(buf, len);
136 }
137 #endif
138 
139 
140 // dumpfn is a function abstracting prints to terminal (or to string).
141 template<class DumpFn>
142 C4_NO_INLINE void _dump(DumpFn &&dumpfn, substr, csubstr fmt)
143 {
144  std::forward<DumpFn>(dumpfn)(fmt);
145 }
146 template<class DumpFn, class Arg, class ...Args>
147 C4_NO_INLINE void _dump(DumpFn &&dumpfn, substr argbuf, csubstr fmt, Arg const& arg, Args const& ...more)
148 {
149  size_t pos = fmt.find("{}");
150  if(pos == csubstr::npos)
151  return std::forward<DumpFn>(dumpfn)(fmt); // NOLINT // LCOV_EXCL_LINE
152  std::forward<DumpFn>(dumpfn)(fmt.first(pos)); // NOLINT
153  std::forward<DumpFn>(dumpfn)(_to_chars_limited(argbuf, arg)); // NOLINT
154  _dump(std::forward<DumpFn>(dumpfn), argbuf, fmt.sub(pos + 2), more...); // NOLINT
155 }
156 
157 
158 template<class ...Args>
159 C4_NO_INLINE csubstr _mk_err_msg(substr buf, csubstr fmt, Args const& ...args)
160 {
161  detail::_SubstrWriter writer(buf);
162  auto dumpfn = [&writer](csubstr s){ writer.append(s); };
163  char writebuf[RYML_LOGBUF_SIZE];
164  _dump(dumpfn, writebuf, fmt, args...);
165  return _maybe_add_ellipsis(buf, writer.pos);
166 }
167 
168 RYML_EXPORT csubstr _get_text_region(csubstr text, size_t pos, size_t num_lines_before, size_t num_lines_after);
169 
170 } // namespace detail
171 
172 /// @endcond
173 
174 
175 //-----------------------------------------------------------------------------
176 //-----------------------------------------------------------------------------
177 //-----------------------------------------------------------------------------
178 
179 /** @addtogroup doc_error_handling
180  *
181  * @{ */
182 
183 
184 /** generic formatting of a location
185  *
186  * @param dumpfn function taking a csubstr and abstracting a string
187  * concatenation operation, such as appending to a std::string or
188  * printing to terminal.
189  * @param loc the location to be formatted
190  *
191  * For example:
192  *
193  * ```c++
194  * /// to output to std::cerr:
195  * location_format([&s](csubstr s){
196  * std::cerr.write(s.str, s.len);
197  * }, loc);
198  *
199  * /// to build a string:
200  * std::string msg;
201  * location_format([&s](csubstr s){
202  * msg.append(s.str, s.len);
203  * }, loc);
204  * ```
205  */
206 template<class DumpFn>
207 C4_NO_INLINE size_t location_format(DumpFn &&dumpfn, Location const& loc);
208 
209 
210 /** Generic formatting of a location, printing the source code buffer
211  * region around the location.
212  *
213  * @param dumpfn function taking a csubstr and abstracting a string
214  * concatenation operation, such as appending to a std::string or
215  * printing to terminal.
216  * @param location the location
217  * @param source_buffer the source buffer
218  * @param call a string with a call of attention to print in the
219  * message (see examples below)
220  * @param num_lines_before how many source buffer lines to print
221  * before the location line
222  * @param num_lines_after how many source buffer lines to print
223  * after the location line
224  * @param first_col_highlight the first column to highlight
225  * around the location line
226  * @param last_col_highlight the last column to highlight
227  * around the location line
228  * @param maxlen the maximum number of columns to show in the error
229  * message; source buffer lines will have at most this number
230  * of columns shown; if the line is longer than this, the line
231  * will be trimmed as needed at the end and/or beginning, and
232  * only the relevant columns *around* the location are shown
233  *
234  * For example:
235  *
236  * ```c++
237  * std::string out;
238  * auto dumpfn = [&out](csubstr s){ out.append(s.str, s.len); };
239  * format_location_with_context(dumpfn, location, source, "error");
240  * ```
241  *
242  * will result in this string:
243  *
244  * ```
245  * file.yaml:3: col=3 (11B): error:
246  * error:
247  * error: ccc
248  * error: |
249  * error: (here)
250  * error:
251  * error: see region:
252  * error:
253  * error: aaa
254  * error: bbb
255  * error: ccc
256  * error: |
257  * error: (here)
258  * ```
259  *
260  * If an empty string is passed for the call of attention,
261  *
262  * ```c++
263  * format_location_with_context(dumpfn, location, source);
264  * ```
265  *
266  * the returned string becomes:
267  *
268  * ```
269  * file.yaml:3: col=3 (11B): ccc
270  * |
271  * (here)
272  * file.yaml:3: col=3 (11B): see region:
273  * aaa
274  * bbb
275  * ccc
276  * |
277  * (here)
278  * ```
279  */
280 template<class DumpFn>
281 C4_NO_INLINE void location_format_with_context(DumpFn &&dumpfn,
282  Location const &location,
283  csubstr source_buffer,
284  csubstr call = "",
285  size_t num_lines_before = 3,
286  size_t num_lines_after = 0,
287  size_t first_col_highlight = 0,
288  size_t last_col_highlight = 0,
289  size_t maxlen = 80u);
290 
291 
292 //-----------------------------------------------------------------------------
293 
294 /** Given an error message and associated basic error data, format it fully as a basic error message.
295  *
296  * @param dumpfn function taking a csubstr and abstracting a string
297  * concatenation operation, such as appending to a std::string or
298  * printing to terminal.
299  * @param msg the error message
300  * @param errdata the error data
301  *
302  * For example:
303  *
304  * ```c++
305  * /// to output to cerr:
306  * err_basic_format([](csubstr s){
307  * std::cerr.write(s.str, s.len);
308  * }, errmsg, errdata);
309  *
310  * /// to build a string:
311  * std::string msg;
312  * error_basic_format([&msg](csubstr s){
313  * msg.append(s.str, s.len);
314  * }, errmsg, errdata);
315  * ```
316  */
317 template<class DumpFn>
318 C4_NO_INLINE void err_basic_format(DumpFn &&dumpfn, csubstr msg, ErrorDataBasic const& errdata);
319 
320 /** trigger a basic error to its respective handler, with a non-formatted error message. */
321 C4_NORETURN RYML_EXPORT C4_NO_INLINE void err_basic(Callbacks const& callbacks, ErrorDataBasic const& errdata, const char* msg_);
322 /** trigger a basic error to its respective handler, with a non-formatted error message. Like (1), but use the current global callbacks. */
323 C4_NORETURN RYML_EXPORT C4_NO_INLINE void err_basic(ErrorDataBasic const& errdata, const char* msg);
324 /** trigger a basic error to its respective handler, with a formatted error message. */
325 template<class ...Args>
326 C4_NORETURN C4_NO_INLINE void err_basic(Callbacks const& callbacks, ErrorDataBasic const& errdata, const char *fmt, Args const& ...args)
327 {
328  char errbuf[RYML_ERRMSG_SIZE];
329  csubstr msg = detail::_mk_err_msg(errbuf, to_csubstr(fmt), args...);
330  callbacks.m_error_basic(msg, errdata, callbacks.m_user_data);
331  abort(); // the call above should not return, but force it here in case it does // LCOV_EXCL_LINE
332  C4_UNREACHABLE_AFTER_ERR();
333 }
334 /** trigger a basic error to its respective handler, with a formatted error message. Like (1), but use the current global callbacks. */
335 template<class ...Args>
336 C4_NORETURN C4_NO_INLINE void err_basic(ErrorDataBasic const& errdata, const char *fmt, Args const& ...args)
337 {
338  err_basic(get_callbacks(), errdata, fmt, args...);
339  C4_UNREACHABLE_AFTER_ERR();
340 }
341 
342 
343 /** Given an error message and associated parse error data, format it fully as a parse error message.
344  *
345  * @param dumpfn function taking a csubstr and abstracting a string
346  * concatenation operation, such as appending to a std::string or
347  * printing to terminal.
348  * @param msg the error message
349  * @param errdata the error data
350  *
351  * For example:
352  *
353  * ```c++
354  * /// to output to cerr:
355  * /// this is what err_parse_print() does
356  * err_parse_format([](csubstr s){
357  * std::cerr.write(s.str, s.len);
358  * }, errmsg, errdata);
359  *
360  * /// to build a string:
361  * std::string msg;
362  * err_parse_format([](csubstr s){
363  * s.append(s.str, s.len);
364  * }, errmsg, errdata);
365  * ```
366  *
367  * @note if the (preferably original) source buffer is kept, @ref
368  * location_format_with_context() can be used to also an additional
369  * rich error message showing the YAML source buffer region around
370  * that location.
371  */
372 template<class DumpFn>
373 C4_NO_INLINE void err_parse_format(DumpFn &&dumpfn, csubstr msg, ErrorDataParse const& errdata);
374 
375 /** trigger a parse error to its respective handler, with a non-formatted error message */
376 C4_NORETURN RYML_EXPORT C4_NO_INLINE void err_parse(Callbacks const& callbacks, ErrorDataParse const& errdata, const char *msg);
377 /** trigger a parse error to its respective handler, with a non-formatted error message. Like (1), but use the current global callbacks. */
378 C4_NORETURN RYML_EXPORT C4_NO_INLINE void err_parse(ErrorDataParse const& errdata, const char *msg);
379 /** trigger a parse error to its respective handler, with a formatted error message */
380 template<class ...Args>
381 C4_NORETURN C4_NO_INLINE void err_parse(Callbacks const& callbacks, ErrorDataParse const& errdata, const char *fmt, Args const& ...args)
382 {
383  char errbuf[RYML_ERRMSG_SIZE];
384  csubstr msg = detail::_mk_err_msg(errbuf, to_csubstr(fmt), args...);
385  if(callbacks.m_error_parse)
386  callbacks.m_error_parse(msg, errdata, callbacks.m_user_data);
387  // fall to basic error if there is no parse handler set, but use errdata.ymlloc instead of errdata.cpploc
388  else if(callbacks.m_error_basic)
389  callbacks.m_error_basic(msg, errdata.ymlloc, callbacks.m_user_data);
390  abort(); // the call above should not return, so force it here in case it does // LCOV_EXCL_LINE
391  C4_UNREACHABLE_AFTER_ERR();
392 }
393 /** trigger a parse error to its respective handler, with a formatted error message. Like (1), but use the current global callbacks. */
394 template<class ...Args>
395 C4_NORETURN C4_NO_INLINE void err_parse(ErrorDataParse const& errdata, const char *fmt, Args const& ...args)
396 {
397  err_parse(get_callbacks(), errdata, fmt, args...);
398  C4_UNREACHABLE_AFTER_ERR();
399 }
400 
401 
402 /** Given an error message and associated visit error data, format it
403  * fully as a visit error message.
404  *
405  * @param dumpfn function taking a csubstr and abstracting a string
406  * concatenation operation, such as appending to a std::string or
407  * printing to terminal.
408  * @param msg the error message
409  * @param errdata the error data
410  *
411  * For example:
412  *
413  * ```c++
414  * /// to output to cerr:
415  * err_visit_format([](csubstr s){
416  * std::cerr.write(s.str, s.len);
417  * }, errmsg, errdata);
418  *
419  * /// to build a string:
420  * std::string msg;
421  * err_visit_format([&msg](csubstr s){
422  * msg.append(s.str, s.len);
423  * }, errmsg, errdata);
424  *
425  * @note under certain conditions, it is possible to obtain an
426  * associated location, and subsequently use @ref
427  * location_format_with_context() to also create a rich error message
428  * showing the YAML source buffer region around that location. This is
429  * possible if the (preferably original) source buffer is kept, and
430  * the node location can be retrieved from the parser.
431  * ```
432  */
433 template<class DumpFn>
434 C4_NO_INLINE void err_visit_format(DumpFn &&dumpfn, csubstr msg, ErrorDataVisit const& errdata);
435 
436 
437 /** trigger a visit error to its respective handler, with a non-formatted error message */
438 C4_NORETURN RYML_EXPORT C4_NO_INLINE void err_visit(Callbacks const& callbacks, ErrorDataVisit const& errdata, const char *msg);
439 /** trigger a visit error to its respective handler, with a non-formatted error message. Like (1), but uses the current global callbacks. */
440 C4_NORETURN RYML_EXPORT C4_NO_INLINE void err_visit(ErrorDataVisit const& errdata, const char *msg);
441 /** trigger a visit error to its respective handler, with a formatted error message */
442 template<class ...Args>
443 C4_NORETURN C4_NO_INLINE void err_visit(Callbacks const& callbacks, ErrorDataVisit const& errdata, const char *fmt, Args const& ...args)
444 {
445  char errbuf[RYML_ERRMSG_SIZE];
446  csubstr msg = detail::_mk_err_msg(errbuf, to_csubstr(fmt), args...);
447  if(callbacks.m_error_visit)
448  callbacks.m_error_visit(msg, errdata, callbacks.m_user_data);
449  // fall to basic error if there is no visit handler set
450  else if(callbacks.m_error_basic)
451  callbacks.m_error_basic(msg, errdata.cpploc, callbacks.m_user_data);
452  abort(); // the call above should not return, so force it here in case it does // LCOV_EXCL_LINE
453  C4_UNREACHABLE_AFTER_ERR();
454 }
455 /** trigger a visit error to its respective handler, with a formatted error message. Like (1), but use the current global callbacks. */
456 template<class ...Args>
457 C4_NORETURN C4_NO_INLINE void err_visit(ErrorDataVisit const& errdata, const char *fmt, Args const& ...args)
458 {
459  err_visit(get_callbacks(), errdata, fmt, args...);
460  C4_UNREACHABLE_AFTER_ERR();
461 }
462 
463 
464 //-----------------------------------------------------------------------------
465 
466 #if defined(_RYML_WITH_EXCEPTIONS) || defined(__DOXYGEN__)
467 
468 /** Exception thrown by the default basic error implementation. To
469  * obtain the full error message, use @ref err_basic_format(), or the
470  * helper @ref format_exc().
471  *
472  * @note Available only if @ref
473  * RYML_DEFAULT_CALLBACK_USES_EXCEPTIONS is defined, and @ref
474  * RYML_NO_DEFAULT_CALLBACKS is NOT defined. */
475 struct RYML_EXPORT ExceptionBasic : public std::exception
476 {
477  ExceptionBasic(csubstr msg, ErrorDataBasic const& errdata_) noexcept;
478  const char* what() const noexcept override { return msg; }
479  ErrorDataBasic errdata_basic; ///< error data
480  char msg[RYML_ERRMSG_SIZE]; ///< the reported error message, without location indication.
481 };
482 
483 
484 /** Exception thrown by the default parse error implementation. To
485  * obtain the full error message containing context, use @ref
486  * err_parse_format(), or the helper @ref format_exc().
487  *
488  * @note This exception derives from @ref ExceptionBasic and can be
489  * catched using either type.
490  *
491  * @note Available only if @ref
492  * RYML_DEFAULT_CALLBACK_USES_EXCEPTIONS is defined, and @ref
493  * RYML_NO_DEFAULT_CALLBACKS is NOT defined. */
495 {
496  ExceptionParse(csubstr msg, ErrorDataParse const& errdata_) noexcept;
498 };
499 
500 
501 /** Exception thrown by the default visit error implementation. To
502  * obtain the full error message containing context, use @ref
503  * err_visit_format(), or the helper @ref format_exc().
504  *
505  * @note This exception derives from @ref ExceptionBasic and can be
506  * catched using either type.
507  *
508  * @note Available only if @ref
509  * RYML_DEFAULT_CALLBACK_USES_EXCEPTIONS is defined, and @ref
510  * RYML_NO_DEFAULT_CALLBACKS is NOT defined. */
512 {
513  ExceptionVisit(csubstr msg, ErrorDataVisit const& errdata_) noexcept;
515 };
516 
517 
518 /** Format a basic exception to an existing char container
519  *
520  * @note Available only if @ref
521  * RYML_DEFAULT_CALLBACK_USES_EXCEPTIONS is defined, and @ref
522  * RYML_NO_DEFAULT_CALLBACKS is NOT defined. */
523 template<class CharContainer>
524 void format_exc(CharContainer *out, ExceptionBasic const& exc)
525 {
526  out->clear();
527  err_basic_format([out](csubstr s){
528  out->append(s.str, s.len);
529  }, csubstr{exc.msg, strlen(exc.msg)}, exc.errdata_basic);
530 }
531 /** Format a parse exception to an existing char container
532  *
533  * @note Available only if @ref
534  * RYML_DEFAULT_CALLBACK_USES_EXCEPTIONS is defined, and @ref
535  * RYML_NO_DEFAULT_CALLBACKS is NOT defined. */
536 template<class CharContainer>
537 void format_exc(CharContainer *out, ExceptionParse const& exc)
538 {
539  out->clear();
540  err_parse_format([out](csubstr s){
541  out->append(s.str, s.len);
542  }, csubstr{exc.msg, strlen(exc.msg)}, exc.errdata_parse);
543 }
544 /** Format a visit exception to an existing char container
545  *
546  * @note Available only if @ref
547  * RYML_DEFAULT_CALLBACK_USES_EXCEPTIONS is defined, and @ref
548  * RYML_NO_DEFAULT_CALLBACKS is NOT defined. */
549 template<class CharContainer>
550 void format_exc(CharContainer *out, ExceptionVisit const& exc)
551 {
552  out->clear();
553  err_visit_format([out](csubstr s){
554  out->append(s.str, s.len);
555  }, csubstr{exc.msg, strlen(exc.msg)}, exc.errdata_visit);
556 }
557 /** Format a parse exception, and return a newly-created char
558  * container
559  *
560  * @note Available only if @ref
561  * RYML_DEFAULT_CALLBACK_USES_EXCEPTIONS is defined, and @ref
562  * RYML_NO_DEFAULT_CALLBACKS is NOT defined. */
563 template<class CharContainer, class ExceptionT>
564 CharContainer format_exc(ExceptionT const& exc)
565 {
566  CharContainer str;
567  format_exc(&str, exc);
568  return str;
569 }
570 
571 #endif // _RYML_WITH_EXCEPTIONS
572 
573 /** @} */
574 
575 
576 //-----------------------------------------------------------------------------
577 //-----------------------------------------------------------------------------
578 //-----------------------------------------------------------------------------
579 
580 /// @cond dev
581 
582 
583 #if RYML_USE_ASSERT
584 # define _RYML_ASSERT_BASIC(cond) _RYML_CHECK_BASIC(cond)
585 # define _RYML_ASSERT_BASIC_(cb, cond) _RYML_CHECK_BASIC_((cb), cond)
586 # define _RYML_ASSERT_BASIC_MSG(cond, ...) _RYML_CHECK_BASIC_MSG(cond, __VA_ARGS__)
587 # define _RYML_ASSERT_BASIC_MSG_(cb, cond, ...) _RYML_CHECK_BASIC_MSG_((cb), cond, __VA_ARGS__)
588 # define _RYML_ASSERT_PARSE(cond, ymlloc) _RYML_CHECK_PARSE(cond, (ymlloc))
589 # define _RYML_ASSERT_PARSE_(cb, cond, ymlloc) _RYML_CHECK_PARSE_((cb), cond, (ymlloc))
590 # define _RYML_ASSERT_PARSE_MSG(cond, ymlloc, ...) _RYML_CHECK_PARSE_MSG(cond, (ymlloc), __VA_ARGS__)
591 # define _RYML_ASSERT_PARSE_MSG_(cb, cond, ymlloc, ...) _RYML_CHECK_PARSE_MSG_((cb), cond, (ymlloc), __VA_ARGS__)
592 # define _RYML_ASSERT_VISIT(cond, tree, node) _RYML_CHECK_VISIT(cond, (tree), (node))
593 # define _RYML_ASSERT_VISIT_(cb, cond, tree, node) _RYML_CHECK_VISIT_((cb), cond, (tree), (node))
594 # define _RYML_ASSERT_VISIT_MSG(cond, tree, node, ...) _RYML_CHECK_VISIT_MSG(cond, (tree), (node), __VA_ARGS__)
595 # define _RYML_ASSERT_VISIT_MSG_(cb, cond, tree, node, ...) _RYML_CHECK_VISIT_MSG_((cb), cond, (tree), (node), __VA_ARGS__)
596 #else
597 # define _RYML_ASSERT_BASIC(cond)
598 # define _RYML_ASSERT_BASIC_(cb, cond)
599 # define _RYML_ASSERT_BASIC_MSG(cond, ...)
600 # define _RYML_ASSERT_BASIC_MSG_(cb, cond, ...)
601 # define _RYML_ASSERT_PARSE(cond, ymlloc)
602 # define _RYML_ASSERT_PARSE_(cb, cond, ymlloc)
603 # define _RYML_ASSERT_PARSE_MSG(cond, ymlloc, ...)
604 # define _RYML_ASSERT_PARSE_MSG_(cb, cond, ymlloc, ...)
605 # define _RYML_ASSERT_VISIT(cont, tree, node)
606 # define _RYML_ASSERT_VISIT_(cb, cont, tree, node)
607 # define _RYML_ASSERT_VISIT_MSG(cont, tree, node, ...)
608 # define _RYML_ASSERT_VISIT_MSG_(cb, cont, tree, node, ...)
609 #endif
610 
611 #define _RYML_ERR_BASIC(...) \
612  do \
613  { \
614  RYML_DEBUG_BREAK(); \
615  ::c4::yml::err_basic((::c4::yml::ErrorDataBasic{RYML_LOC_HERE()}), __VA_ARGS__); \
616  C4_UNREACHABLE_AFTER_ERR(); \
617  } while(false)
618 #define _RYML_ERR_PARSE(ymlloc, ...) \
619  do \
620  { \
621  RYML_DEBUG_BREAK(); \
622  ::c4::yml::err_parse((::c4::yml::ErrorDataParse{RYML_LOC_HERE(), ymlloc}), __VA_ARGS__); \
623  C4_UNREACHABLE_AFTER_ERR(); \
624  } while(false)
625 #define _RYML_ERR_VISIT(tree, node, ...) \
626  do \
627  { \
628  RYML_DEBUG_BREAK(); \
629  ::c4::yml::err_visit((::c4::yml::ErrorDataVisit{RYML_LOC_HERE(), tree, node}), __VA_ARGS__); \
630  C4_UNREACHABLE_AFTER_ERR(); \
631  } while(false)
632 
633 
634 #define _RYML_ERR_BASIC_(cb, ...) \
635  do \
636  { \
637  RYML_DEBUG_BREAK(); \
638  ::c4::yml::err_basic((cb), (::c4::yml::ErrorDataBasic{RYML_LOC_HERE()}), __VA_ARGS__); \
639  C4_UNREACHABLE_AFTER_ERR(); \
640  } while(false)
641 #define _RYML_ERR_PARSE_(cb, ymlloc, ...) \
642  do \
643  { \
644  RYML_DEBUG_BREAK(); \
645  ::c4::yml::err_parse((cb), (::c4::yml::ErrorDataParse{RYML_LOC_HERE(), ymlloc}), __VA_ARGS__); \
646  C4_UNREACHABLE_AFTER_ERR(); \
647  } while(false)
648 #define _RYML_ERR_VISIT_(cb, tree, node, ...) \
649  do \
650  { \
651  RYML_DEBUG_BREAK(); \
652  ::c4::yml::err_visit((cb), (::c4::yml::ErrorDataVisit{RYML_LOC_HERE(), tree, node}), __VA_ARGS__); \
653  C4_UNREACHABLE_AFTER_ERR(); \
654  } while(false)
655 
656 
657 #ifndef RYML_SHORT_CHECK_MSG
658 #define _RYML_MAYBE_MSG(cond) ": " #cond
659 #define _RYML_MAYBE_MSG_(cond) ": " #cond ": "
660 #else
661 #define _RYML_MAYBE_MSG(cond)
662 #define _RYML_MAYBE_MSG_(cond) ": "
663 #endif
664 
665 #define _RYML_CHECK_BASIC(cond) \
666  do { \
667  if(C4_UNLIKELY(!(cond))) \
668  { \
669  RYML_DEBUG_BREAK(); \
670  ::c4::yml::err_basic((::c4::yml::ErrorDataBasic{RYML_LOC_HERE()}), "check failed" _RYML_MAYBE_MSG(cond)); \
671  C4_UNREACHABLE_AFTER_ERR(); \
672  } \
673  } while(false)
674 #define _RYML_CHECK_PARSE(cond, ymlloc) \
675  do { \
676  if(C4_UNLIKELY(!(cond))) \
677  { \
678  RYML_DEBUG_BREAK(); \
679  ::c4::yml::err_parse((::c4::yml::ErrorDataParse{RYML_LOC_HERE(), ymlloc}), "check failed" _RYML_MAYBE_MSG(cond)); \
680  C4_UNREACHABLE_AFTER_ERR(); \
681  } \
682  } while(false)
683 #define _RYML_CHECK_VISIT(cond, tree, node) \
684  do { \
685  if(C4_UNLIKELY(!(cond))) \
686  { \
687  RYML_DEBUG_BREAK(); \
688  ::c4::yml::err_visit((::c4::yml::ErrorDataVisit{RYML_LOC_HERE(), tree, node}), "check failed" _RYML_MAYBE_MSG(cond)); \
689  C4_UNREACHABLE_AFTER_ERR(); \
690  } \
691  } while(false)
692 
693 
694 #define _RYML_CHECK_BASIC_(cb, cond) \
695  do { \
696  if(C4_UNLIKELY(!(cond))) \
697  { \
698  RYML_DEBUG_BREAK(); \
699  ::c4::yml::err_basic((cb), (::c4::yml::ErrorDataBasic{RYML_LOC_HERE()}), "check failed" _RYML_MAYBE_MSG(cond)); \
700  C4_UNREACHABLE_AFTER_ERR(); \
701  } \
702  } while(false)
703 #define _RYML_CHECK_PARSE_(cb, cond, ymlloc) \
704  do { \
705  if(C4_UNLIKELY(!(cond))) \
706  { \
707  RYML_DEBUG_BREAK(); \
708  ::c4::yml::err_parse((cb), (::c4::yml::ErrorDataParse{RYML_LOC_HERE(), ymlloc}), "check failed" _RYML_MAYBE_MSG(cond)); \
709  C4_UNREACHABLE_AFTER_ERR(); \
710  } \
711  } while(false)
712 #define _RYML_CHECK_VISIT_(cb, cond, tree, node) \
713  do { \
714  if(C4_UNLIKELY(!(cond))) \
715  { \
716  RYML_DEBUG_BREAK(); \
717  ::c4::yml::err_visit((cb), (::c4::yml::ErrorDataVisit{RYML_LOC_HERE(), tree, node}), "check failed" _RYML_MAYBE_MSG(cond)); \
718  C4_UNREACHABLE_AFTER_ERR(); \
719  } \
720  } while(false)
721 
722 
723 #define _RYML_CHECK_BASIC_MSG(cond, ...) \
724  do { \
725  if(C4_UNLIKELY(!(cond))) \
726  { \
727  RYML_DEBUG_BREAK(); \
728  ::c4::yml::err_basic((::c4::yml::ErrorDataBasic{RYML_LOC_HERE()}), "check failed" _RYML_MAYBE_MSG_(cond) __VA_ARGS__); \
729  C4_UNREACHABLE_AFTER_ERR(); \
730  } \
731  } while(false)
732 #define _RYML_CHECK_PARSE_MSG(cond, ymlloc, ...) \
733  do { \
734  if(C4_UNLIKELY(!(cond))) \
735  { \
736  RYML_DEBUG_BREAK(); \
737  ::c4::yml::err_parse((::c4::yml::ErrorDataParse{RYML_LOC_HERE(), ymlloc}), "check failed" _RYML_MAYBE_MSG_(cond) __VA_ARGS__); \
738  C4_UNREACHABLE_AFTER_ERR(); \
739  } \
740  } while(false)
741 #define _RYML_CHECK_VISIT_MSG(cond, tree, node, ...) \
742  do { \
743  if(C4_UNLIKELY(!(cond))) \
744  { \
745  RYML_DEBUG_BREAK(); \
746  ::c4::yml::err_visit((::c4::yml::ErrorDataVisit{RYML_LOC_HERE(), tree, node}), "check failed" _RYML_MAYBE_MSG_(cond) __VA_ARGS__); \
747  C4_UNREACHABLE_AFTER_ERR(); \
748  } \
749  } while(false)
750 
751 
752 #define _RYML_CHECK_BASIC_MSG_(cb, cond, ...) \
753  do { \
754  if(C4_UNLIKELY(!(cond))) \
755  { \
756  RYML_DEBUG_BREAK(); \
757  ::c4::yml::err_basic((cb), (::c4::yml::ErrorDataBasic{RYML_LOC_HERE()}), "check failed" _RYML_MAYBE_MSG_(cond) __VA_ARGS__); \
758  C4_UNREACHABLE_AFTER_ERR(); \
759  } \
760  } while(false)
761 #define _RYML_CHECK_PARSE_MSG_(cb, cond, ymlloc, ...) \
762  do { \
763  if(C4_UNLIKELY(!(cond))) \
764  { \
765  RYML_DEBUG_BREAK(); \
766  ::c4::yml::err_parse((cb), (::c4::yml::ErrorDataParse{RYML_LOC_HERE(), ymlloc}), "check failed" _RYML_MAYBE_MSG_(cond) __VA_ARGS__); \
767  C4_UNREACHABLE_AFTER_ERR(); \
768  } \
769  } while(false)
770 #define _RYML_CHECK_VISIT_MSG_(cb, cond, tree, node, ...) \
771  do { \
772  if(C4_UNLIKELY(!(cond))) \
773  { \
774  RYML_DEBUG_BREAK(); \
775  ::c4::yml::err_visit((cb), (::c4::yml::ErrorDataVisit{RYML_LOC_HERE(), tree, node}), "check failed" _RYML_MAYBE_MSG_(cond) __VA_ARGS__); \
776  C4_UNREACHABLE_AFTER_ERR(); \
777  } \
778  } while(false)
779 
780 /// @endcond
781 
782 } // namespace yml
783 } // namespace c4
784 
785 #endif /* _C4_YML_ERROR_HPP_ */
Common utilities and infrastructure used by ryml.
#define RYML_ERRMSG_SIZE
size for the error message buffer
Definition: common.hpp:34
#define RYML_LOGBUF_SIZE
size for the buffer used to format individual values to string while preparing an error message....
Definition: common.hpp:44
#define RYML_EXPORT
Definition: export.hpp:15
Callbacks const & get_callbacks()
get the global callbacks
Definition: common.cpp:94
size_t location_format(DumpFn &&dumpfn, Location const &loc)
generic formatting of a location
Definition: error.def.hpp:16
void err_visit(ErrorDataVisit const &errdata, const char *msg)
trigger a visit error to its respective handler, with a non-formatted error message.
Definition: common.cpp:228
void err_visit_format(DumpFn &&dumpfn, csubstr msg, ErrorDataVisit const &errdata)
Given an error message and associated visit error data, format it fully as a visit error message.
Definition: error.def.hpp:348
void err_basic(ErrorDataBasic const &errdata, const char *msg)
trigger a basic error to its respective handler, with a non-formatted error message.
Definition: common.cpp:196
void location_format_with_context(DumpFn &&dumpfn, Location const &location, csubstr source_buffer, csubstr call, size_t num_lines_before, size_t num_lines_after, size_t first_col_highlight, size_t last_col_highlight, size_t maxlen)
Generic formatting of a location, printing the source code buffer region around the location.
Definition: error.def.hpp:84
void err_basic_format(DumpFn &&dumpfn, csubstr msg, ErrorDataBasic const &errdata)
Given an error message and associated basic error data, format it fully as a basic error message.
Definition: error.def.hpp:316
void err_parse_format(DumpFn &&dumpfn, csubstr msg, ErrorDataParse const &errdata)
Given an error message and associated parse error data, format it fully as a parse error message.
Definition: error.def.hpp:329
void format_exc(CharContainer *out, ExceptionBasic const &exc)
Format a basic exception to an existing char container.
Definition: error.hpp:524
void err_parse(ErrorDataParse const &errdata, const char *msg)
trigger a parse error to its respective handler, with a non-formatted error message.
Definition: common.cpp:210
csubstr to_csubstr(substr s) noexcept
neutral version for use in generic code
Definition: substr.hpp:2210
csubstr _get_text_region(csubstr text, size_t pos, size_t num_lines_before, size_t num_lines_after)
Definition: common.cpp:283
@ npos
a null string position
Definition: common.hpp:258
size_t to_chars(substr buf, escaped_scalar e)
formatting implementation to escape a scalar with escape_scalar()
(Undefined by default) Use shorter error message from checks/asserts: do not show the check condition...
Definition: common.cpp:14
a traits class to mark a type as a string type (meaning c4::to_csubstr() can be used directly).
Definition: substr.hpp:2245
A c-style callbacks class to customize behavior on errors or allocation.
Definition: common.hpp:538
pfn_error_basic m_error_basic
a pointer to a basic error handler function
Definition: common.hpp:542
pfn_error_parse m_error_parse
a pointer to a parse error handler function
Definition: common.hpp:543
void * m_user_data
data to be forwarded in every call to a callback
Definition: common.hpp:539
pfn_error_visit m_error_visit
a pointer to a visit error handler function
Definition: common.hpp:544
Data for a basic error.
Definition: common.hpp:312
Data for a parse error.
Definition: common.hpp:321
Location ymlloc
location in the YAML source buffer where the error was detected.
Definition: common.hpp:323
Data for a visit error.
Definition: common.hpp:331
Location cpploc
location in the C++ source file where the error was detected.
Definition: common.hpp:332
Exception thrown by the default basic error implementation.
Definition: error.hpp:476
ErrorDataBasic errdata_basic
error data
Definition: error.hpp:479
ExceptionBasic(csubstr msg, ErrorDataBasic const &errdata_) noexcept
char msg[RYML_ERRMSG_SIZE]
the reported error message, without location indication.
Definition: error.hpp:480
const char * what() const noexcept override
Definition: error.hpp:478
Exception thrown by the default parse error implementation.
Definition: error.hpp:495
ExceptionParse(csubstr msg, ErrorDataParse const &errdata_) noexcept
ErrorDataParse errdata_parse
Definition: error.hpp:497
Exception thrown by the default visit error implementation.
Definition: error.hpp:512
ErrorDataVisit errdata_visit
Definition: error.hpp:514
ExceptionVisit(csubstr msg, ErrorDataVisit const &errdata_) noexcept