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