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