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