error.hpp (14967B)
1 #ifndef _C4_ERROR_HPP_ 2 #define _C4_ERROR_HPP_ 3 4 /** @file error.hpp Facilities for error reporting and runtime assertions. */ 5 6 /** @defgroup error_checking Error checking */ 7 8 #include "c4/config.hpp" 9 10 #ifdef _DOXYGEN_ 11 /** if this is defined and exceptions are enabled, then calls to C4_ERROR() 12 * will throw an exception 13 * @ingroup error_checking */ 14 # define C4_EXCEPTIONS_ENABLED 15 /** if this is defined and exceptions are enabled, then calls to C4_ERROR() 16 * will throw an exception 17 * @see C4_EXCEPTIONS_ENABLED 18 * @ingroup error_checking */ 19 # define C4_ERROR_THROWS_EXCEPTION 20 /** evaluates to noexcept when C4_ERROR might be called and 21 * exceptions are disabled. Otherwise, defaults to nothing. 22 * @ingroup error_checking */ 23 # define C4_NOEXCEPT 24 #endif // _DOXYGEN_ 25 26 #if defined(C4_EXCEPTIONS_ENABLED) && defined(C4_ERROR_THROWS_EXCEPTION) 27 # define C4_NOEXCEPT 28 #else 29 # define C4_NOEXCEPT noexcept 30 #endif 31 32 33 namespace c4 { 34 namespace detail { 35 struct fail_type__ {}; 36 } // detail 37 } // c4 38 #define C4_STATIC_ERROR(dummy_type, errmsg) \ 39 static_assert(std::is_same<dummy_type, c4::detail::fail_type__>::value, errmsg) 40 41 42 //----------------------------------------------------------------------------- 43 44 #define C4_ASSERT_SAME_TYPE(ty1, ty2) \ 45 C4_STATIC_ASSERT(std::is_same<ty1 C4_COMMA_X ty2>::value) 46 47 #define C4_ASSERT_DIFF_TYPE(ty1, ty2) \ 48 C4_STATIC_ASSERT( ! std::is_same<ty1 C4_COMMA_X ty2>::value) 49 50 51 //----------------------------------------------------------------------------- 52 53 #ifdef _DOXYGEN_ 54 /** utility macro that triggers a breakpoint when 55 * the debugger is attached and NDEBUG is not defined. 56 * @ingroup error_checking */ 57 # define C4_DEBUG_BREAK() 58 #endif // _DOXYGEN_ 59 60 61 #if defined(NDEBUG) || defined(C4_NO_DEBUG_BREAK) 62 # define C4_DEBUG_BREAK() 63 #else 64 # ifdef __clang__ 65 # pragma clang diagnostic push 66 # if !defined(__APPLE_CC__) 67 # if __clang_major__ >= 10 68 # pragma clang diagnostic ignored "-Wgnu-inline-cpp-without-extern" // debugbreak/debugbreak.h:50:16: error: 'gnu_inline' attribute without 'extern' in C++ treated as externally available, this changed in Clang 10 [-Werror,-Wgnu-inline-cpp-without-extern] 69 # endif 70 # else 71 # if __clang_major__ >= 13 72 # pragma clang diagnostic ignored "-Wgnu-inline-cpp-without-extern" // debugbreak/debugbreak.h:50:16: error: 'gnu_inline' attribute without 'extern' in C++ treated as externally available, this changed in Clang 10 [-Werror,-Wgnu-inline-cpp-without-extern] 73 # endif 74 # endif 75 # elif defined(__GNUC__) 76 # endif 77 # include <c4/ext/debugbreak/debugbreak.h> 78 # define C4_DEBUG_BREAK() if(c4::is_debugger_attached()) { ::debug_break(); } 79 # ifdef __clang__ 80 # pragma clang diagnostic pop 81 # elif defined(__GNUC__) 82 # endif 83 #endif 84 85 namespace c4 { 86 C4CORE_EXPORT bool is_debugger_attached(); 87 } // namespace c4 88 89 90 //----------------------------------------------------------------------------- 91 92 #ifdef __clang__ 93 /* NOTE: using , ## __VA_ARGS__ to deal with zero-args calls to 94 * variadic macros is not portable, but works in clang, gcc, msvc, icc. 95 * clang requires switching off compiler warnings for pedantic mode. 96 * @see http://stackoverflow.com/questions/32047685/variadic-macro-without-arguments */ 97 # pragma clang diagnostic push 98 # pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments" // warning: token pasting of ',' and __VA_ARGS__ is a GNU extension 99 #elif defined(__GNUC__) 100 /* GCC also issues a warning for zero-args calls to variadic macros. 101 * This warning is switched on with -pedantic and apparently there is no 102 * easy way to turn it off as with clang. But marking this as a system 103 * header works. 104 * @see https://gcc.gnu.org/onlinedocs/cpp/System-Headers.html 105 * @see http://stackoverflow.com/questions/35587137/ */ 106 # pragma GCC system_header 107 #endif 108 109 110 //----------------------------------------------------------------------------- 111 112 namespace c4 { 113 114 typedef enum : uint32_t { 115 /** when an error happens and the debugger is attached, call C4_DEBUG_BREAK(). 116 * Without effect otherwise. */ 117 ON_ERROR_DEBUGBREAK = 0x01 << 0, 118 /** when an error happens log a message. */ 119 ON_ERROR_LOG = 0x01 << 1, 120 /** when an error happens invoke a callback if it was set with 121 * set_error_callback(). */ 122 ON_ERROR_CALLBACK = 0x01 << 2, 123 /** when an error happens call std::terminate(). */ 124 ON_ERROR_ABORT = 0x01 << 3, 125 /** when an error happens and exceptions are enabled throw an exception. 126 * Without effect otherwise. */ 127 ON_ERROR_THROW = 0x01 << 4, 128 /** the default flags. */ 129 ON_ERROR_DEFAULTS = ON_ERROR_DEBUGBREAK|ON_ERROR_LOG|ON_ERROR_CALLBACK|ON_ERROR_ABORT 130 } ErrorFlags_e; 131 using error_flags = uint32_t; 132 C4CORE_EXPORT void set_error_flags(error_flags f); 133 C4CORE_EXPORT error_flags get_error_flags(); 134 135 136 using error_callback_type = void (*)(const char* msg, size_t msg_size); 137 C4CORE_EXPORT void set_error_callback(error_callback_type cb); 138 C4CORE_EXPORT error_callback_type get_error_callback(); 139 140 141 //----------------------------------------------------------------------------- 142 /** RAII class controling the error settings inside a scope. */ 143 struct ScopedErrorSettings 144 { 145 error_flags m_flags; 146 error_callback_type m_callback; 147 148 explicit ScopedErrorSettings(error_callback_type cb) 149 : m_flags(get_error_flags()), 150 m_callback(get_error_callback()) 151 { 152 set_error_callback(cb); 153 } 154 explicit ScopedErrorSettings(error_flags flags) 155 : m_flags(get_error_flags()), 156 m_callback(get_error_callback()) 157 { 158 set_error_flags(flags); 159 } 160 explicit ScopedErrorSettings(error_flags flags, error_callback_type cb) 161 : m_flags(get_error_flags()), 162 m_callback(get_error_callback()) 163 { 164 set_error_flags(flags); 165 set_error_callback(cb); 166 } 167 ~ScopedErrorSettings() 168 { 169 set_error_flags(m_flags); 170 set_error_callback(m_callback); 171 } 172 }; 173 174 175 //----------------------------------------------------------------------------- 176 177 /** source location */ 178 struct srcloc; 179 180 C4CORE_EXPORT void handle_error(srcloc s, const char *fmt, ...); 181 C4CORE_EXPORT void handle_warning(srcloc s, const char *fmt, ...); 182 183 184 # define C4_ERROR(msg, ...) \ 185 do { \ 186 if(c4::get_error_flags() & c4::ON_ERROR_DEBUGBREAK) \ 187 { \ 188 C4_DEBUG_BREAK() \ 189 } \ 190 c4::handle_error(C4_SRCLOC(), msg, ## __VA_ARGS__); \ 191 } while(0) 192 193 194 # define C4_WARNING(msg, ...) \ 195 c4::handle_warning(C4_SRCLOC(), msg, ## __VA_ARGS__) 196 197 198 #if defined(C4_ERROR_SHOWS_FILELINE) && defined(C4_ERROR_SHOWS_FUNC) 199 200 struct srcloc 201 { 202 const char *file = ""; 203 const char *func = ""; 204 int line = 0; 205 }; 206 #define C4_SRCLOC() c4::srcloc{__FILE__, C4_PRETTY_FUNC, __LINE__} 207 208 #elif defined(C4_ERROR_SHOWS_FILELINE) 209 210 struct srcloc 211 { 212 const char *file; 213 int line; 214 }; 215 #define C4_SRCLOC() c4::srcloc{__FILE__, __LINE__} 216 217 #elif ! defined(C4_ERROR_SHOWS_FUNC) 218 219 struct srcloc 220 { 221 }; 222 #define C4_SRCLOC() c4::srcloc() 223 224 #else 225 # error not implemented 226 #endif 227 228 229 //----------------------------------------------------------------------------- 230 // assertions 231 232 // Doxygen needs this so that only one definition counts 233 #ifdef _DOXYGEN_ 234 /** Explicitly enables assertions, independently of NDEBUG status. 235 * This is meant to allow enabling assertions even when NDEBUG is defined. 236 * Defaults to undefined. 237 * @ingroup error_checking */ 238 # define C4_USE_ASSERT 239 /** assert that a condition is true; this is turned off when NDEBUG 240 * is defined and C4_USE_ASSERT is not true. 241 * @ingroup error_checking */ 242 # define C4_ASSERT 243 /** same as C4_ASSERT(), additionally prints a printf-formatted message 244 * @ingroup error_checking */ 245 # define C4_ASSERT_MSG 246 /** evaluates to C4_NOEXCEPT when C4_XASSERT is disabled; otherwise, defaults 247 * to noexcept 248 * @ingroup error_checking */ 249 # define C4_NOEXCEPT_A 250 #endif // _DOXYGEN_ 251 252 #ifndef C4_USE_ASSERT 253 # ifdef NDEBUG 254 # define C4_USE_ASSERT 0 255 # else 256 # define C4_USE_ASSERT 1 257 # endif 258 #endif 259 260 #if C4_USE_ASSERT 261 # define C4_ASSERT(cond) C4_CHECK(cond) 262 # define C4_ASSERT_MSG(cond, /*fmt, */...) C4_CHECK_MSG(cond, ## __VA_ARGS__) 263 # define C4_ASSERT_IF(predicate, cond) if(predicate) { C4_ASSERT(cond); } 264 # define C4_NOEXCEPT_A C4_NOEXCEPT 265 #else 266 # define C4_ASSERT(cond) 267 # define C4_ASSERT_MSG(cond, /*fmt, */...) 268 # define C4_ASSERT_IF(predicate, cond) 269 # define C4_NOEXCEPT_A noexcept 270 #endif 271 272 273 //----------------------------------------------------------------------------- 274 // extreme assertions 275 276 // Doxygen needs this so that only one definition counts 277 #ifdef _DOXYGEN_ 278 /** Explicitly enables extreme assertions; this is meant to allow enabling 279 * assertions even when NDEBUG is defined. Defaults to undefined. 280 * @ingroup error_checking */ 281 # define C4_USE_XASSERT 282 /** extreme assertion: can be switched off independently of 283 * the regular assertion; use for example for bounds checking in hot code. 284 * Turned on only when C4_USE_XASSERT is defined 285 * @ingroup error_checking */ 286 # define C4_XASSERT 287 /** same as C4_XASSERT(), and additionally prints a printf-formatted message 288 * @ingroup error_checking */ 289 # define C4_XASSERT_MSG 290 /** evaluates to C4_NOEXCEPT when C4_XASSERT is disabled; otherwise, defaults to noexcept 291 * @ingroup error_checking */ 292 # define C4_NOEXCEPT_X 293 #endif // _DOXYGEN_ 294 295 #ifndef C4_USE_XASSERT 296 # define C4_USE_XASSERT C4_USE_ASSERT 297 #endif 298 299 #if C4_USE_XASSERT 300 # define C4_XASSERT(cond) C4_CHECK(cond) 301 # define C4_XASSERT_MSG(cond, /*fmt, */...) C4_CHECK_MSG(cond, ## __VA_ARGS__) 302 # define C4_XASSERT_IF(predicate, cond) if(predicate) { C4_XASSERT(cond); } 303 # define C4_NOEXCEPT_X C4_NOEXCEPT 304 #else 305 # define C4_XASSERT(cond) 306 # define C4_XASSERT_MSG(cond, /*fmt, */...) 307 # define C4_XASSERT_IF(predicate, cond) 308 # define C4_NOEXCEPT_X noexcept 309 #endif 310 311 312 //----------------------------------------------------------------------------- 313 // checks: never switched-off 314 315 /** Check that a condition is true, or raise an error when not 316 * true. Unlike C4_ASSERT(), this check is not disabled in non-debug 317 * builds. 318 * @see C4_ASSERT 319 * @ingroup error_checking 320 * 321 * @todo add constexpr-compatible compile-time assert: 322 * https://akrzemi1.wordpress.com/2017/05/18/asserts-in-constexpr-functions/ 323 */ 324 #define C4_CHECK(cond) \ 325 do { \ 326 if(C4_UNLIKELY(!(cond))) \ 327 { \ 328 C4_ERROR("check failed: %s", #cond); \ 329 } \ 330 } while(0) 331 332 333 /** like C4_CHECK(), and additionally log a printf-style message. 334 * @see C4_CHECK 335 * @ingroup error_checking */ 336 #define C4_CHECK_MSG(cond, fmt, ...) \ 337 do { \ 338 if(C4_UNLIKELY(!(cond))) \ 339 { \ 340 C4_ERROR("check failed: " #cond "\n" fmt, ## __VA_ARGS__); \ 341 } \ 342 } while(0) 343 344 345 //----------------------------------------------------------------------------- 346 // Common error conditions 347 348 #define C4_NOT_IMPLEMENTED() C4_ERROR("NOT IMPLEMENTED") 349 #define C4_NOT_IMPLEMENTED_MSG(/*msg, */...) C4_ERROR("NOT IMPLEMENTED: " __VA_ARGS__) 350 #define C4_NOT_IMPLEMENTED_IF(condition) do { if(C4_UNLIKELY(condition)) { C4_ERROR("NOT IMPLEMENTED"); } } while(0) 351 #define C4_NOT_IMPLEMENTED_IF_MSG(condition, /*msg, */...) do { if(C4_UNLIKELY(condition)) { C4_ERROR("NOT IMPLEMENTED: " __VA_ARGS__); } } while(0) 352 353 #define C4_NEVER_REACH() do { C4_ERROR("never reach this point"); C4_UNREACHABLE(); } while(0) 354 #define C4_NEVER_REACH_MSG(/*msg, */...) do { C4_ERROR("never reach this point: " __VA_ARGS__); C4_UNREACHABLE(); } while(0) 355 356 357 358 //----------------------------------------------------------------------------- 359 // helpers for warning suppression 360 // idea adapted from https://github.com/onqtam/doctest/ 361 362 // TODO: add C4_MESSAGE() https://stackoverflow.com/questions/18252351/custom-preprocessor-macro-for-a-conditional-pragma-message-xxx?rq=1 363 364 365 #ifdef C4_MSVC 366 #define C4_SUPPRESS_WARNING_MSVC_PUSH __pragma(warning(push)) 367 #define C4_SUPPRESS_WARNING_MSVC(w) __pragma(warning(disable : w)) 368 #define C4_SUPPRESS_WARNING_MSVC_POP __pragma(warning(pop)) 369 #else // C4_MSVC 370 #define C4_SUPPRESS_WARNING_MSVC_PUSH 371 #define C4_SUPPRESS_WARNING_MSVC(w) 372 #define C4_SUPPRESS_WARNING_MSVC_POP 373 #endif // C4_MSVC 374 375 376 #ifdef C4_CLANG 377 #define C4_PRAGMA_TO_STR(x) _Pragma(#x) 378 #define C4_SUPPRESS_WARNING_CLANG_PUSH _Pragma("clang diagnostic push") 379 #define C4_SUPPRESS_WARNING_CLANG(w) C4_PRAGMA_TO_STR(clang diagnostic ignored w) 380 #define C4_SUPPRESS_WARNING_CLANG_POP _Pragma("clang diagnostic pop") 381 #else // C4_CLANG 382 #define C4_SUPPRESS_WARNING_CLANG_PUSH 383 #define C4_SUPPRESS_WARNING_CLANG(w) 384 #define C4_SUPPRESS_WARNING_CLANG_POP 385 #endif // C4_CLANG 386 387 388 #ifdef C4_GCC 389 #define C4_PRAGMA_TO_STR(x) _Pragma(#x) 390 #define C4_SUPPRESS_WARNING_GCC_PUSH _Pragma("GCC diagnostic push") 391 #define C4_SUPPRESS_WARNING_GCC(w) C4_PRAGMA_TO_STR(GCC diagnostic ignored w) 392 #define C4_SUPPRESS_WARNING_GCC_POP _Pragma("GCC diagnostic pop") 393 #else // C4_GCC 394 #define C4_SUPPRESS_WARNING_GCC_PUSH 395 #define C4_SUPPRESS_WARNING_GCC(w) 396 #define C4_SUPPRESS_WARNING_GCC_POP 397 #endif // C4_GCC 398 399 400 #define C4_SUPPRESS_WARNING_MSVC_WITH_PUSH(w) \ 401 C4_SUPPRESS_WARNING_MSVC_PUSH \ 402 C4_SUPPRESS_WARNING_MSVC(w) 403 404 #define C4_SUPPRESS_WARNING_CLANG_WITH_PUSH(w) \ 405 C4_SUPPRESS_WARNING_CLANG_PUSH \ 406 C4_SUPPRESS_WARNING_CLANG(w) 407 408 #define C4_SUPPRESS_WARNING_GCC_WITH_PUSH(w) \ 409 C4_SUPPRESS_WARNING_GCC_PUSH \ 410 C4_SUPPRESS_WARNING_GCC(w) 411 412 413 #define C4_SUPPRESS_WARNING_GCC_CLANG_PUSH \ 414 C4_SUPPRESS_WARNING_GCC_PUSH \ 415 C4_SUPPRESS_WARNING_CLANG_PUSH 416 417 #define C4_SUPPRESS_WARNING_GCC_CLANG(w) \ 418 C4_SUPPRESS_WARNING_GCC(w) \ 419 C4_SUPPRESS_WARNING_CLANG(w) 420 421 #define C4_SUPPRESS_WARNING_GCC_CLANG_WITH_PUSH(w) \ 422 C4_SUPPRESS_WARNING_GCC_WITH_PUSH(w) \ 423 C4_SUPPRESS_WARNING_CLANG_WITH_PUSH(w) 424 425 #define C4_SUPPRESS_WARNING_GCC_CLANG_POP \ 426 C4_SUPPRESS_WARNING_GCC_POP \ 427 C4_SUPPRESS_WARNING_CLANG_POP 428 429 } // namespace c4 430 431 #ifdef __clang__ 432 # pragma clang diagnostic pop 433 #endif 434 435 #endif /* _C4_ERROR_HPP_ */