include/SDL_assert.h
author Ryan C. Gordon <icculus@icculus.org>
Thu, 28 May 2015 01:54:52 -0400
changeset 9671 0e1f57b051f4
parent 9619 b94b6d0bff0f
child 9998 f67cf37e9cd4
permissions -rw-r--r--
Windows GetBasePath should use GetModuleFileNameExW() and check for overflows.

Apparently you might get strange paths from GetModuleFileName(), such as
short path names or UNC filenames, so this avoids that problem. Since you have
to tapdance with linking different libraries and defining macros depending on
what Windows you plan to target, we dynamically load the API we need, which
works on all versions of Windows (on Win7, it'll load a compatibility wrapper
for the newer API location).

What a mess.

This also now does the right thing if there isn't enough space to store the
path, looping with a larger allocated buffer each try.

Fixes Bugzilla #2435.
slouken@3647
     1
/*
slouken@5535
     2
  Simple DirectMedia Layer
slouken@9619
     3
  Copyright (C) 1997-2015 Sam Lantinga <slouken@libsdl.org>
slouken@3647
     4
slouken@5535
     5
  This software is provided 'as-is', without any express or implied
slouken@5535
     6
  warranty.  In no event will the authors be held liable for any damages
slouken@5535
     7
  arising from the use of this software.
slouken@3647
     8
slouken@5535
     9
  Permission is granted to anyone to use this software for any purpose,
slouken@5535
    10
  including commercial applications, and to alter it and redistribute it
slouken@5535
    11
  freely, subject to the following restrictions:
slouken@3647
    12
slouken@5535
    13
  1. The origin of this software must not be misrepresented; you must not
slouken@5535
    14
     claim that you wrote the original software. If you use this software
slouken@5535
    15
     in a product, an acknowledgment in the product documentation would be
slouken@5535
    16
     appreciated but is not required.
slouken@5535
    17
  2. Altered source versions must be plainly marked as such, and must not be
slouken@5535
    18
     misrepresented as being the original software.
slouken@5535
    19
  3. This notice may not be removed or altered from any source distribution.
slouken@3647
    20
*/
slouken@3647
    21
slouken@3647
    22
#ifndef _SDL_assert_h
slouken@3647
    23
#define _SDL_assert_h
slouken@3647
    24
slouken@3655
    25
#include "SDL_config.h"
slouken@3655
    26
slouken@3655
    27
#include "begin_code.h"
slouken@3655
    28
/* Set up for C function definitions, even when using C++ */
slouken@3655
    29
#ifdef __cplusplus
slouken@3655
    30
extern "C" {
slouken@3655
    31
#endif
slouken@3655
    32
slouken@3647
    33
#ifndef SDL_ASSERT_LEVEL
slouken@3654
    34
#ifdef SDL_DEFAULT_ASSERT_LEVEL
slouken@3654
    35
#define SDL_ASSERT_LEVEL SDL_DEFAULT_ASSERT_LEVEL
slouken@3654
    36
#elif defined(_DEBUG) || defined(DEBUG) || \
slouken@3654
    37
      (defined(__GNUC__) && !defined(__OPTIMIZE__))
slouken@3653
    38
#define SDL_ASSERT_LEVEL 2
slouken@3653
    39
#else
slouken@3653
    40
#define SDL_ASSERT_LEVEL 1
slouken@3647
    41
#endif
slouken@3653
    42
#endif /* SDL_ASSERT_LEVEL */
slouken@3647
    43
slouken@3647
    44
/*
slouken@3655
    45
These are macros and not first class functions so that the debugger breaks
slouken@3655
    46
on the assertion line and not in some random guts of SDL, and so each
icculus@3665
    47
assert can have unique static variables associated with it.
slouken@3655
    48
*/
slouken@3655
    49
icculus@6430
    50
#if defined(_MSC_VER)
slouken@5491
    51
/* Don't include intrin.h here because it contains C++ code */
icculus@6430
    52
    extern void __cdecl __debugbreak(void);
slouken@5011
    53
    #define SDL_TriggerBreakpoint() __debugbreak()
gabomdq@8833
    54
#elif (!defined(__NACL__) && defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)))
slouken@3655
    55
    #define SDL_TriggerBreakpoint() __asm__ __volatile__ ( "int $3\n\t" )
slouken@3655
    56
#elif defined(HAVE_SIGNAL_H)
slouken@3655
    57
    #include <signal.h>
slouken@3655
    58
    #define SDL_TriggerBreakpoint() raise(SIGTRAP)
slouken@3655
    59
#else
slouken@3655
    60
    /* How do we trigger breakpoints on this platform? */
slouken@3655
    61
    #define SDL_TriggerBreakpoint()
slouken@3655
    62
#endif
slouken@3655
    63
icculus@5552
    64
#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 supports __func__ as a standard. */
slouken@3655
    65
#   define SDL_FUNCTION __func__
slouken@3655
    66
#elif ((__GNUC__ >= 2) || defined(_MSC_VER))
slouken@3655
    67
#   define SDL_FUNCTION __FUNCTION__
slouken@3655
    68
#else
slouken@3655
    69
#   define SDL_FUNCTION "???"
slouken@3655
    70
#endif
slouken@3655
    71
#define SDL_FILE    __FILE__
slouken@3655
    72
#define SDL_LINE    __LINE__
slouken@3655
    73
slouken@3655
    74
/*
slouken@3647
    75
sizeof (x) makes the compiler still parse the expression even without
slouken@3647
    76
assertions enabled, so the code is always checked at compile time, but
slouken@3647
    77
doesn't actually generate code for it, so there are no side effects or
slouken@3647
    78
expensive checks at run time, just the constant size of what x WOULD be,
slouken@3647
    79
which presumably gets optimized out as unused.
slouken@3647
    80
This also solves the problem of...
slouken@3647
    81
slouken@3647
    82
    int somevalue = blah();
slouken@3647
    83
    SDL_assert(somevalue == 1);
slouken@3647
    84
slouken@3647
    85
...which would cause compiles to complain that somevalue is unused if we
slouken@3647
    86
disable assertions.
slouken@3647
    87
*/
slouken@3647
    88
icculus@9463
    89
/* "while (0,0)" fools Microsoft's compiler's /W4 warning level into thinking
icculus@9463
    90
    this condition isn't constant. And looks like an owl's face! */
icculus@9470
    91
#ifdef _MSC_VER  /* stupid /W4 warnings. */
icculus@9463
    92
#define SDL_NULL_WHILE_LOOP_CONDITION (0,0)
icculus@9470
    93
#else
icculus@9470
    94
#define SDL_NULL_WHILE_LOOP_CONDITION (0)
icculus@9470
    95
#endif
icculus@7736
    96
slouken@3647
    97
#define SDL_disabled_assert(condition) \
icculus@7736
    98
    do { (void) sizeof ((condition)); } while (SDL_NULL_WHILE_LOOP_CONDITION)
slouken@3647
    99
slouken@3647
   100
typedef enum
slouken@3647
   101
{
slouken@3647
   102
    SDL_ASSERTION_RETRY,  /**< Retry the assert immediately. */
slouken@3647
   103
    SDL_ASSERTION_BREAK,  /**< Make the debugger trigger a breakpoint. */
slouken@3647
   104
    SDL_ASSERTION_ABORT,  /**< Terminate the program. */
slouken@3647
   105
    SDL_ASSERTION_IGNORE,  /**< Ignore the assert. */
icculus@5625
   106
    SDL_ASSERTION_ALWAYS_IGNORE  /**< Ignore the assert from now on. */
icculus@9371
   107
} SDL_AssertState;
slouken@3647
   108
icculus@9371
   109
typedef struct SDL_AssertData
slouken@3647
   110
{
slouken@3647
   111
    int always_ignore;
slouken@3647
   112
    unsigned int trigger_count;
slouken@3647
   113
    const char *condition;
slouken@3647
   114
    const char *filename;
slouken@3647
   115
    int linenum;
slouken@3647
   116
    const char *function;
icculus@9371
   117
    const struct SDL_AssertData *next;
icculus@9371
   118
} SDL_AssertData;
slouken@3647
   119
icculus@6761
   120
#if (SDL_ASSERT_LEVEL > 0)
icculus@6761
   121
icculus@3668
   122
/* Never call this directly. Use the SDL_assert* macros. */
icculus@9371
   123
extern DECLSPEC SDL_AssertState SDLCALL SDL_ReportAssertion(SDL_AssertData *,
slouken@3655
   124
                                                             const char *,
icculus@8189
   125
                                                             const char *, int)
icculus@8190
   126
#if defined(__clang__)
icculus@8190
   127
#if __has_feature(attribute_analyzer_noreturn)
icculus@8189
   128
/* this tells Clang's static analysis that we're a custom assert function,
icculus@8189
   129
   and that the analyzer should assume the condition was always true past this
icculus@8189
   130
   SDL_assert test. */
icculus@8189
   131
   __attribute__((analyzer_noreturn))
icculus@8189
   132
#endif
icculus@8190
   133
#endif
icculus@8189
   134
;
slouken@3647
   135
slouken@3647
   136
/* the do {} while(0) avoids dangling else problems:
slouken@3647
   137
    if (x) SDL_assert(y); else blah();
slouken@3647
   138
       ... without the do/while, the "else" could attach to this macro's "if".
slouken@3647
   139
   We try to handle just the minimum we need here in a macro...the loop,
slouken@3647
   140
   the static vars, and break points. The heavy lifting is handled in
slouken@3647
   141
   SDL_ReportAssertion(), in SDL_assert.c.
slouken@3647
   142
*/
slouken@3647
   143
#define SDL_enabled_assert(condition) \
slouken@3647
   144
    do { \
slouken@3647
   145
        while ( !(condition) ) { \
icculus@9371
   146
            static struct SDL_AssertData sdl_assert_data = { \
slouken@3655
   147
                0, 0, #condition, 0, 0, 0, 0 \
slouken@3647
   148
            }; \
icculus@9371
   149
            const SDL_AssertState sdl_assert_state = SDL_ReportAssertion(&sdl_assert_data, SDL_FUNCTION, SDL_FILE, SDL_LINE); \
icculus@9019
   150
            if (sdl_assert_state == SDL_ASSERTION_RETRY) { \
slouken@3647
   151
                continue; /* go again. */ \
icculus@9019
   152
            } else if (sdl_assert_state == SDL_ASSERTION_BREAK) { \
slouken@3647
   153
                SDL_TriggerBreakpoint(); \
slouken@3647
   154
            } \
slouken@3647
   155
            break; /* not retrying. */ \
slouken@3647
   156
        } \
icculus@7736
   157
    } while (SDL_NULL_WHILE_LOOP_CONDITION)
slouken@3647
   158
slouken@3647
   159
#endif  /* enabled assertions support code */
slouken@3647
   160
slouken@3647
   161
/* Enable various levels of assertions. */
slouken@3647
   162
#if SDL_ASSERT_LEVEL == 0   /* assertions disabled */
slouken@3647
   163
#   define SDL_assert(condition) SDL_disabled_assert(condition)
slouken@3647
   164
#   define SDL_assert_release(condition) SDL_disabled_assert(condition)
slouken@3647
   165
#   define SDL_assert_paranoid(condition) SDL_disabled_assert(condition)
slouken@3647
   166
#elif SDL_ASSERT_LEVEL == 1  /* release settings. */
slouken@3652
   167
#   define SDL_assert(condition) SDL_disabled_assert(condition)
slouken@3647
   168
#   define SDL_assert_release(condition) SDL_enabled_assert(condition)
slouken@3652
   169
#   define SDL_assert_paranoid(condition) SDL_disabled_assert(condition)
slouken@3647
   170
#elif SDL_ASSERT_LEVEL == 2  /* normal settings. */
slouken@3647
   171
#   define SDL_assert(condition) SDL_enabled_assert(condition)
slouken@3647
   172
#   define SDL_assert_release(condition) SDL_enabled_assert(condition)
slouken@3647
   173
#   define SDL_assert_paranoid(condition) SDL_disabled_assert(condition)
slouken@3647
   174
#elif SDL_ASSERT_LEVEL == 3  /* paranoid settings. */
slouken@3647
   175
#   define SDL_assert(condition) SDL_enabled_assert(condition)
slouken@3647
   176
#   define SDL_assert_release(condition) SDL_enabled_assert(condition)
slouken@3647
   177
#   define SDL_assert_paranoid(condition) SDL_enabled_assert(condition)
slouken@3647
   178
#else
slouken@3655
   179
#   error Unknown assertion level.
slouken@3647
   180
#endif
slouken@3647
   181
icculus@7664
   182
/* this assertion is never disabled at any level. */
icculus@7664
   183
#define SDL_assert_always(condition) SDL_enabled_assert(condition)
icculus@7664
   184
icculus@3670
   185
icculus@9371
   186
typedef SDL_AssertState (SDLCALL *SDL_AssertionHandler)(
icculus@9371
   187
                                 const SDL_AssertData* data, void* userdata);
icculus@3670
   188
icculus@3670
   189
/**
icculus@3670
   190
 *  \brief Set an application-defined assertion handler.
icculus@3670
   191
 *
icculus@3670
   192
 *  This allows an app to show its own assertion UI and/or force the
icculus@3670
   193
 *  response to an assertion failure. If the app doesn't provide this, SDL
icculus@3670
   194
 *  will try to do the right thing, popping up a system-specific GUI dialog,
icculus@3670
   195
 *  and probably minimizing any fullscreen windows.
icculus@3670
   196
 *
icculus@3670
   197
 *  This callback may fire from any thread, but it runs wrapped in a mutex, so
icculus@3670
   198
 *  it will only fire from one thread at a time.
icculus@3670
   199
 *
icculus@3670
   200
 *  Setting the callback to NULL restores SDL's original internal handler.
icculus@3670
   201
 *
icculus@3670
   202
 *  This callback is NOT reset to SDL's internal handler upon SDL_Quit()!
icculus@3670
   203
 *
icculus@9371
   204
 *  \return SDL_AssertState value of how to handle the assertion failure.
slouken@7191
   205
 *
icculus@3670
   206
 *  \param handler Callback function, called when an assertion fails.
icculus@3670
   207
 *  \param userdata A pointer passed to the callback as-is.
icculus@3670
   208
 */
icculus@3670
   209
extern DECLSPEC void SDLCALL SDL_SetAssertionHandler(
icculus@3670
   210
                                            SDL_AssertionHandler handler,
icculus@3670
   211
                                            void *userdata);
icculus@3670
   212
icculus@3670
   213
/**
icculus@8167
   214
 *  \brief Get the default assertion handler.
icculus@8167
   215
 *
icculus@8167
   216
 *  This returns the function pointer that is called by default when an
icculus@8167
   217
 *   assertion is triggered. This is an internal function provided by SDL,
icculus@8167
   218
 *   that is used for assertions when SDL_SetAssertionHandler() hasn't been
icculus@8167
   219
 *   used to provide a different function.
icculus@8167
   220
 *
icculus@8167
   221
 *  \return The default SDL_AssertionHandler that is called when an assert triggers.
icculus@8167
   222
 */
icculus@8167
   223
extern DECLSPEC SDL_AssertionHandler SDLCALL SDL_GetDefaultAssertionHandler(void);
icculus@8167
   224
icculus@8167
   225
/**
icculus@8167
   226
 *  \brief Get the current assertion handler.
icculus@8167
   227
 *
icculus@8167
   228
 *  This returns the function pointer that is called when an assertion is
icculus@8167
   229
 *   triggered. This is either the value last passed to
icculus@8167
   230
 *   SDL_SetAssertionHandler(), or if no application-specified function is
icculus@8167
   231
 *   set, is equivalent to calling SDL_GetDefaultAssertionHandler().
icculus@8167
   232
 *
icculus@8167
   233
 *   \param puserdata Pointer to a void*, which will store the "userdata"
icculus@8167
   234
 *                    pointer that was passed to SDL_SetAssertionHandler().
icculus@8167
   235
 *                    This value will always be NULL for the default handler.
icculus@8167
   236
 *                    If you don't care about this data, it is safe to pass
icculus@8167
   237
 *                    a NULL pointer to this function to ignore it.
icculus@8167
   238
 *  \return The SDL_AssertionHandler that is called when an assert triggers.
icculus@8167
   239
 */
icculus@8167
   240
extern DECLSPEC SDL_AssertionHandler SDLCALL SDL_GetAssertionHandler(void **puserdata);
icculus@8167
   241
icculus@8167
   242
/**
icculus@3670
   243
 *  \brief Get a list of all assertion failures.
icculus@3670
   244
 *
icculus@3670
   245
 *  Get all assertions triggered since last call to SDL_ResetAssertionReport(),
icculus@3670
   246
 *  or the start of the program.
icculus@3670
   247
 *
icculus@3670
   248
 *  The proper way to examine this data looks something like this:
icculus@3670
   249
 *
icculus@3670
   250
 *  <code>
icculus@9371
   251
 *  const SDL_AssertData *item = SDL_GetAssertionReport();
icculus@5541
   252
 *  while (item) {
icculus@3670
   253
 *      printf("'%s', %s (%s:%d), triggered %u times, always ignore: %s.\n",
icculus@3670
   254
 *             item->condition, item->function, item->filename,
icculus@3670
   255
 *             item->linenum, item->trigger_count,
icculus@3670
   256
 *             item->always_ignore ? "yes" : "no");
icculus@3670
   257
 *      item = item->next;
icculus@3670
   258
 *  }
icculus@3670
   259
 *  </code>
icculus@3670
   260
 *
icculus@5541
   261
 *  \return List of all assertions.
icculus@3670
   262
 *  \sa SDL_ResetAssertionReport
icculus@3670
   263
 */
icculus@9371
   264
extern DECLSPEC const SDL_AssertData * SDLCALL SDL_GetAssertionReport(void);
icculus@3670
   265
icculus@3670
   266
/**
icculus@3670
   267
 *  \brief Reset the list of all assertion failures.
icculus@3670
   268
 *
icculus@3670
   269
 *  Reset list of all assertions triggered.
icculus@3670
   270
 *
icculus@3670
   271
 *  \sa SDL_GetAssertionReport
icculus@3670
   272
 */
icculus@3670
   273
extern DECLSPEC void SDLCALL SDL_ResetAssertionReport(void);
icculus@3670
   274
icculus@9371
   275
icculus@9371
   276
/* these had wrong naming conventions until 2.0.4. Please update your app! */
icculus@9371
   277
#define SDL_assert_state SDL_AssertState
icculus@9371
   278
#define SDL_assert_data SDL_AssertData
icculus@9371
   279
icculus@9371
   280
slouken@3655
   281
/* Ends C function definitions when using C++ */
slouken@3655
   282
#ifdef __cplusplus
slouken@3655
   283
}
slouken@3655
   284
#endif
slouken@3655
   285
#include "close_code.h"
slouken@3655
   286
slouken@3647
   287
#endif /* _SDL_assert_h */
slouken@3647
   288
slouken@3647
   289
/* vi: set ts=4 sw=4 expandtab: */