duckstation

duckstation, but archived from the revision just before upstream changed it to a proprietary software project, this version is the libre one
git clone https://git.neptards.moe/u3shit/duckstation.git
Log | Files | Refs | README | LICENSE

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_ */